diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 51c1403a5..1042574c7 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2473,6 +2473,45 @@ where self } + /// Consume the array, call `f` by **v**alue on each element, and return an + /// owned array with the new values. Works for **any** `F: FnMut(A)->B`. + /// + /// If `A` and `B` are the same type then the map is performed by delegating + /// to [`mapv_into()`] and then converting into an owned array. This avoids + /// unnecessary memory allocations in [`mapv()`]. + /// + /// If `A` and `B` are different types then a new array is allocated and the + /// map is performed as in [`mapv()`]. + /// + /// Elements are visited in arbitrary order. + pub fn mapv_into_any(self, mut f: F) -> Array + where + S: DataMut, + F: FnMut(A) -> B, + A: Clone + 'static, + B: 'static, + { + if core::any::TypeId::of::() == core::any::TypeId::of::() { + // A and B are the same type. + // Wrap f in a closure of type FnMut(A) -> A . + let f = |a| { + let b = f(a); + // Safe because A and B are the same type. + unsafe { unlimited_transmute::(b) } + }; + // Delegate to mapv_into() using the wrapped closure. + // Convert output to a uniquely owned array of type Array. + let output = self.mapv_into(f).into_owned(); + // Change the return type from Array to Array. + // Again, safe because A and B are the same type. + unsafe { unlimited_transmute::, Array>(output) } + } else { + // A and B are not the same type. + // Fallback to mapv(). + self.mapv(f) + } + } + /// Modify the array in place by calling `f` by mutable reference on each element. /// /// Elements are visited in arbitrary order. diff --git a/tests/array.rs b/tests/array.rs index 37065ba1b..d0fc67def 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -989,6 +989,20 @@ fn map1() { assert_eq!(a[(0, 0)], *c[(0, 0)]); } +#[test] +fn mapv_into_any_same_type() { + let a: Array = array![[1., 2., 3.], [4., 5., 6.]]; + let a_plus_one: Array = array![[2., 3., 4.], [5., 6., 7.]]; + assert_eq!(a.mapv_into_any(|a| a + 1.), a_plus_one); +} + +#[test] +fn mapv_into_any_diff_types() { + let a: Array = array![[1., 2., 3.], [4., 5., 6.]]; + let a_even: Array = array![[false, true, false], [true, false, true]]; + assert_eq!(a.mapv_into_any(|a| a.round() as i32 % 2 == 0), a_even); +} + #[test] fn as_slice_memory_order_mut_arcarray() { // Test that mutation breaks sharing for `ArcArray`.