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.into_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                let mut ca: Self = self.into_iter().rev().collect_trusted();
35                ca.rename(self.name().clone());
36                ca
37            }
38        }
39    };
40}
41
42impl_reverse!(BooleanType, BooleanChunked);
43impl_reverse!(BinaryOffsetType, BinaryOffsetChunked);
44impl_reverse!(ListType, ListChunked);
45
46impl ChunkReverse for BinaryChunked {
47    fn reverse(&self) -> Self {
48        if self.chunks.len() == 1 {
49            let arr = self.downcast_iter().next().unwrap();
50            let views = arr.views().iter().copied().rev().collect::<Vec<_>>();
51
52            unsafe {
53                let arr = BinaryViewArray::new_unchecked(
54                    arr.dtype().clone(),
55                    views.into(),
56                    arr.data_buffers().clone(),
57                    arr.validity().map(|bitmap| bitmap.iter().rev().collect()),
58                    arr.total_bytes_len(),
59                    arr.total_buffer_len(),
60                )
61                .boxed();
62                BinaryChunked::from_chunks_and_dtype_unchecked(
63                    self.name().clone(),
64                    vec![arr],
65                    self.dtype().clone(),
66                )
67            }
68        } else {
69            let ca = IdxCa::from_vec(
70                PlSmallStr::EMPTY,
71                (0..self.len() as IdxSize).rev().collect(),
72            );
73            unsafe { self.take_unchecked(&ca) }
74        }
75    }
76}
77
78impl ChunkReverse for StringChunked {
79    fn reverse(&self) -> Self {
80        unsafe { self.as_binary().reverse().to_string_unchecked() }
81    }
82}
83
84#[cfg(feature = "dtype-array")]
85impl ChunkReverse for ArrayChunked {
86    fn reverse(&self) -> Self {
87        if !self.inner_dtype().is_primitive_numeric() {
88            todo!("reverse for FixedSizeList with non-numeric dtypes not yet supported")
89        }
90        let ca = self.rechunk();
91        let arr = ca.downcast_as_array();
92        let values = arr.values().as_ref();
93
94        let mut builder =
95            get_fixed_size_list_builder(ca.inner_dtype(), ca.len(), ca.width(), ca.name().clone())
96                .expect("not yet supported");
97
98        // SAFETY, we are within bounds
99        unsafe {
100            if arr.null_count() == 0 {
101                for i in (0..arr.len()).rev() {
102                    builder.push_unchecked(values, i)
103                }
104            } else {
105                let validity = arr.validity().unwrap();
106                for i in (0..arr.len()).rev() {
107                    if validity.get_bit_unchecked(i) {
108                        builder.push_unchecked(values, i)
109                    } else {
110                        builder.push_null()
111                    }
112                }
113            }
114        }
115        builder.finish()
116    }
117}
118
119#[cfg(feature = "object")]
120impl<T: PolarsObject> ChunkReverse for ObjectChunked<T> {
121    fn reverse(&self) -> Self {
122        // SAFETY: we know we don't go out of bounds.
123        unsafe {
124            self.take_unchecked(
125                &(0..self.len() as IdxSize)
126                    .rev()
127                    .collect_ca(PlSmallStr::EMPTY),
128            )
129        }
130    }
131}