Skip to content

Commit 78bccb3

Browse files
committed
Introduce the partition_dedup/by/by_key methods for slices
1 parent 7714c43 commit 78bccb3

File tree

4 files changed

+237
-5
lines changed

4 files changed

+237
-5
lines changed

src/liballoc/vec.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -947,10 +947,9 @@ impl<T> Vec<T> {
947947
/// Removes all but the first of consecutive elements in the vector satisfying a given equality
948948
/// relation.
949949
///
950-
/// The `same_bucket` function is passed references to two elements from the vector, and
951-
/// returns `true` if the elements compare equal, or `false` if they do not. The elements are
952-
/// passed in opposite order from their order in the vector, so if `same_bucket(a, b)` returns
953-
/// `true`, `a` is removed.
950+
/// The `same_bucket` function is passed references to two elements from the vector and
951+
/// must determine if the elements compare equal. The elements are passed in opposite order
952+
/// from their order in the slice, so if `same_bucket(a, b)` returns `true`, `a` is removed.
954953
///
955954
/// If the vector is sorted, this removes all duplicates.
956955
///
@@ -1533,7 +1532,8 @@ impl<'a> Drop for SetLenOnDrop<'a> {
15331532
}
15341533

15351534
impl<T: PartialEq> Vec<T> {
1536-
/// Removes consecutive repeated elements in the vector.
1535+
/// Removes consecutive repeated elements in the vector according to the
1536+
/// [`PartialEq`] trait implementation.
15371537
///
15381538
/// If the vector is sorted, this removes all duplicates.
15391539
///

src/libcore/slice/mod.rs

+172
Original file line numberDiff line numberDiff line change
@@ -1402,6 +1402,178 @@ impl<T> [T] {
14021402
sort::quicksort(self, |a, b| f(a).lt(&f(b)));
14031403
}
14041404

1405+
/// Moves all consecutive repeated elements to the end of the slice according to the
1406+
/// [`PartialEq`] trait implementation.
1407+
///
1408+
/// Returns two slices. The first contains no consecutive repeated elements.
1409+
/// The second contains all the duplicates in no specified order.
1410+
///
1411+
/// If the slice is sorted, the first returned slice contains no duplicates.
1412+
///
1413+
/// # Examples
1414+
///
1415+
/// ```
1416+
/// #![feature(slice_partition_dedup)]
1417+
///
1418+
/// let mut slice = [1, 2, 2, 3, 3, 2, 1, 1];
1419+
///
1420+
/// let (dedup, duplicates) = slice.partition_dedup();
1421+
///
1422+
/// assert_eq!(dedup, [1, 2, 3, 2, 1]);
1423+
/// assert_eq!(duplicates, [2, 3, 1]);
1424+
/// ```
1425+
#[unstable(feature = "slice_partition_dedup", issue = "54279")]
1426+
#[inline]
1427+
pub fn partition_dedup(&mut self) -> (&mut [T], &mut [T])
1428+
where T: PartialEq
1429+
{
1430+
self.partition_dedup_by(|a, b| a == b)
1431+
}
1432+
1433+
/// Moves all but the first of consecutive elements to the end of the slice satisfying
1434+
/// a given equality relation.
1435+
///
1436+
/// Returns two slices. The first contains no consecutive repeated elements.
1437+
/// The second contains all the duplicates in no specified order.
1438+
///
1439+
/// The `same_bucket` function is passed references to two elements from the slice and
1440+
/// must determine if the elements compare equal. The elements are passed in opposite order
1441+
/// from their order in the slice, so if `same_bucket(a, b)` returns `true`, `a` is moved
1442+
/// at the end of the slice.
1443+
///
1444+
/// If the slice is sorted, the first returned slice contains no duplicates.
1445+
///
1446+
/// # Examples
1447+
///
1448+
/// ```
1449+
/// #![feature(slice_partition_dedup)]
1450+
///
1451+
/// let mut slice = ["foo", "Foo", "BAZ", "Bar", "bar", "baz", "BAZ"];
1452+
///
1453+
/// let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.eq_ignore_ascii_case(b));
1454+
///
1455+
/// assert_eq!(dedup, ["foo", "BAZ", "Bar", "baz"]);
1456+
/// assert_eq!(duplicates, ["bar", "Foo", "BAZ"]);
1457+
/// ```
1458+
#[unstable(feature = "slice_partition_dedup", issue = "54279")]
1459+
#[inline]
1460+
pub fn partition_dedup_by<F>(&mut self, mut same_bucket: F) -> (&mut [T], &mut [T])
1461+
where F: FnMut(&mut T, &mut T) -> bool
1462+
{
1463+
// Although we have a mutable reference to `self`, we cannot make
1464+
// *arbitrary* changes. The `same_bucket` calls could panic, so we
1465+
// must ensure that the slice is in a valid state at all times.
1466+
//
1467+
// The way that we handle this is by using swaps; we iterate
1468+
// over all the elements, swapping as we go so that at the end
1469+
// the elements we wish to keep are in the front, and those we
1470+
// wish to reject are at the back. We can then split the slice.
1471+
// This operation is still O(n).
1472+
//
1473+
// Example: We start in this state, where `r` represents "next
1474+
// read" and `w` represents "next_write`.
1475+
//
1476+
// r
1477+
// +---+---+---+---+---+---+
1478+
// | 0 | 1 | 1 | 2 | 3 | 3 |
1479+
// +---+---+---+---+---+---+
1480+
// w
1481+
//
1482+
// Comparing self[r] against self[w-1], this is not a duplicate, so
1483+
// we swap self[r] and self[w] (no effect as r==w) and then increment both
1484+
// r and w, leaving us with:
1485+
//
1486+
// r
1487+
// +---+---+---+---+---+---+
1488+
// | 0 | 1 | 1 | 2 | 3 | 3 |
1489+
// +---+---+---+---+---+---+
1490+
// w
1491+
//
1492+
// Comparing self[r] against self[w-1], this value is a duplicate,
1493+
// so we increment `r` but leave everything else unchanged:
1494+
//
1495+
// r
1496+
// +---+---+---+---+---+---+
1497+
// | 0 | 1 | 1 | 2 | 3 | 3 |
1498+
// +---+---+---+---+---+---+
1499+
// w
1500+
//
1501+
// Comparing self[r] against self[w-1], this is not a duplicate,
1502+
// so swap self[r] and self[w] and advance r and w:
1503+
//
1504+
// r
1505+
// +---+---+---+---+---+---+
1506+
// | 0 | 1 | 2 | 1 | 3 | 3 |
1507+
// +---+---+---+---+---+---+
1508+
// w
1509+
//
1510+
// Not a duplicate, repeat:
1511+
//
1512+
// r
1513+
// +---+---+---+---+---+---+
1514+
// | 0 | 1 | 2 | 3 | 1 | 3 |
1515+
// +---+---+---+---+---+---+
1516+
// w
1517+
//
1518+
// Duplicate, advance r. End of slice. Split at w.
1519+
1520+
let len = self.len();
1521+
if len <= 1 {
1522+
return (self, &mut [])
1523+
}
1524+
1525+
let ptr = self.as_mut_ptr();
1526+
let mut next_read: usize = 1;
1527+
let mut next_write: usize = 1;
1528+
1529+
unsafe {
1530+
// Avoid bounds checks by using raw pointers.
1531+
while next_read < len {
1532+
let ptr_read = ptr.add(next_read);
1533+
let prev_ptr_write = ptr.add(next_write - 1);
1534+
if !same_bucket(&mut *ptr_read, &mut *prev_ptr_write) {
1535+
if next_read != next_write {
1536+
let ptr_write = prev_ptr_write.offset(1);
1537+
mem::swap(&mut *ptr_read, &mut *ptr_write);
1538+
}
1539+
next_write += 1;
1540+
}
1541+
next_read += 1;
1542+
}
1543+
}
1544+
1545+
self.split_at_mut(next_write)
1546+
}
1547+
1548+
/// Moves all but the first of consecutive elements to the end of the slice that resolve
1549+
/// to the same key.
1550+
///
1551+
/// Returns two slices. The first contains no consecutive repeated elements.
1552+
/// The second contains all the duplicates in no specified order.
1553+
///
1554+
/// If the slice is sorted, the first returned slice contains no duplicates.
1555+
///
1556+
/// # Examples
1557+
///
1558+
/// ```
1559+
/// #![feature(slice_partition_dedup)]
1560+
///
1561+
/// let mut slice = [10, 20, 21, 30, 30, 20, 11, 13];
1562+
///
1563+
/// let (dedup, duplicates) = slice.partition_dedup_by_key(|i| *i / 10);
1564+
///
1565+
/// assert_eq!(dedup, [10, 20, 30, 20, 11]);
1566+
/// assert_eq!(duplicates, [21, 30, 13]);
1567+
/// ```
1568+
#[unstable(feature = "slice_partition_dedup", issue = "54279")]
1569+
#[inline]
1570+
pub fn partition_dedup_by_key<K, F>(&mut self, mut key: F) -> (&mut [T], &mut [T])
1571+
where F: FnMut(&mut T) -> K,
1572+
K: PartialEq,
1573+
{
1574+
self.partition_dedup_by(|a, b| key(a) == key(b))
1575+
}
1576+
14051577
/// Rotates the slice in-place such that the first `mid` elements of the
14061578
/// slice move to the end while the last `self.len() - mid` elements move to
14071579
/// the front. After calling `rotate_left`, the element previously at index

src/libcore/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#![feature(inner_deref)]
4040
#![feature(slice_internals)]
4141
#![feature(option_replace)]
42+
#![feature(slice_partition_dedup)]
4243
#![feature(copy_within)]
4344

4445
extern crate core;

src/libcore/tests/slice.rs

+59
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,65 @@ fn test_align_to_empty_mid() {
10011001
}
10021002
}
10031003

1004+
#[test]
1005+
fn test_slice_partition_dedup_by() {
1006+
let mut slice: [i32; 9] = [1, -1, 2, 3, 1, -5, 5, -2, 2];
1007+
1008+
let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.abs() == b.abs());
1009+
1010+
assert_eq!(dedup, [1, 2, 3, 1, -5, -2]);
1011+
assert_eq!(duplicates, [5, -1, 2]);
1012+
}
1013+
1014+
#[test]
1015+
fn test_slice_partition_dedup_empty() {
1016+
let mut slice: [i32; 0] = [];
1017+
1018+
let (dedup, duplicates) = slice.partition_dedup();
1019+
1020+
assert_eq!(dedup, []);
1021+
assert_eq!(duplicates, []);
1022+
}
1023+
1024+
#[test]
1025+
fn test_slice_partition_dedup_one() {
1026+
let mut slice = [12];
1027+
1028+
let (dedup, duplicates) = slice.partition_dedup();
1029+
1030+
assert_eq!(dedup, [12]);
1031+
assert_eq!(duplicates, []);
1032+
}
1033+
1034+
#[test]
1035+
fn test_slice_partition_dedup_multiple_ident() {
1036+
let mut slice = [12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11];
1037+
1038+
let (dedup, duplicates) = slice.partition_dedup();
1039+
1040+
assert_eq!(dedup, [12, 11]);
1041+
assert_eq!(duplicates, [12, 12, 12, 12, 11, 11, 11, 11, 11]);
1042+
}
1043+
1044+
#[test]
1045+
fn test_slice_partition_dedup_partialeq() {
1046+
#[derive(Debug)]
1047+
struct Foo(i32, i32);
1048+
1049+
impl PartialEq for Foo {
1050+
fn eq(&self, other: &Foo) -> bool {
1051+
self.0 == other.0
1052+
}
1053+
}
1054+
1055+
let mut slice = [Foo(0, 1), Foo(0, 5), Foo(1, 7), Foo(1, 9)];
1056+
1057+
let (dedup, duplicates) = slice.partition_dedup();
1058+
1059+
assert_eq!(dedup, [Foo(0, 1), Foo(1, 7)]);
1060+
assert_eq!(duplicates, [Foo(0, 5), Foo(1, 9)]);
1061+
}
1062+
10041063
#[test]
10051064
fn test_copy_within() {
10061065
// Start to end, with a RangeTo.

0 commit comments

Comments
 (0)