ditoslav
ditoslav

Reputation: 4872

How to group by non consecutive Vec elements in Rust?

I have a Vec<(A, B)> and I want to group by A but not just consecutive but all elements in the Vec. The closest thing I found is Itertools::group_by which only works on consecutive values. I understand that the consecutiveness is related to optimizing allocation but I just want a regular C# group by. Priority is to not have to use a new library just for this.

A is not hashable, only Ord. I want a resulting Vec<(A, Vec<(A, B))> or equivalent

Upvotes: 7

Views: 4554

Answers (1)

Sven Marnach
Sven Marnach

Reputation: 602155

Assuming that "comparable" means A: Ord, i.e. that there is a total ordering on A, you can fold an iterator over items of type (A, B) into a BTreeMap from A to Vec<B>:

use std::collections::BTreeMap;

fn group_pairs<A, B, I>(v: I) -> BTreeMap<A, Vec<B>>
where
    A: Ord,
    I: IntoIterator<Item = (A, B)>,
{
    v.into_iter().fold(BTreeMap::new(), |mut acc, (a, b)| {
        acc.entry(a).or_default().push(b);
        acc
    })
}

Some people prefer a for loop over a fold:

fn group_pairs<A, B, I>(v: I) -> BTreeMap<A, Vec<B>>
where
    A: Ord,
    I: IntoIterator<Item = (A, B)>,
{
    let mut result = BTreeMap::<A, Vec<B>>::new();
    for (a, b) in v {
        result.entry(a).or_default().push(b);
    }
    result
}

Example:

let data = vec![(1, 2), (2, 3), (1, 1), (2, 4), (3, 5)];
let grouped = vec![(1, vec![2, 1]), (2, vec![3, 4]), (3, vec![5])];
assert_eq!(group_pairs(data).into_iter().collect::<Vec<_>>(), grouped);

Upvotes: 17

Related Questions