diff --git a/src/map.rs b/src/map.rs index 2cce45fd..339bcd0e 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1303,6 +1303,34 @@ impl IndexMap { self.as_slice().binary_search_by_key(b, f) } + /// Checks if the keys of this map are sorted. + #[inline] + pub fn is_sorted(&self) -> bool + where + K: PartialOrd, + { + self.as_slice().is_sorted() + } + + /// Checks if this map is sorted using the given comparator function. + #[inline] + pub fn is_sorted_by<'a, F>(&'a self, cmp: F) -> bool + where + F: FnMut(&'a K, &'a V, &'a K, &'a V) -> bool, + { + self.as_slice().is_sorted_by(cmp) + } + + /// Checks if this map is sorted using the given sort-key function. + #[inline] + pub fn is_sorted_by_key<'a, F, T>(&'a self, sort_key: F) -> bool + where + F: FnMut(&'a K, &'a V) -> T, + T: PartialOrd, + { + self.as_slice().is_sorted_by_key(sort_key) + } + /// Returns the index of the partition point of a sorted map according to the given predicate /// (the index of the first element of the second partition). /// diff --git a/src/map/slice.rs b/src/map/slice.rs index b0537a30..59e685e3 100644 --- a/src/map/slice.rs +++ b/src/map/slice.rs @@ -258,6 +258,55 @@ impl Slice { self.binary_search_by(|k, v| f(k, v).cmp(b)) } + /// Checks if the keys of this slice are sorted. + #[inline] + pub fn is_sorted(&self) -> bool + where + K: PartialOrd, + { + // TODO(MSRV 1.82): self.entries.is_sorted_by(|a, b| a.key <= b.key) + self.is_sorted_by_key(|k, _| k) + } + + /// Checks if this slice is sorted using the given comparator function. + #[inline] + pub fn is_sorted_by<'a, F>(&'a self, mut cmp: F) -> bool + where + F: FnMut(&'a K, &'a V, &'a K, &'a V) -> bool, + { + // TODO(MSRV 1.82): self.entries + // .is_sorted_by(move |a, b| cmp(&a.key, &a.value, &b.key, &b.value)) + let mut iter = self.entries.iter(); + match iter.next() { + Some(mut prev) => iter.all(move |next| { + let sorted = cmp(&prev.key, &prev.value, &next.key, &next.value); + prev = next; + sorted + }), + None => true, + } + } + + /// Checks if this slice is sorted using the given sort-key function. + #[inline] + pub fn is_sorted_by_key<'a, F, T>(&'a self, mut sort_key: F) -> bool + where + F: FnMut(&'a K, &'a V) -> T, + T: PartialOrd, + { + // TODO(MSRV 1.82): self.entries + // .is_sorted_by_key(move |a| sort_key(&a.key, &a.value)) + let mut iter = self.entries.iter().map(move |a| sort_key(&a.key, &a.value)); + match iter.next() { + Some(mut prev) => iter.all(move |next| { + let sorted = prev <= next; + prev = next; + sorted + }), + None => true, + } + } + /// Returns the index of the partition point of a sorted map according to the given predicate /// (the index of the first element of the second partition). /// diff --git a/src/map/tests.rs b/src/map/tests.rs index 08692866..79041905 100644 --- a/src/map/tests.rs +++ b/src/map/tests.rs @@ -1264,3 +1264,49 @@ fn insert_sorted_by() { } assert_eq!(values, *map.as_slice()); } + +#[test] +fn is_sorted() { + fn expect(map: &IndexMap, e: [bool; 7]) { + assert_eq!(e[0], map.is_sorted()); + assert_eq!(e[1], map.is_sorted_by(|k1, _, k2, _| k1 < k2)); + assert_eq!(e[2], map.is_sorted_by(|k1, _, k2, _| k1 > k2)); + assert_eq!(e[3], map.is_sorted_by(|_, v1, _, v2| v1 < v2)); + assert_eq!(e[4], map.is_sorted_by(|_, v1, _, v2| v1 > v2)); + assert_eq!(e[5], map.is_sorted_by_key(|k, _| k)); + assert_eq!(e[6], map.is_sorted_by_key(|_, v| v)); + } + + let mut map = IndexMap::from_iter((0..10).map(|i| (i, i * i))); + expect(&map, [true, true, false, true, false, true, true]); + + map[5] = -1; + expect(&map, [true, true, false, false, false, true, false]); + + map[5] = 25; + map.replace_index(5, -1).unwrap(); + expect(&map, [false, false, false, true, false, false, true]); +} + +#[test] +fn is_sorted_trivial() { + fn expect(map: &IndexMap, e: [bool; 5]) { + assert_eq!(e[0], map.is_sorted()); + assert_eq!(e[1], map.is_sorted_by(|_, _, _, _| true)); + assert_eq!(e[2], map.is_sorted_by(|_, _, _, _| false)); + assert_eq!(e[3], map.is_sorted_by_key(|_, _| 0f64)); + assert_eq!(e[4], map.is_sorted_by_key(|_, _| f64::NAN)); + } + + let mut map = IndexMap::new(); + expect(&map, [true, true, true, true, true]); + + map.insert(0, 0); + expect(&map, [true, true, true, true, true]); + + map.insert(1, 1); + expect(&map, [true, true, false, true, false]); + + map.reverse(); + expect(&map, [false, true, false, true, false]); +} diff --git a/src/set.rs b/src/set.rs index 9a527f8d..df686a65 100644 --- a/src/set.rs +++ b/src/set.rs @@ -1074,6 +1074,34 @@ impl IndexSet { self.as_slice().binary_search_by_key(b, f) } + /// Checks if the values of this set are sorted. + #[inline] + pub fn is_sorted(&self) -> bool + where + T: PartialOrd, + { + self.as_slice().is_sorted() + } + + /// Checks if this set is sorted using the given comparator function. + #[inline] + pub fn is_sorted_by<'a, F>(&'a self, cmp: F) -> bool + where + F: FnMut(&'a T, &'a T) -> bool, + { + self.as_slice().is_sorted_by(cmp) + } + + /// Checks if this set is sorted using the given sort-key function. + #[inline] + pub fn is_sorted_by_key<'a, F, K>(&'a self, sort_key: F) -> bool + where + F: FnMut(&'a T) -> K, + K: PartialOrd, + { + self.as_slice().is_sorted_by_key(sort_key) + } + /// Returns the index of the partition point of a sorted set according to the given predicate /// (the index of the first element of the second partition). /// diff --git a/src/set/slice.rs b/src/set/slice.rs index ee79c8eb..666459db 100644 --- a/src/set/slice.rs +++ b/src/set/slice.rs @@ -160,6 +160,53 @@ impl Slice { self.binary_search_by(|k| f(k).cmp(b)) } + /// Checks if the values of this slice are sorted. + #[inline] + pub fn is_sorted(&self) -> bool + where + T: PartialOrd, + { + // TODO(MSRV 1.82): self.entries.is_sorted_by(|a, b| a.key <= b.key) + self.is_sorted_by(T::le) + } + + /// Checks if this slice is sorted using the given comparator function. + #[inline] + pub fn is_sorted_by<'a, F>(&'a self, mut cmp: F) -> bool + where + F: FnMut(&'a T, &'a T) -> bool, + { + // TODO(MSRV 1.82): self.entries.is_sorted_by(move |a, b| cmp(&a.key, &b.key)) + let mut iter = self.entries.iter(); + match iter.next() { + Some(mut prev) => iter.all(move |next| { + let sorted = cmp(&prev.key, &next.key); + prev = next; + sorted + }), + None => true, + } + } + + /// Checks if this slice is sorted using the given sort-key function. + #[inline] + pub fn is_sorted_by_key<'a, F, K>(&'a self, mut sort_key: F) -> bool + where + F: FnMut(&'a T) -> K, + K: PartialOrd, + { + // TODO(MSRV 1.82): self.entries.is_sorted_by_key(move |a| sort_key(&a.key)) + let mut iter = self.entries.iter().map(move |a| sort_key(&a.key)); + match iter.next() { + Some(mut prev) => iter.all(move |next| { + let sorted = prev <= next; + prev = next; + sorted + }), + None => true, + } + } + /// Returns the index of the partition point of a sorted set according to the given predicate /// (the index of the first element of the second partition). /// diff --git a/src/set/tests.rs b/src/set/tests.rs index e59f514c..bf761e08 100644 --- a/src/set/tests.rs +++ b/src/set/tests.rs @@ -1019,3 +1019,42 @@ fn test_partition_point() { assert_eq!(b.partition_point(|&x| x < 7), 2); assert_eq!(b.partition_point(|&x| x < 8), 3); } + +#[test] +fn is_sorted() { + fn expect(set: &IndexSet, e: [bool; 4]) { + assert_eq!(e[0], set.is_sorted()); + assert_eq!(e[1], set.is_sorted_by(|v1, v2| v1 < v2)); + assert_eq!(e[2], set.is_sorted_by(|v1, v2| v1 > v2)); + assert_eq!(e[3], set.is_sorted_by_key(|v| v)); + } + + let mut set = IndexSet::::from_iter(0..10); + expect(&set, [true, true, false, true]); + + set.replace_index(5, -1).unwrap(); + expect(&set, [false, false, false, false]); +} + +#[test] +fn is_sorted_trivial() { + fn expect(set: &IndexSet, e: [bool; 5]) { + assert_eq!(e[0], set.is_sorted()); + assert_eq!(e[1], set.is_sorted_by(|_, _| true)); + assert_eq!(e[2], set.is_sorted_by(|_, _| false)); + assert_eq!(e[3], set.is_sorted_by_key(|_| 0f64)); + assert_eq!(e[4], set.is_sorted_by_key(|_| f64::NAN)); + } + + let mut set = IndexSet::::default(); + expect(&set, [true, true, true, true, true]); + + set.insert(0); + expect(&set, [true, true, true, true, true]); + + set.insert(1); + expect(&set, [true, true, false, true, false]); + + set.reverse(); + expect(&set, [false, true, false, true, false]); +}