Skip to content

Commit

Permalink
feat: unique for arrays (#181)
Browse files Browse the repository at this point in the history
## 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
](#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<T> = 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 <[email protected]>
Co-authored-by: gaetbout <[email protected]>
  • Loading branch information
3 people authored Sep 29, 2023
1 parent 46c8d8a commit 7bc7b8a
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 1 deletion.
23 changes: 23 additions & 0 deletions src/data_structures/src/array_ext.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ trait ArrayTraitExt<T> {
self: @Array<T>
) -> Option<usize>;
fn dedup<impl TPartialEq: PartialEq<T>>(self: @Array<T>) -> Array<T>;
fn unique<impl TPartialEq: PartialEq<T>>(self: @Array<T>) -> Array<T>;
}

trait SpanTraitExt<T> {
Expand All @@ -45,6 +46,7 @@ trait SpanTraitExt<T> {
self: Span<T>
) -> Option<usize>;
fn dedup<impl TPartialEq: PartialEq<T>>(self: Span<T>) -> Array<T>;
fn unique<impl TPartialEq: PartialEq<T>>(self: Span<T>) -> Array<T>;
}

impl ArrayImpl<T, impl TCopy: Copy<T>, impl TDrop: Drop<T>> of ArrayTraitExt<T> {
Expand Down Expand Up @@ -141,6 +143,10 @@ impl ArrayImpl<T, impl TCopy: Copy<T>, impl TDrop: Drop<T>> of ArrayTraitExt<T>
fn dedup<impl TPartialEq: PartialEq<T>>(mut self: @Array<T>) -> Array<T> {
self.span().dedup()
}

fn unique<impl TPartialEq: PartialEq<T>>(mut self: @Array<T>) -> Array<T> {
self.span().unique()
}
}

impl SpanImpl<T, impl TCopy: Copy<T>, impl TDrop: Drop<T>> of SpanTraitExt<T> {
Expand Down Expand Up @@ -385,4 +391,21 @@ impl SpanImpl<T, impl TCopy: Copy<T>, impl TDrop: Drop<T>> of SpanTraitExt<T> {

ret
}

fn unique<impl TPartialEq: PartialEq<T>>(mut self: Span<T>) -> Array<T> {
let mut ret = array![];
loop {
match self.pop_front() {
Option::Some(v) => {
if !ret.contains(*v) {
ret.append(*v);
}
},
Option::None => {
break;
}
};
};
ret
}
}
78 changes: 77 additions & 1 deletion src/data_structures/src/tests/array_ext_test.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use core::option::OptionTrait;
use array::{ArrayTrait, SpanTrait};

use alexandria_data_structures::array_ext::{ArrayTraitExt, SpanTraitExt};

// dedup
Expand Down Expand Up @@ -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<u128> = 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

Expand Down

0 comments on commit 7bc7b8a

Please sign in to comment.