Skip to content

Commit

Permalink
Merge pull request #128 from linclelinkpart5/master
Browse files Browse the repository at this point in the history
Adding `reverse()` to `IndexMap` & `IndexSet`
  • Loading branch information
cuviper authored Jun 9, 2020
2 parents da18351 + a442475 commit 8c9df6b
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 0 deletions.
26 changes: 26 additions & 0 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1253,6 +1253,13 @@ where
self.into_iter()
}

/// Reverses the order of the map’s key-value pairs in place.
///
/// Computes in **O(n)** time and **O(1)** space.
pub fn reverse(&mut self) {
self.core.reverse()
}

/// Clears the `IndexMap`, returning all key-value pairs as a drain iterator.
/// Keeps the allocated memory for reuse.
pub fn drain(&mut self, range: RangeFull) -> Drain<K, V> {
Expand Down Expand Up @@ -1752,6 +1759,25 @@ impl<K, V> OrderMapCore<K, V> {
self.restore_hash_index(side_index);
}

fn reverse(&mut self) {
self.entries.reverse();

// No need to save hash indices, can easily calculate what they should
// be, given that this is an in-place reversal.
dispatch_32_vs_64!(self => apply_new_index(&mut self.indices, self.entries.len()));

fn apply_new_index<Sz>(indices: &mut [Pos], len: usize)
where
Sz: Size,
{
for pos in indices {
if let Some((i, _)) = pos.resolve::<Sz>() {
pos.set_pos::<Sz>(len - i - 1);
}
}
}
}

fn save_hash_index(&mut self) -> Vec<usize> {
// Temporarily use the hash field in a bucket to store the old index.
// Save the old hash values in `side_index`. Then we can sort
Expand Down
7 changes: 7 additions & 0 deletions src/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,13 @@ where
}
}

/// Reverses the order of the set’s values in place.
///
/// Computes in **O(n)** time and **O(1)** space.
pub fn reverse(&mut self) {
self.map.reverse()
}

/// Clears the `IndexSet`, returning all values as a drain iterator.
/// Keeps the allocated memory for reuse.
pub fn drain(&mut self, range: RangeFull) -> Drain<T> {
Expand Down
53 changes: 53 additions & 0 deletions tests/quick.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,59 @@ quickcheck! {
map.sort_by(|_, v1, _, v2| Ord::cmp(v1, v2));
assert_sorted_by_key(map, |t| t.1);
}

fn reverse(keyvals: Large<Vec<(i8, i8)>>) -> () {
let mut map: IndexMap<_, _> = IndexMap::from_iter(keyvals.to_vec());

fn generate_answer(input: &Vec<(i8, i8)>) -> Vec<(i8, i8)> {
// to mimic what `IndexMap::from_iter` does:
// need to get (A) the unique keys in forward order, and (B) the
// last value of each of those keys.

// create (A): an iterable that yields the unique keys in ltr order
let mut seen_keys = HashSet::new();
let unique_keys_forward = input.iter().filter_map(move |(k, _)| {
if seen_keys.contains(k) { None }
else { seen_keys.insert(*k); Some(*k) }
});

// create (B): a mapping of keys to the last value seen for that key
// this is the same as reversing the input and taking the first
// value seen for that key!
let mut last_val_per_key = HashMap::new();
for &(k, v) in input.iter().rev() {
if !last_val_per_key.contains_key(&k) {
last_val_per_key.insert(k, v);
}
}

// iterate over the keys in (A) in order, and match each one with
// the corresponding last value from (B)
let mut ans: Vec<_> = unique_keys_forward
.map(|k| (k, *last_val_per_key.get(&k).unwrap()))
.collect();

// finally, since this test is testing `.reverse()`, reverse the
// answer in-place
ans.reverse();

ans
}

let answer = generate_answer(&keyvals.0);

// perform the work
map.reverse();

// check it contains all the values it should
for &(key, val) in &answer {
assert_eq!(map[&key], val);
}

// check the order
let mapv = Vec::from_iter(map);
assert_eq!(answer, mapv);
}
}

fn assert_sorted_by_key<I, Key, X>(iterable: I, key: Key)
Expand Down

0 comments on commit 8c9df6b

Please sign in to comment.