orhun
orhun

Reputation: 89

Grouping similar elements in a Vector of Vector in Rust

I have to following piece of code that will have to find groups by comparing different vector elements:

fn main() {
    let data = vec![
        vec!["EG 1", "EG 2", "EG 3"],
        vec!["XG 1", "XG 3"],
        vec!["SG 1", "SG 6", "SG 8"],
        vec!["PS 1", "PS 8"],
    ];
    let result = Vec::<Vec<&str>>::new();
    //
    // element1.split_whitespace().last() == element2.split_whitespace().last()
    //
    assert_eq!(
        vec![
            vec!["EG 1", "XG 1", "PS 1", "SG 1"],
            vec!["EG 2"],
            vec!["XG 3", "EG 3"],
            vec!["SG 6"],
            vec!["SG 8", "PS 8"]
        ],
        result
    );
}

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7101df9439dd9cdff9f4b36c99a4efc6

As I commented out, I'd like to use the trailing number for comparing the elements:

element1.split_whitespace().last() == element2.split_whitespace().last()

I'm thinking of changing it in the future to use some library like fuzzywuzzy-rs but it's OK to compare as above for testing the algorithm.

I couldn't construct the loop properly and I have two issues that I'd like to point out:

Upvotes: 0

Views: 774

Answers (2)

Jason
Jason

Reputation: 5575

If you'd want to group elements, I would opt for a map-like structure.

The following seems to be what you're after:

use std::collections::BTreeMap;

fn main() {
    let data = vec![
        vec!["EG 1", "EG 2", "EG 3"],
        vec!["XG 1", "XG 3"],
        vec!["SG 1", "SG 6", "SG 8"],
        vec!["PS 1", "PS 8"],
    ];
    
    let mut result = BTreeMap::<usize, Vec<String>>::new();
    
    for values in data.iter() {
        for &s in values.iter() {
            let id = s.split_whitespace()
                .last()
                .map(|s| s.parse::<usize>().unwrap())
                .unwrap();
                
            result.entry(id).or_default().push(s.to_owned());
        }
    }
    
    println!("{:?}", result.values());
}

Playground

Using BTreeMap::values you can then get the values back out into a Vec, ordered by their key:

[
    [ "EG 1", "XG 1", "SG 1", "PS 1" ],
    [ "EG 2" ],
    [ "EG 3", "XG 3" ],
    [ "SG 6" ],
    [ "SG 8", "PS 8" ],
]

Upvotes: 1

Angelicos Phosphoros
Angelicos Phosphoros

Reputation: 3057

You need something like that:

fn main() {
    let data = vec![
        vec!["EG 1", "EG 2", "EG 3"],
        vec!["XG 1", "XG 3"],
        vec!["SG 1", "SG 6", "SG 8"],
        vec!["PS 1", "PS 8"],
    ];
    
    let mut map = std::collections::HashMap::new();
    for vv in data.iter(){
        for v in vv.iter().copied(){
            let k = v.split(" ").nth(1).unwrap();
            map.entry(k).or_insert(Vec::new()).push(v);
        }
    }
    
    let result: Vec<_> = {
        let mut result: Vec<(&str, Vec<_>)> = map.into_iter().collect();
        result.sort_by(|a, b|a.0.cmp(b.0));
        result.into_iter().map(|(_, v)|v).collect()
    };
}

Upvotes: 0

Related Questions