polars_core/chunked_array/ops/
append.rs1use polars_error::constants::LENGTH_LIMIT_MSG;
2
3use crate::prelude::*;
4use crate::series::IsSorted;
5
6pub(crate) fn new_chunks(chunks: &mut Vec<ArrayRef>, other: &[ArrayRef], len: usize) {
7 if chunks.len() == 1 && len == 0 {
9 other.clone_into(chunks);
10 } else {
11 for chunk in other {
12 if !chunk.is_empty() {
13 chunks.push(chunk.clone());
14 }
15 }
16 }
17}
18
19pub(crate) fn new_chunks_owned(chunks: &mut Vec<ArrayRef>, other: Vec<ArrayRef>, len: usize) {
20 if chunks.len() == 1 && len == 0 {
22 *chunks = other;
23 } else {
24 chunks.reserve(other.len());
25 chunks.extend(other.into_iter().filter(|c| !c.is_empty()));
26 }
27}
28
29pub(super) fn update_sorted_flag_before_append<T>(ca: &mut ChunkedArray<T>, other: &ChunkedArray<T>)
30where
31 T: PolarsDataType,
32 for<'a> T::Physical<'a>: TotalOrd,
33{
34 let sorted_flag = match (
37 ca.null_count() != ca.len(),
38 other.null_count() != other.len(),
39 ) {
40 (false, false) => IsSorted::Ascending,
41 (false, true) => {
42 if
43 ca.is_empty()
45 || (
46 other.is_sorted_any() && 1 + other.last_non_null().unwrap() == other.len()
48 )
49 {
50 other.is_sorted_flag()
51 } else {
52 IsSorted::Not
53 }
54 },
55 (true, false) => {
56 if
57 other.is_empty()
59 || (
60 ca.is_sorted_any() && ca.first_non_null().unwrap() == 0
62 )
63 {
64 ca.is_sorted_flag()
65 } else {
66 IsSorted::Not
67 }
68 },
69 (true, true) => {
70 let both_sorted = (ca.is_sorted_any() || ca.len() == 1)
74 && (other.is_sorted_any() || other.len() == 1);
75
76 let flags_differ = ca.is_sorted_flag() != other.is_sorted_flag();
79 let is_single_value = |arr: &ChunkedArray<T>| {
80 arr.len() - arr.null_count() == 1
81 || (both_sorted
82 && flags_differ
83 && unsafe {
84 arr.value_unchecked(arr.first_non_null().unwrap())
85 .tot_eq(&arr.value_unchecked(arr.last_non_null().unwrap()))
86 })
87 };
88 let l_single = is_single_value(ca);
89 let r_single = is_single_value(other);
90
91 if !both_sorted || (flags_differ && !(l_single || r_single)) {
92 IsSorted::Not
93 } else {
94 let l_idx = ca.last_non_null().unwrap();
95 let r_idx = other.first_non_null().unwrap();
96
97 let null_pos_check =
98 (1 + l_idx == ca.len())
101 && (r_idx == 0)
103 && !(ca.first_non_null().unwrap() != 0 && 1 + other.last_non_null().unwrap() != other.len());
105
106 if !null_pos_check {
107 IsSorted::Not
108 } else {
109 #[allow(unused_assignments)]
110 let mut out = IsSorted::Not;
111
112 let l_val = unsafe { ca.value_unchecked(l_idx) };
114 let r_val = unsafe { other.value_unchecked(r_idx) };
115
116 match (l_single, r_single) {
117 (true, true) => {
118 out = [IsSorted::Descending, IsSorted::Ascending]
119 [l_val.tot_le(&r_val) as usize];
120 drop(l_val);
121 drop(r_val);
122 ca.set_sorted_flag(out);
123 return;
124 },
125 (true, false) => out = other.is_sorted_flag(),
126 _ => out = ca.is_sorted_flag(),
127 }
128
129 debug_assert!(!matches!(out, IsSorted::Not));
130
131 let check = if matches!(out, IsSorted::Ascending) {
132 l_val.tot_le(&r_val)
133 } else {
134 l_val.tot_ge(&r_val)
135 };
136
137 if !check {
138 out = IsSorted::Not
139 }
140
141 out
142 }
143 }
144 },
145 };
146
147 ca.set_sorted_flag(sorted_flag);
148}
149
150impl<T> ChunkedArray<T>
151where
152 T: PolarsDataType<IsNested = FalseT, IsObject = FalseT>,
153 for<'a> T::Physical<'a>: TotalOrd,
154{
155 pub fn append(&mut self, other: &Self) -> PolarsResult<()> {
159 update_sorted_flag_before_append::<T>(self, other);
160 let len = self.len();
161 self.length = self
162 .length
163 .checked_add(other.length)
164 .ok_or_else(|| polars_err!(ComputeError: LENGTH_LIMIT_MSG))?;
165 self.null_count += other.null_count;
166 new_chunks(&mut self.chunks, &other.chunks, len);
167 Ok(())
168 }
169
170 pub fn append_owned(&mut self, mut other: Self) -> PolarsResult<()> {
174 update_sorted_flag_before_append::<T>(self, &other);
175 let len = self.len();
176 self.length = self
177 .length
178 .checked_add(other.length)
179 .ok_or_else(|| polars_err!(ComputeError: LENGTH_LIMIT_MSG))?;
180 self.null_count += other.null_count;
181 new_chunks_owned(&mut self.chunks, std::mem::take(&mut other.chunks), len);
182 Ok(())
183 }
184}
185
186#[doc(hidden)]
187impl ListChunked {
188 pub fn append(&mut self, other: &Self) -> PolarsResult<()> {
189 self.append_owned(other.clone())
190 }
191
192 pub fn append_owned(&mut self, mut other: Self) -> PolarsResult<()> {
193 let dtype = merge_dtypes(self.dtype(), other.dtype())?;
194 self.field = Arc::new(Field::new(self.name().clone(), dtype));
195
196 let len = self.len();
197 self.length = self
198 .length
199 .checked_add(other.length)
200 .ok_or_else(|| polars_err!(ComputeError: LENGTH_LIMIT_MSG))?;
201 self.null_count += other.null_count;
202 self.set_sorted_flag(IsSorted::Not);
203 if !other.get_fast_explode_list() {
204 self.unset_fast_explode_list()
205 }
206
207 new_chunks_owned(&mut self.chunks, std::mem::take(&mut other.chunks), len);
208 Ok(())
209 }
210}
211
212#[cfg(feature = "dtype-array")]
213#[doc(hidden)]
214impl ArrayChunked {
215 pub fn append(&mut self, other: &Self) -> PolarsResult<()> {
216 self.append_owned(other.clone())
217 }
218
219 pub fn append_owned(&mut self, mut other: Self) -> PolarsResult<()> {
220 let dtype = merge_dtypes(self.dtype(), other.dtype())?;
221 self.field = Arc::new(Field::new(self.name().clone(), dtype));
222
223 let len = self.len();
224
225 self.length = self
226 .length
227 .checked_add(other.length)
228 .ok_or_else(|| polars_err!(ComputeError: LENGTH_LIMIT_MSG))?;
229 self.null_count += other.null_count;
230
231 self.set_sorted_flag(IsSorted::Not);
232
233 new_chunks_owned(&mut self.chunks, std::mem::take(&mut other.chunks), len);
234 Ok(())
235 }
236}
237
238#[cfg(feature = "dtype-struct")]
239#[doc(hidden)]
240impl StructChunked {
241 pub fn append(&mut self, other: &Self) -> PolarsResult<()> {
242 self.append_owned(other.clone())
243 }
244
245 pub fn append_owned(&mut self, mut other: Self) -> PolarsResult<()> {
246 let dtype = merge_dtypes(self.dtype(), other.dtype())?;
247 self.field = Arc::new(Field::new(self.name().clone(), dtype));
248
249 let len = self.len();
250
251 self.length = self
252 .length
253 .checked_add(other.length)
254 .ok_or_else(|| polars_err!(ComputeError: LENGTH_LIMIT_MSG))?;
255 self.null_count += other.null_count;
256
257 self.set_sorted_flag(IsSorted::Not);
258
259 new_chunks_owned(&mut self.chunks, std::mem::take(&mut other.chunks), len);
260 Ok(())
261 }
262}
263
264#[cfg(feature = "dtype-categorical")]
265#[doc(hidden)]
266impl<T: PolarsCategoricalType> CategoricalChunked<T> {
267 pub fn append(&mut self, other: &Self) -> PolarsResult<()> {
268 assert!(self.dtype() == other.dtype());
269 self.phys.append(&other.phys)
270 }
271
272 pub fn append_owned(&mut self, other: Self) -> PolarsResult<()> {
273 assert!(self.dtype() == other.dtype());
274 self.phys.append_owned(other.phys)
275 }
276}
277
278#[cfg(feature = "object")]
279#[doc(hidden)]
280impl<T: PolarsObject> ObjectChunked<T> {
281 pub fn append(&mut self, other: &Self) -> PolarsResult<()> {
282 self.append_owned(other.clone())
283 }
284
285 pub fn append_owned(&mut self, mut other: Self) -> PolarsResult<()> {
286 let len = self.len();
287 self.length = self
288 .length
289 .checked_add(other.length)
290 .ok_or_else(|| polars_err!(ComputeError: LENGTH_LIMIT_MSG))?;
291 self.null_count += other.null_count;
292 self.set_sorted_flag(IsSorted::Not);
293
294 new_chunks_owned(&mut self.chunks, std::mem::take(&mut other.chunks), len);
295 Ok(())
296 }
297}