From 7bc7b8a03cdf29570c217f68e25d1b3c2a9a6b9d Mon Sep 17 00:00:00 2001 From: akhercha Date: Fri, 29 Sep 2023 12:04:02 +0200 Subject: [PATCH] feat: unique for arrays (#181) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Pull Request type - [ ] Bugfix - [X] Feature - [ ] Code style update (formatting, renaming) - [ ] Refactoring (no functional changes, no API changes) - [ ] Build-related changes - [ ] Documentation content changes - [ ] Other (please describe): ## What is the current behavior? Issue number [#172 ](https://github.com/keep-starknet-strange/alexandria/issues/172) ## What is the new behavior? - Added the `unique` methods for `SpanTraitExt` & `ArrayTraitExt`, - Added the corresponding unit tests. ## Does this introduce a breaking change? - [ ] Yes - [X] No ## Other information For this little snippet: ```rust let shortest: Span = if (self.len() <= 1) || (ret.len() <= self.len()) { ret.span() } else { self }; ``` Without the `self.len() <= 1` member some of my tests were failing. Tried multiple things but couldn't find any better even though I feel like there's a more optimized solution... Curious to have your inputs! 🙏 --------- Co-authored-by: akhercha Co-authored-by: gaetbout --- src/data_structures/src/array_ext.cairo | 23 ++++++ .../src/tests/array_ext_test.cairo | 78 ++++++++++++++++++- 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/src/data_structures/src/array_ext.cairo b/src/data_structures/src/array_ext.cairo index a1420a3c..aa022d3d 100644 --- a/src/data_structures/src/array_ext.cairo +++ b/src/data_structures/src/array_ext.cairo @@ -22,6 +22,7 @@ trait ArrayTraitExt { self: @Array ) -> Option; fn dedup>(self: @Array) -> Array; + fn unique>(self: @Array) -> Array; } trait SpanTraitExt { @@ -45,6 +46,7 @@ trait SpanTraitExt { self: Span ) -> Option; fn dedup>(self: Span) -> Array; + fn unique>(self: Span) -> Array; } impl ArrayImpl, impl TDrop: Drop> of ArrayTraitExt { @@ -141,6 +143,10 @@ impl ArrayImpl, impl TDrop: Drop> of ArrayTraitExt fn dedup>(mut self: @Array) -> Array { self.span().dedup() } + + fn unique>(mut self: @Array) -> Array { + self.span().unique() + } } impl SpanImpl, impl TDrop: Drop> of SpanTraitExt { @@ -385,4 +391,21 @@ impl SpanImpl, impl TDrop: Drop> of SpanTraitExt { ret } + + fn unique>(mut self: Span) -> Array { + let mut ret = array![]; + loop { + match self.pop_front() { + Option::Some(v) => { + if !ret.contains(*v) { + ret.append(*v); + } + }, + Option::None => { + break; + } + }; + }; + ret + } } diff --git a/src/data_structures/src/tests/array_ext_test.cairo b/src/data_structures/src/tests/array_ext_test.cairo index e4f95426..63c2178b 100644 --- a/src/data_structures/src/tests/array_ext_test.cairo +++ b/src/data_structures/src/tests/array_ext_test.cairo @@ -1,6 +1,5 @@ use core::option::OptionTrait; use array::{ArrayTrait, SpanTrait}; - use alexandria_data_structures::array_ext::{ArrayTraitExt, SpanTraitExt}; // dedup @@ -1068,6 +1067,83 @@ fn index_of_max_last_span() { assert(arr.len() == 3, 'arr should not be consummed'); } +// unique + +#[test] +#[available_gas(2000000)] +fn unique() { + let mut arr = array![32_u128, 256_u128, 128_u128, 256_u128, 1024_u128]; + let mut out_arr = arr.unique(); + assert(out_arr.len() == 4, 'Duplicates should be dropped'); + assert(*out_arr[0] == 32_u128, 'Should be 32'); + assert(*out_arr[1] == 256_u128, 'Should be 256'); + assert(*out_arr[2] == 128_u128, 'Should be 128'); + assert(*out_arr[3] == 1024_u128, 'Should be 1024'); +} + +#[test] +#[available_gas(2000000)] +fn unique_all() { + let mut arr = array![84_u128, 84_u128, 84_u128]; + let mut out_arr = arr.unique(); + assert(out_arr.len() == 1, 'Duplicates should be dropped'); + assert(*out_arr[0] == 84_u128, 'Should be 128'); +} + +#[test] +#[available_gas(2000000)] +fn unique_none() { + let mut arr: Array = array![]; + let mut out_arr = arr.unique(); + assert(out_arr.len() == 0, 'out_arr should be empty'); +} + +#[test] +#[available_gas(2000000)] +fn unique_at_start() { + let mut arr = array![16_u128, 16_u128, 16_u128, 128_u128, 64_u128, 32_u128]; + let mut out_arr = arr.unique(); + assert(out_arr.len() == 4, 'Duplicates should be dropped'); + assert(*out_arr[0] == 16_u128, 'Should be 16'); + assert(*out_arr[1] == 128_u128, 'Should be 128'); + assert(*out_arr[2] == 64_u128, 'Should be 64'); + assert(*out_arr[3] == 32_u128, 'Should be 32'); +} + +#[test] +#[available_gas(2000000)] +fn unique_at_middle() { + let mut arr = array![128_u128, 256_u128, 84_u128, 84_u128, 84_u128, 1_u128]; + let mut out_arr = arr.unique(); + assert(out_arr.len() == 4, 'Duplicates should be dropped'); + assert(*out_arr[0] == 128_u128, 'Should be 128'); + assert(*out_arr[1] == 256_u128, 'Should be 256'); + assert(*out_arr[2] == 84_u128, 'Should be 84'); + assert(*out_arr[3] == 1_u128, 'Should be 1'); +} + +#[test] +#[available_gas(2000000)] +fn unique_at_end() { + let mut arr = array![32_u128, 16_u128, 64_u128, 128_u128, 128_u128, 128_u128]; + let mut out_arr = arr.unique(); + assert(out_arr.len() == 4, 'Duplicates should be dropped'); + assert(*out_arr[0] == 32_u128, 'Should be 32'); + assert(*out_arr[1] == 16_u128, 'Should be 16'); + assert(*out_arr[2] == 64_u128, 'Should be 64'); + assert(*out_arr[3] == 128_u128, 'Should be 128'); +} + +#[test] +#[available_gas(2000000)] +fn unique_without_duplicates() { + let mut arr = array![42_u128, 84_u128, 21_u128]; + let mut out_arr = arr.unique(); + assert(out_arr.len() == 3, 'No values should drop'); + assert(*out_arr[0] == 42_u128, 'Should be 42'); + assert(*out_arr[1] == 84_u128, 'Should be 84'); + assert(*out_arr[2] == 21_u128, 'Should be 21'); +} // Utility fn