Grab first element from an array in a struct

This was hard to explain with a title. Basically I'm converting from a JSON (which is an array of this Struct)

pub struct MiiNames {
    pub player_id: i32,
    pub mii_names: Vec<MiiName>,
}

pub struct MiiName {
    pub mii_name: String,
    pub mkdd: bool,
    pub mkds: bool,
    pub mkw: bool,
    pub mk7: bool,
    pub mk8u: bool,
    pub mk8dx: bool,
}

From this, I filter the array with the Player ID, and this can net to a Vec of length 0 or 1.
I then want to grab all the strings from it, and return Vec

mii_names
        .clone()
        .into_iter()
        .filter(|r| r.player_id == chadsoft_id_data.player_id) // Result can be an empty vector
        .map(|r| {
            r.mii_names
                .into_iter()
                .map(|r| r.mii_name)
                .collect::<Vec<String>>()
        })
        .collect::<Vec<Vec<String>>>()
        .get(0)
        .unwrap_or(&vec![])
        .to_vec()

This is the best solution I could come up with, but it feels over the top.

I figured it out, there's the flatten method.

mii_names
    .clone()
    .into_iter()
    .filter(|r| r.player_id == chadsoft_id_data.player_id)
    .flat_map(|r| r.mii_names.into_iter().map(|r| r.mii_name))
    .collect::<Vec<String>>()

Note that you can skip many allocations by not cloning whole Vec<MiiNames>, but instead using Iterator::cloned adaptor, or just cloning mii_name. For example:

pub fn foo(mii_names: &Vec<MiiNames>) -> Vec<String> {
    mii_names
        .into_iter()
        .filter(|r| r.player_id == 42)
        .map(|r| r.mii_names.iter().map(|r| r.mii_name.clone()))
        .flatten()
        .collect()
}

You can save even more memory, if you use Arc<str> instead of String for representing mii_name. For detailed explanation I recommend Use Arc instead of Vec video by Logan Smith.

2 Likes

I knew of Arc but in the whole codebase I have to use it as Vec due to other reasons, also this whole ordeal is going inside of yet another Struct - though I did not know about .cloned(), thanks!

Edit: I actually cannot clone just the strings themselves nor use Iterator::cloned because this whole thing is within a loop, so mii_names would get moved into without the initial call to clone, though that is beyond the scope of this thread.

You can also use flat_map(..) instead of map(...).flatten(), See Clippy Lints

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.