#143 May 17, 2026

143. Vec::dedup_by_key — Collapse Consecutive Duplicates by a Derived Key

Vec::dedup() only collapses runs that are exactly equal. When you care about a derived attribute — the minute on a timestamp, the domain in an email, the first letter of a word — reach for dedup_by_key.

The plain dedup() is strict: two adjacent elements are only merged if == says so.

1
2
3
let mut nums = vec![1, 1, 2, 3, 3, 3, 2, 5];
nums.dedup();
assert_eq!(nums, vec![1, 2, 3, 2, 5]);

But often “duplicate” really means “shares some property with its neighbour.” dedup_by_key takes a closure that maps each element to a key, then keeps the first of every consecutive run whose keys match:

1
2
3
let mut nums = vec![1, 3, 5, 2, 4, 7, 6, 8];
nums.dedup_by_key(|n| *n % 2);
assert_eq!(nums, vec![1, 2, 7, 6]);

1, 3, 5 all have key 1 → keep 1. Then 2, 4 have key 0 → keep 2. Then 7 has key 1 → keep it. Then 6, 8 have key 0 → keep 6.

The practical case: log lines already in time order, and you want one representative per minute.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
let mut lines = vec![
    "12:01 GET /a".to_string(),
    "12:01 GET /b".to_string(),
    "12:01 POST /c".to_string(),
    "12:02 GET /d".to_string(),
    "12:02 GET /e".to_string(),
    "12:03 PATCH /f".to_string(),
];

lines.dedup_by_key(|line| line[..5].to_string());

assert_eq!(lines.len(), 3);
assert_eq!(lines[0], "12:01 GET /a");
assert_eq!(lines[1], "12:02 GET /d");
assert_eq!(lines[2], "12:03 PATCH /f");

Two things worth remembering. First, it only looks at adjacent pairs — if you need full uniqueness, pair it with sort_by_key first. Second, if your equivalence isn’t expressible as a key (e.g. “values within 0.1 of each other”), there’s a sibling dedup_by that takes |a, b| -> bool directly. All three are in-place, allocation-free, and run in linear time.

← Previous 145. Duration::from_nanos_u128 — Round-Trip Nanoseconds Without the u64 Cast