Skip to content

Commit 1201c54

Browse files
authored
Merge pull request #402 from 4e554c4c/insert_by
Add `insert_sorted_by{,_key}` methods for map/set
2 parents a168b17 + d4ee508 commit 1201c54

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

src/map.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,54 @@ where
493493
}
494494
}
495495

496+
/// Insert a key-value pair in the map at its ordered position among keys
497+
/// sorted by `cmp`.
498+
///
499+
/// This is equivalent to finding the position with
500+
/// [`binary_search_by`][Self::binary_search_by], then calling
501+
/// [`insert_before`][Self::insert_before] with the given key and value.
502+
///
503+
/// If the existing keys are **not** already sorted, then the insertion
504+
/// index is unspecified (like [`slice::binary_search`]), but the key-value
505+
/// pair is moved to or inserted at that position regardless.
506+
///
507+
/// Computes in **O(n)** time (average).
508+
pub fn insert_sorted_by<F>(&mut self, cmp: F, key: K, value: V) -> (usize, Option<V>)
509+
where
510+
K: Ord,
511+
F: FnMut(&K, &V) -> Ordering,
512+
{
513+
let (Ok(i) | Err(i)) = self.binary_search_by(cmp);
514+
self.insert_before(i, key, value)
515+
}
516+
517+
/// Insert a key-value pair in the map at its ordered position
518+
/// using a sort-key extraction function.
519+
///
520+
/// This is equivalent to finding the position with
521+
/// [`binary_search_by_key`][Self::binary_search_by_key] with `sort_key(key)`, then
522+
/// calling [`insert_before`][Self::insert_before] with the given key and value.
523+
///
524+
/// If the existing keys are **not** already sorted, then the insertion
525+
/// index is unspecified (like [`slice::binary_search`]), but the key-value
526+
/// pair is moved to or inserted at that position regardless.
527+
///
528+
/// Computes in **O(n)** time (average).
529+
pub fn insert_sorted_by_key<F, B>(
530+
&mut self,
531+
mut sort_key: F,
532+
key: K,
533+
value: V,
534+
) -> (usize, Option<V>)
535+
where
536+
B: Ord,
537+
F: FnMut(&K, &V) -> B,
538+
{
539+
let search_key = sort_key(&key, &value);
540+
let (Ok(i) | Err(i)) = self.binary_search_by_key(&search_key, sort_key);
541+
self.insert_before(i, key, value)
542+
}
543+
496544
/// Insert a key-value pair in the map before the entry at the given index, or at the end.
497545
///
498546
/// If an equivalent key already exists in the map: the key remains and

src/map/tests.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,3 +1226,41 @@ fn disjoint_indices_mut_fail_duplicate() {
12261226
Err(crate::GetDisjointMutError::OverlappingIndices)
12271227
);
12281228
}
1229+
1230+
#[test]
1231+
fn insert_sorted_by_key() {
1232+
let mut values = [(-1, 8), (3, 18), (-27, 2), (-2, 5)];
1233+
let mut map: IndexMap<i32, i32> = IndexMap::new();
1234+
for (key, value) in values {
1235+
let (_, old) = map.insert_sorted_by_key(|k, _| k.abs(), key, value);
1236+
assert_eq!(old, None);
1237+
}
1238+
values.sort_by_key(|(key, _)| key.abs());
1239+
assert_eq!(values, *map.as_slice());
1240+
1241+
for (key, value) in &mut values {
1242+
let (_, old) = map.insert_sorted_by_key(|k, _| k.abs(), *key, -*value);
1243+
assert_eq!(old, Some(*value));
1244+
*value = -*value;
1245+
}
1246+
assert_eq!(values, *map.as_slice());
1247+
}
1248+
1249+
#[test]
1250+
fn insert_sorted_by() {
1251+
let mut values = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)];
1252+
let mut map: IndexMap<i32, i32> = IndexMap::new();
1253+
for (key, value) in values {
1254+
let (_, old) = map.insert_sorted_by(|probe, _| key.cmp(probe), key, value);
1255+
assert_eq!(old, None);
1256+
}
1257+
values.reverse();
1258+
assert_eq!(values, *map.as_slice());
1259+
1260+
for (key, value) in &mut values {
1261+
let (_, old) = map.insert_sorted_by(|probe, _| (*key).cmp(probe), *key, -*value);
1262+
assert_eq!(old, Some(*value));
1263+
*value = -*value;
1264+
}
1265+
assert_eq!(values, *map.as_slice());
1266+
}

src/set.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,48 @@ where
422422
(index, existing.is_none())
423423
}
424424

425+
/// Insert the value into the set at its ordered position among values
426+
/// sorted by `cmp`.
427+
///
428+
/// This is equivalent to finding the position with
429+
/// [`binary_search_by`][Self::binary_search_by], then calling
430+
/// [`insert_before`][Self::insert_before].
431+
///
432+
/// If the existing items are **not** already sorted, then the insertion
433+
/// index is unspecified (like [`slice::binary_search`]), but the value
434+
/// is moved to or inserted at that position regardless.
435+
///
436+
/// Computes in **O(n)** time (average).
437+
pub fn insert_sorted_by<F>(&mut self, mut cmp: F, value: T) -> (usize, bool)
438+
where
439+
T: Ord,
440+
F: FnMut(&T) -> Ordering,
441+
{
442+
let (index, existing) = self.map.insert_sorted_by(|k, _| cmp(k), value, ());
443+
(index, existing.is_none())
444+
}
445+
446+
/// Insert the value into the set at its ordered position among values
447+
/// using a sort-key extraction function.
448+
///
449+
/// This is equivalent to finding the position with
450+
/// [`binary_search_by_key`][Self::binary_search_by_key] with `sort_key(key)`,
451+
/// then calling [`insert_before`][Self::insert_before].
452+
///
453+
/// If the existing items are **not** already sorted, then the insertion
454+
/// index is unspecified (like [`slice::binary_search`]), but the value
455+
/// is moved to or inserted at that position regardless.
456+
///
457+
/// Computes in **O(n)** time (average).
458+
pub fn insert_sorted_by_key<F, B>(&mut self, mut sort_key: F, value: T) -> (usize, bool)
459+
where
460+
B: Ord,
461+
F: FnMut(&T) -> B,
462+
{
463+
let (index, existing) = self.map.insert_sorted_by_key(|k, _| sort_key(k), value, ());
464+
(index, existing.is_none())
465+
}
466+
425467
/// Insert the value into the set before the value at the given index, or at the end.
426468
///
427469
/// If an equivalent item already exists in the set, it returns `false` leaving the

0 commit comments

Comments
 (0)