Repository: jorgecarleitao/arrow2 (rust)

Issue:
Arrow IPC sorts projection
This means that columns are returned in a different order than requested by the given projection argument. It would be great if we the `projection` argument could dictate column order.

Pull request:
Made IPC projection not sort projection
Closes #1074

Patch:
diff --git a/src/array/boolean/from.rs b/src/array/boolean/from.rs
index 8c76751376..81a5395ccc 100644
--- a/src/array/boolean/from.rs
+++ b/src/array/boolean/from.rs
@@ -1,91 +1,13 @@
 use std::iter::FromIterator;
 
-use crate::trusted_len::TrustedLen;
-
 use super::{BooleanArray, MutableBooleanArray};
 
 impl<P: AsRef<[Option<bool>]>> From<P> for BooleanArray {
-    /// Creates a new [`BooleanArray`] out of a slice of Optional `bool`.
     fn from(slice: P) -> Self {
         MutableBooleanArray::from(slice).into()
     }
 }
 
-impl BooleanArray {
-    /// Creates a new [`BooleanArray`] from an [`TrustedLen`] of `bool`.
-    #[inline]
-    pub fn from_trusted_len_values_iter<I: TrustedLen<Item = bool>>(iterator: I) -> Self {
-        MutableBooleanArray::from_trusted_len_values_iter(iterator).into()
-    }
-
-    /// Creates a new [`BooleanArray`] from an [`TrustedLen`] of `bool`.
-    /// Use this over [`BooleanArray::from_trusted_len_iter`] when the iterator is trusted len
-    /// but this crate does not mark it as such.
-    /// # Safety
-    /// The iterator must be [`TrustedLen`](https://doc.rust-lang.org/std/iter/trait.TrustedLen.html).
-    /// I.e. that `size_hint().1` correctly reports its length.
-    #[inline]
-    pub unsafe fn from_trusted_len_values_iter_unchecked<I: Iterator<Item = bool>>(
-        iterator: I,
-    ) -> Self {
-        MutableBooleanArray::from_trusted_len_values_iter_unchecked(iterator).into()
-    }
-
-    /// Creates a new [`BooleanArray`] from a slice of `bool`.
-    #[inline]
-    pub fn from_slice<P: AsRef<[bool]>>(slice: P) -> Self {
-        MutableBooleanArray::from_slice(slice).into()
-    }
-
-    /// Creates a [`BooleanArray`] from an iterator of trusted length.
-    /// Use this over [`BooleanArray::from_trusted_len_iter`] when the iterator is trusted len
-    /// but this crate does not mark it as such.
-    /// # Safety
-    /// The iterator must be [`TrustedLen`](https://doc.rust-lang.org/std/iter/trait.TrustedLen.html).
-    /// I.e. that `size_hint().1` correctly reports its length.
-    #[inline]
-    pub unsafe fn from_trusted_len_iter_unchecked<I, P>(iterator: I) -> Self
-    where
-        P: std::borrow::Borrow<bool>,
-        I: Iterator<Item = Option<P>>,
-    {
-        MutableBooleanArray::from_trusted_len_iter_unchecked(iterator).into()
-    }
-
-    /// Creates a [`BooleanArray`] from a [`TrustedLen`].
-    #[inline]
-    pub fn from_trusted_len_iter<I, P>(iterator: I) -> Self
-    where
-        P: std::borrow::Borrow<bool>,
-        I: TrustedLen<Item = Option<P>>,
-    {
-        MutableBooleanArray::from_trusted_len_iter(iterator).into()
-    }
-
-    /// Creates a [`BooleanArray`] from an falible iterator of trusted length.
-    /// # Safety
-    /// The iterator must be [`TrustedLen`](https://doc.rust-lang.org/std/iter/trait.TrustedLen.html).
-    /// I.e. that `size_hint().1` correctly reports its length.
-    #[inline]
-    pub unsafe fn try_from_trusted_len_iter_unchecked<E, I, P>(iterator: I) -> Result<Self, E>
-    where
-        P: std::borrow::Borrow<bool>,
-        I: Iterator<Item = Result<Option<P>, E>>,
-    {
-        Ok(MutableBooleanArray::try_from_trusted_len_iter_unchecked(iterator)?.into())
-    }
-
-    /// Creates a [`BooleanArray`] from a [`TrustedLen`].
-    #[inline]
-    pub fn try_from_trusted_len_iter<E, I, P>(iterator: I) -> Result<Self, E>
-    where
-        P: std::borrow::Borrow<bool>,
-        I: TrustedLen<Item = Result<Option<P>, E>>,
-    {
-        Ok(MutableBooleanArray::try_from_trusted_len_iter(iterator)?.into())
-    }
-}
-
 impl<Ptr: std::borrow::Borrow<Option<bool>>> FromIterator<Ptr> for BooleanArray {
     fn from_iter<I: IntoIterator<Item = Ptr>>(iter: I) -> Self {
         MutableBooleanArray::from_iter(iter).into()
diff --git a/src/array/boolean/iterator.rs b/src/array/boolean/iterator.rs
index 8b5e679fef..3f07bfb28e 100644
--- a/src/array/boolean/iterator.rs
+++ b/src/array/boolean/iterator.rs
@@ -13,23 +13,6 @@ impl<'a> IntoIterator for &'a BooleanArray {
     }
 }
 
-impl<'a> BooleanArray {
-    /// Returns an iterator over the optional values of this [`BooleanArray`].
-    #[inline]
-    pub fn iter(&'a self) -> ZipValidity<'a, bool, BitmapIter<'a>> {
-        zip_validity(
-            self.values().iter(),
-            self.validity.as_ref().map(|x| x.iter()),
-        )
-    }
-
-    /// Returns an iterator over the values of this [`BooleanArray`]
-    #[inline]
-    pub fn values_iter(&'a self) -> BitmapIter<'a> {
-        self.values().iter()
-    }
-}
-
 impl<'a> IntoIterator for &'a MutableBooleanArray {
     type Item = Option<bool>;
     type IntoIter = ZipValidity<'a, bool, BitmapIter<'a>>;
diff --git a/src/array/boolean/mod.rs b/src/array/boolean/mod.rs
index 815ec6b134..85ad172be0 100644
--- a/src/array/boolean/mod.rs
+++ b/src/array/boolean/mod.rs
@@ -1,7 +1,11 @@
 use crate::{
-    bitmap::{Bitmap, MutableBitmap},
+    bitmap::{
+        utils::{zip_validity, BitmapIter, ZipValidity},
+        Bitmap, MutableBitmap,
+    },
     datatypes::{DataType, PhysicalType},
     error::Error,
+    trusted_len::TrustedLen,
 };
 use either::Either;
 
@@ -16,8 +20,31 @@ mod mutable;
 pub use iterator::*;
 pub use mutable::*;
 
-/// The Arrow's equivalent to an immutable `Vec<Option<bool>>`, but with `1/16` of its size.
-/// Cloning and slicing this struct is `O(1)`.
+/// A [`BooleanArray`] is Arrow's semantically equivalent of an immutable `Vec<Option<bool>>`.
+/// It implements [`Array`].
+///
+/// One way to think about a [`BooleanArray`] is `(DataType, Arc<Vec<u8>>, Option<Arc<Vec<u8>>>)`
+/// where:
+/// * the first item is the array's logical type
+/// * the second is the immutable values
+/// * the third is the immutable validity (whether a value is null or not as a bitmap).
+///
+/// The size of this struct is `O(1)`, as all data is stored behind an [`std::sync::Arc`].
+/// # Example
+/// ```
+/// use arrow2::array::BooleanArray;
+/// use arrow2::bitmap::Bitmap;
+/// use arrow2::buffer::Buffer;
+///
+/// let array = BooleanArray::from([Some(true), None, Some(false)]);
+/// assert_eq!(array.value(0), true);
+/// assert_eq!(array.iter().collect::<Vec<_>>(), vec![Some(true), None, Some(false)]);
+/// assert_eq!(array.values_iter().collect::<Vec<_>>(), vec![true, false, false]);
+/// // the underlying representation
+/// assert_eq!(array.values(), &Bitmap::from([true, false, false]));
+/// assert_eq!(array.validity(), Some(&Bitmap::from([true, false, true])));
+///
+/// ```
 #[derive(Clone)]
 pub struct BooleanArray {
     data_type: DataType,
@@ -58,87 +85,67 @@ impl BooleanArray {
         })
     }
 
-    /// The canonical method to create a [`BooleanArray`]
-    /// # Panics
-    /// This function errors iff:
-    /// * The validity is not `None` and its length is different from `values`'s length
-    /// * The `data_type`'s [`PhysicalType`] is not equal to [`PhysicalType::Boolean`].
-    pub fn new(data_type: DataType, values: Bitmap, validity: Option<Bitmap>) -> Self {
-        Self::try_new(data_type, values, validity).unwrap()
+    /// Returns an iterator over the optional values of this [`BooleanArray`].
+    #[inline]
+    pub fn iter(&self) -> ZipValidity<bool, BitmapIter> {
+        zip_validity(
+            self.values().iter(),
+            self.validity.as_ref().map(|x| x.iter()),
+        )
     }
 
-    /// Alias for `new`
-    pub fn from_data(data_type: DataType, values: Bitmap, validity: Option<Bitmap>) -> Self {
-        Self::new(data_type, values, validity)
+    /// Returns an iterator over the values of this [`BooleanArray`].
+    #[inline]
+    pub fn values_iter(&self) -> BitmapIter {
+        self.values().iter()
     }
 
-    /// Returns a new empty [`BooleanArray`].
-    pub fn new_empty(data_type: DataType) -> Self {
-        Self::new(data_type, Bitmap::new(), None)
+    /// Returns the length of this array
+    #[inline]
+    pub fn len(&self) -> usize {
+        self.values.len()
     }
 
-    /// Returns a new [`BooleanArray`] whose all slots are null / `None`.
-    pub fn new_null(data_type: DataType, length: usize) -> Self {
-        let bitmap = Bitmap::new_zeroed(length);
-        Self::new(data_type, bitmap.clone(), Some(bitmap))
+    /// The values [`Bitmap`].
+    /// Values on null slots are undetermined (they can be anything).
+    #[inline]
+    pub fn values(&self) -> &Bitmap {
+        &self.values
     }
 
-    /// Boxes self into a [`Box<dyn Array>`].
-    pub fn boxed(self) -> Box<dyn Array> {
-        Box::new(self)
+    /// Returns the optional validity.
+    #[inline]
+    pub fn validity(&self) -> Option<&Bitmap> {
+        self.validity.as_ref()
     }
 
-    /// Boxes self into a [`std::sync::Arc<dyn Array>`].
-    pub fn arced(self) -> std::sync::Arc<dyn Array> {
-        std::sync::Arc::new(self)
+    /// Returns the arrays' [`DataType`].
+    #[inline]
+    pub fn data_type(&self) -> &DataType {
+        &self.data_type
     }
 
-    /// Applies a function `f` to the values of this array, cloning the values
-    /// iff they are being shared with others
-    ///
-    /// This is an API to use clone-on-write
-    /// # Implementation
-    /// This function is `O(f)` if the data is not being shared, and `O(N) + O(f)`
-    /// if it is being shared (since it results in a `O(N)` memcopy).
-    /// # Panics
-    /// This function panics if the function modifies the length of the [`MutableBitmap`].
-    pub fn apply_values_mut<F: Fn(&mut MutableBitmap)>(&mut self, f: F) {
-        let values = std::mem::take(&mut self.values);
-        let mut values = values.make_mut();
-        f(&mut values);
-        if let Some(validity) = &self.validity {
-            assert_eq!(validity.len(), values.len());
-        }
-        self.values = values.into();
+    /// Returns the value at index `i`
+    /// # Panic
+    /// This function panics iff `i >= self.len()`.
+    #[inline]
+    pub fn value(&self, i: usize) -> bool {
+        self.values.get_bit(i)
     }
 
-    /// Applies a function `f` to the validity of this array, cloning it
-    /// iff it is being shared.
-    ///
-    /// This is an API to leverage clone-on-write
-    /// # Implementation
-    /// This function is `O(f)` if the data is not being shared, and `O(N) + O(f)`
-    /// if it is being shared (since it results in a `O(N)` memcopy).
-    /// # Panics
-    /// This function panics if the function modifies the length of the [`MutableBitmap`].
-    pub fn apply_validity_mut<F: Fn(&mut MutableBitmap)>(&mut self, f: F) {
-        if let Some(validity) = self.validity.as_mut() {
-            let values = std::mem::take(validity);
-            let mut bitmap = values.make_mut();
-            f(&mut bitmap);
-            assert_eq!(bitmap.len(), self.values.len());
-            *validity = bitmap.into();
-        }
+    /// Returns the element at index `i` as bool
+    /// # Safety
+    /// Caller must be sure that `i < self.len()`
+    #[inline]
+    pub unsafe fn value_unchecked(&self, i: usize) -> bool {
+        self.values.get_bit_unchecked(i)
     }
-}
 
-// must use
-impl BooleanArray {
     /// Returns a slice of this [`BooleanArray`].
     /// # Implementation
-    /// This operation is `O(1)` as it amounts to increase two ref counts.
+    /// This operation is `O(1)` as it amounts to increase up to two ref counts.
     /// # Panic
-    /// This function panics iff `offset + length >= self.len()`.
+    /// This function panics iff `offset + length > self.len()`.
     #[inline]
     #[must_use]
     pub fn slice(&self, offset: usize, length: usize) -> Self {
@@ -168,17 +175,84 @@ impl BooleanArray {
         }
     }
 
-    /// Sets the validity bitmap on this [`BooleanArray`].
+    /// Clones this [`BooleanArray`], returning one with the provided validity.
     /// # Panic
     /// This function panics iff `validity.len() != self.len()`.
     #[must_use]
     pub fn with_validity(&self, validity: Option<Bitmap>) -> Self {
+        let mut array = self.clone();
+        array.set_validity(validity);
+        array
+    }
+
+    /// Sets the validity of this [`BooleanArray`].
+    /// # Panics
+    /// This function panics iff `values.len() != self.len()`.
+    pub fn set_validity(&mut self, validity: Option<Bitmap>) {
         if matches!(&validity, Some(bitmap) if bitmap.len() != self.len()) {
-            panic!("validity should be as least as large as the array")
+            panic!("validity must be equal to the array's length")
+        }
+        self.validity = validity;
+    }
+
+    /// Returns a clone of this [`BooleanArray`] with new values.
+    /// # Panics
+    /// This function panics iff `values.len() != self.len()`.
+    #[must_use]
+    pub fn with_values(&self, values: Bitmap) -> Self {
+        let mut out = self.clone();
+        out.set_values(values);
+        out
+    }
+
+    /// Sets the values of this [`BooleanArray`].
+    /// # Panics
+    /// This function panics iff `values.len() != self.len()`.
+    pub fn set_values(&mut self, values: Bitmap) {
+        assert_eq!(
+            values.len(),
+            self.len(),
+            "values length must be equal to this arrays length"
+        );
+        self.values = values;
+    }
+
+    /// Applies a function `f` to the values of this array, cloning the values
+    /// iff they are being shared with others
+    ///
+    /// This is an API to use clone-on-write
+    /// # Implementation
+    /// This function is `O(f)` if the data is not being shared, and `O(N) + O(f)`
+    /// if it is being shared (since it results in a `O(N)` memcopy).
+    /// # Panics
+    /// This function panics if the function modifies the length of the [`MutableBitmap`].
+    pub fn apply_values_mut<F: Fn(&mut MutableBitmap)>(&mut self, f: F) {
+        let values = std::mem::take(&mut self.values);
+        let mut values = values.make_mut();
+        f(&mut values);
+        if let Some(validity) = &self.validity {
+            assert_eq!(validity.len(), values.len());
+        }
+        self.values = values.into();
+    }
+
+    /// Applies a function `f` to the validity of this array, cloning it
+    /// iff it is being shared.
+    ///
+    /// This is an API to leverage clone-on-write
+    /// # Implementation
+    /// This function is `O(f)` if the data is not being shared, and `O(N) + O(f)`
+    /// if it is being shared (since it results in a `O(N)` memcopy).
+    /// # Panics
+    /// This function panics if the function modifies the length of the [`MutableBitmap`].
+    pub fn apply_validity_mut<F: Fn(&mut MutableBitmap)>(&mut self, f: F) {
+        if let Some(validity) = self.validity.as_mut() {
+            let owned_validity = std::mem::take(validity);
+            let mut mut_bitmap = owned_validity.make_mut();
+            f(&mut mut_bitmap);
+            assert_eq!(mut_bitmap.len(), self.values.len());
+            *validity = mut_bitmap.into();
         }
-        let mut arr = self.clone();
-        arr.validity = validity;
-        arr
     }
 
     /// Try to convert this [`BooleanArray`] to a [`MutableBooleanArray`]
@@ -212,42 +286,113 @@ impl BooleanArray {
             }
         }
     }
-}
 
-// accessors
-impl BooleanArray {
-    /// Returns the length of this array
+    /// Returns a new empty [`BooleanArray`].
+    pub fn new_empty(data_type: DataType) -> Self {
+        Self::new(data_type, Bitmap::new(), None)
+    }
+
+    /// Returns a new [`BooleanArray`] whose all slots are null / `None`.
+    pub fn new_null(data_type: DataType, length: usize) -> Self {
+        let bitmap = Bitmap::new_zeroed(length);
+        Self::new(data_type, bitmap.clone(), Some(bitmap))
+    }
+
+    /// Creates a new [`BooleanArray`] from an [`TrustedLen`] of `bool`.
     #[inline]
-    pub fn len(&self) -> usize {
-        self.values.len()
+    pub fn from_trusted_len_values_iter<I: TrustedLen<Item = bool>>(iterator: I) -> Self {
+        MutableBooleanArray::from_trusted_len_values_iter(iterator).into()
     }
 
-    /// Returns the value at index `i`
-    /// # Panic
-    /// This function panics iff `i >= self.len()`.
+    /// Creates a new [`BooleanArray`] from an [`TrustedLen`] of `bool`.
+    /// Use this over [`BooleanArray::from_trusted_len_iter`] when the iterator is trusted len
+    /// but this crate does not mark it as such.
+    /// # Safety
+    /// The iterator must be [`TrustedLen`](https://doc.rust-lang.org/std/iter/trait.TrustedLen.html).
+    /// I.e. that `size_hint().1` correctly reports its length.
     #[inline]
-    pub fn value(&self, i: usize) -> bool {
-        self.values.get_bit(i)
+    pub unsafe fn from_trusted_len_values_iter_unchecked<I: Iterator<Item = bool>>(
+        iterator: I,
+    ) -> Self {
+        MutableBooleanArray::from_trusted_len_values_iter_unchecked(iterator).into()
     }
 
-    /// Returns the element at index `i` as bool
+    /// Creates a new [`BooleanArray`] from a slice of `bool`.
+    #[inline]
+    pub fn from_slice<P: AsRef<[bool]>>(slice: P) -> Self {
+        MutableBooleanArray::from_slice(slice).into()
+    }
+
+    /// Creates a [`BooleanArray`] from an iterator of trusted length.
+    /// Use this over [`BooleanArray::from_trusted_len_iter`] when the iterator is trusted len
+    /// but this crate does not mark it as such.
     /// # Safety
-    /// Caller must be sure that `i < self.len()`
+    /// The iterator must be [`TrustedLen`](https://doc.rust-lang.org/std/iter/trait.TrustedLen.html).
+    /// I.e. that `size_hint().1` correctly reports its length.
     #[inline]
-    pub unsafe fn value_unchecked(&self, i: usize) -> bool {
-        self.values.get_bit_unchecked(i)
+    pub unsafe fn from_trusted_len_iter_unchecked<I, P>(iterator: I) -> Self
+    where
+        P: std::borrow::Borrow<bool>,
+        I: Iterator<Item = Option<P>>,
+    {
+        MutableBooleanArray::from_trusted_len_iter_unchecked(iterator).into()
     }
 
-    /// The optional validity.
+    /// Creates a [`BooleanArray`] from a [`TrustedLen`].
     #[inline]
-    pub fn validity(&self) -> Option<&Bitmap> {
-        self.validity.as_ref()
+    pub fn from_trusted_len_iter<I, P>(iterator: I) -> Self
+    where
+        P: std::borrow::Borrow<bool>,
+        I: TrustedLen<Item = Option<P>>,
+    {
+        MutableBooleanArray::from_trusted_len_iter(iterator).into()
     }
 
-    /// Returns the values of this [`BooleanArray`].
+    /// Creates a [`BooleanArray`] from an falible iterator of trusted length.
+    /// # Safety
+    /// The iterator must be [`TrustedLen`](https://doc.rust-lang.org/std/iter/trait.TrustedLen.html).
+    /// I.e. that `size_hint().1` correctly reports its length.
     #[inline]
-    pub fn values(&self) -> &Bitmap {
-        &self.values
+    pub unsafe fn try_from_trusted_len_iter_unchecked<E, I, P>(iterator: I) -> Result<Self, E>
+    where
+        P: std::borrow::Borrow<bool>,
+        I: Iterator<Item = Result<Option<P>, E>>,
+    {
+        Ok(MutableBooleanArray::try_from_trusted_len_iter_unchecked(iterator)?.into())
+    }
+
+    /// Creates a [`BooleanArray`] from a [`TrustedLen`].
+    #[inline]
+    pub fn try_from_trusted_len_iter<E, I, P>(iterator: I) -> Result<Self, E>
+    where
+        P: std::borrow::Borrow<bool>,
+        I: TrustedLen<Item = Result<Option<P>, E>>,
+    {
+        Ok(MutableBooleanArray::try_from_trusted_len_iter(iterator)?.into())
+    }
+
+    /// Boxes self into a [`Box<dyn Array>`].
+    pub fn boxed(self) -> Box<dyn Array> {
+        Box::new(self)
+    }
+
+    /// Boxes self into a [`std::sync::Arc<dyn Array>`].
+    pub fn arced(self) -> std::sync::Arc<dyn Array> {
+        std::sync::Arc::new(self)
+    }
+
+    /// The canonical method to create a [`BooleanArray`]
+    /// # Panics
+    /// This function errors iff:
+    /// * The validity is not `None` and its length is different from `values`'s length
+    /// * The `data_type`'s [`PhysicalType`] is not equal to [`PhysicalType::Boolean`].
+    pub fn new(data_type: DataType, values: Bitmap, validity: Option<Bitmap>) -> Self {
+        Self::try_new(data_type, values, validity).unwrap()
+    }
+
+    /// Alias for `new`
+    pub fn from_data(data_type: DataType, values: Bitmap, validity: Option<Bitmap>) -> Self {
+        Self::new(data_type, values, validity)
     }
 }
 
diff --git a/src/array/primitive/mod.rs b/src/array/primitive/mod.rs
index 8443fca7d5..925b9a26fc 100644
--- a/src/array/primitive/mod.rs
+++ b/src/array/primitive/mod.rs
@@ -126,6 +126,7 @@ impl<T: NativeType> PrimitiveArray<T> {
     }
 
     /// Creates a (non-null) [`PrimitiveArray`] from a vector of values.
+    /// This function is `O(1)`.
     /// # Examples
     /// ```
     /// use arrow2::array::PrimitiveArray;
@@ -260,7 +261,7 @@ impl<T: NativeType> PrimitiveArray<T> {
         self.validity = validity;
     }
 
-    /// Returns a clone of this [`PrimitiveArray`] with a new values.
+    /// Returns a clone of this [`PrimitiveArray`] with new values.
     /// # Panics
     /// This function panics iff `values.len() != self.len()`.
     #[must_use]
diff --git a/src/io/ipc/read/common.rs b/src/io/ipc/read/common.rs
index f2a9eaa852..7e32cde5d0 100644
--- a/src/io/ipc/read/common.rs
+++ b/src/io/ipc/read/common.rs
@@ -321,31 +321,31 @@ pub fn prepare_projection(
 
     let fields = projection.iter().map(|x| fields[*x].clone()).collect();
 
-    // selected index; index in
-    let sorted_projection = projection
-        .iter()
-        .copied()
-        .enumerate()
-        .map(|x| (x.1, x.0))
-        .collect::<HashMap<_, _>>(); // e.g. [2, 1] -> {2: 0, 1: 1}
-    projection.sort_unstable(); // e.g. [2, 1] -> [1, 2]
+    // todo: find way to do this more efficiently
+    let mut indices = (0..projection.len()).collect::<Vec<_>>();
+    indices.sort_unstable_by_key(|&i| &projection[i]);
+    let map = indices.iter().copied().enumerate().fold(
+        HashMap::default(),
+        |mut acc, (index, new_index)| {
+            if !acc.contains_key(&new_index) {
+                acc.insert(index, new_index);
+            };
+            acc
+        },
+    );
+    projection.sort_unstable();
 
-    (projection, sorted_projection, fields)
+    (projection, map, fields)
 }
 
 pub fn apply_projection(
     chunk: Chunk<Box<dyn Array>>,
-    projection: &[usize],
     map: &HashMap<usize, usize>,
 ) -> Chunk<Box<dyn Array>> {
     // re-order according to projection
-    let arrays = chunk.into_arrays();
-    let arrays = projection
-        .iter()
-        .map(|x| {
-            let index = map.get(x).unwrap();
-            arrays[*index].clone()
-        })
-        .collect();
+    let mut arrays = chunk.into_arrays();
+    map.iter().for_each(|(old, new)| {
+        arrays.swap(*old, *new);
+    });
     Chunk::new(arrays)
 }
diff --git a/src/io/ipc/read/file_async.rs b/src/io/ipc/read/file_async.rs
index a4674167e3..8397eb4a01 100644
--- a/src/io/ipc/read/file_async.rs
+++ b/src/io/ipc/read/file_async.rs
@@ -90,9 +90,9 @@ impl<'a> FileStream<'a> {
                     &mut block_buffer,
                 ).await?;
 
-                let chunk = if let Some((projection, map)) = &projection {
+                let chunk = if let Some((_, map)) = &projection {
                     // re-order according to projection
-                    apply_projection(chunk, projection, map)
+                    apply_projection(chunk, map)
                 } else {
                     chunk
                 };
diff --git a/src/io/ipc/read/reader.rs b/src/io/ipc/read/reader.rs
index 1e3f4459f9..9807c19849 100644
--- a/src/io/ipc/read/reader.rs
+++ b/src/io/ipc/read/reader.rs
@@ -379,9 +379,9 @@ impl<R: Read + Seek> Iterator for FileReader<R> {
             &mut self.buffer,
         );
 
-        let chunk = if let Some((projection, map, _)) = &self.projection {
+        let chunk = if let Some((_, map, _)) = &self.projection {
             // re-order according to projection
-            chunk.map(|chunk| apply_projection(chunk, projection, map))
+            chunk.map(|chunk| apply_projection(chunk, map))
         } else {
             chunk
         };


Test patch:
diff --git a/tests/it/io/ipc/read/file.rs b/tests/it/io/ipc/read/file.rs
index 18a3190c8b..022cf4de27 100644
--- a/tests/it/io/ipc/read/file.rs
+++ b/tests/it/io/ipc/read/file.rs
@@ -1,5 +1,6 @@
 use std::fs::File;
 
+use arrow2::chunk::Chunk;
 use arrow2::error::Result;
 use arrow2::io::ipc::read::*;
 
@@ -166,18 +167,29 @@ fn test_projection(version: &str, file_name: &str, columns: Vec<usize>) -> Resul
 
     let metadata = read_file_metadata(&mut file)?;
 
-    let expected = columns
+    let (_, _, chunks) = read_gzip_json(version, file_name)?;
+
+    let expected_fields = columns
         .iter()
         .copied()
         .map(|x| metadata.schema.fields[x].clone())
         .collect::<Vec<_>>();
 
-    let mut reader = FileReader::new(&mut file, metadata, Some(columns));
+    let expected_chunks = chunks.into_iter().map(|chunk| {
+        let columns = columns
+            .iter()
+            .copied()
+            .map(|x| chunk.arrays()[x].clone())
+            .collect::<Vec<_>>();
+        Chunk::new(columns)
+    });
 
-    assert_eq!(reader.schema().fields, expected);
+    let reader = FileReader::new(&mut file, metadata, Some(columns.clone()));
 
-    reader.try_for_each(|rhs| {
-        assert_eq!(rhs?.arrays().len(), expected.len());
+    assert_eq!(reader.schema().fields, expected_fields);
+
+    reader.zip(expected_chunks).try_for_each(|(lhs, rhs)| {
+        assert_eq!(&lhs?.arrays()[0], &rhs.arrays()[0]);
         Result::Ok(())
     })?;
     Ok(())
@@ -189,7 +201,9 @@ fn read_projected() -> Result<()> {
     test_projection("1.0.0-littleendian", "generated_dictionary", vec![2])?;
     test_projection("1.0.0-littleendian", "generated_nested", vec![0])?;
 
-    test_projection("1.0.0-littleendian", "generated_primitive", vec![2, 1])
+    test_projection("1.0.0-littleendian", "generated_primitive", vec![2, 1])?;
+
+    test_projection("1.0.0-littleendian", "generated_primitive", vec![0, 2, 1])
 }
 
 fn read_corrupted_ipc(data: Vec<u8>) -> Result<()> {