Skip to main content

polars_core/chunked_array/ops/
reverse.rs

1#[cfg(feature = "dtype-array")]
2use crate::chunked_array::builder::get_fixed_size_list_builder;
3use crate::prelude::*;
4use crate::series::IsSorted;
5use crate::utils::NoNull;
6
7impl<T> ChunkReverse for ChunkedArray<T>
8where
9    T: PolarsNumericType,
10{
11    fn reverse(&self) -> ChunkedArray<T> {
12        let mut out = if let Ok(slice) = self.cont_slice() {
13            let ca: NoNull<ChunkedArray<T>> = slice.iter().rev().copied().collect_trusted();
14            ca.into_inner()
15        } else {
16            self.iter().rev().collect_trusted()
17        };
18        out.rename(self.name().clone());
19
20        match self.is_sorted_flag() {
21            IsSorted::Ascending => out.set_sorted_flag(IsSorted::Descending),
22            IsSorted::Descending => out.set_sorted_flag(IsSorted::Ascending),
23            _ => {},
24        }
25
26        out
27    }
28}
29
30macro_rules! impl_reverse {
31    ($arrow_type:ident, $ca_type:ident) => {
32        impl ChunkReverse for $ca_type {
33            fn reverse(&self) -> Self {
34                if self.is_empty() {
35                    return self.clone();
36                };
37                let mut ca: Self = self.iter().rev().collect_trusted();
38                ca.rename(self.name().clone());
39                ca
40            }
41        }
42    };
43}
44
45impl_reverse!(BooleanType, BooleanChunked);
46impl_reverse!(BinaryOffsetType, BinaryOffsetChunked);
47
48impl ChunkReverse for ListChunked {
49    fn reverse(&self) -> Self {
50        if self.is_empty() {
51            return self.clone();
52        };
53        let ca: Self = self.series_iter().rev().collect_trusted();
54        ca.with_name(self.name().clone())
55    }
56}
57
58impl ChunkReverse for BinaryChunked {
59    fn reverse(&self) -> Self {
60        if self.chunks.len() == 1 {
61            let arr = self.downcast_iter().next().unwrap();
62            let views = arr.views().iter().copied().rev().collect::<Vec<_>>();
63
64            unsafe {
65                let arr = BinaryViewArray::new_unchecked(
66                    arr.dtype().clone(),
67                    views.into(),
68                    arr.data_buffers().clone(),
69                    arr.validity().map(|bitmap| bitmap.iter().rev().collect()),
70                    arr.try_total_bytes_len(),
71                    arr.total_buffer_len(),
72                )
73                .boxed();
74                BinaryChunked::from_chunks_and_dtype_unchecked(
75                    self.name().clone(),
76                    vec![arr],
77                    self.dtype().clone(),
78                )
79            }
80        } else {
81            let ca = IdxCa::from_vec(
82                PlSmallStr::EMPTY,
83                (0..self.len() as IdxSize).rev().collect(),
84            );
85            unsafe { self.take_unchecked(&ca) }
86        }
87    }
88}
89
90impl ChunkReverse for StringChunked {
91    fn reverse(&self) -> Self {
92        unsafe { self.as_binary().reverse().to_string_unchecked() }
93    }
94}
95
96#[cfg(feature = "dtype-array")]
97impl ChunkReverse for ArrayChunked {
98    fn reverse(&self) -> Self {
99        if !self.inner_dtype().is_primitive_numeric() {
100            todo!("reverse for FixedSizeList with non-numeric dtypes not yet supported")
101        }
102        let ca = self.rechunk();
103        let arr = ca.downcast_as_array();
104        let values = arr.values().as_ref();
105
106        let mut builder =
107            get_fixed_size_list_builder(ca.inner_dtype(), ca.len(), ca.width(), ca.name().clone())
108                .expect("not yet supported");
109
110        // SAFETY, we are within bounds
111        unsafe {
112            if arr.null_count() == 0 {
113                for i in (0..arr.len()).rev() {
114                    builder.push_unchecked(values, i)
115                }
116            } else {
117                let validity = arr.validity().unwrap();
118                for i in (0..arr.len()).rev() {
119                    if validity.get_bit_unchecked(i) {
120                        builder.push_unchecked(values, i)
121                    } else {
122                        builder.push_null()
123                    }
124                }
125            }
126        }
127        builder.finish()
128    }
129}
130
131#[cfg(feature = "object")]
132impl<T: PolarsObject> ChunkReverse for ObjectChunked<T> {
133    fn reverse(&self) -> Self {
134        // SAFETY: we know we don't go out of bounds.
135        unsafe {
136            self.take_unchecked(
137                &(0..self.len() as IdxSize)
138                    .rev()
139                    .collect_ca(PlSmallStr::EMPTY),
140            )
141        }
142    }
143}