2
\$\begingroup\$

I have a struct which nests other structs like following:

#[derive(Debug)]
pub struct Rankings {
    conferences: Vec<Conference>,
}

#[derive(Debug)]
pub struct Conference {
    divisions: Vec<Division>,
}

#[derive(Debug)]
pub struct Division {
    teams: Vec<Team>,
}

#[derive(Debug, Clone)]
pub struct Team {
    name: String,
    market: String,
}

What I want to do is converting a Rankings instance to Vec<Team>. Here's my solution:

fn main() {
    let mut rankings = Rankings {
        conferences: vec![
            Conference {
                divisions: vec![
                    Division {
                        teams: vec![
                            Team {
                                name: String::from("Raptors"),
                                market: String::from("Toronto"),
                            },
                            Team {
                                name: String::from("Knicks"),
                                market: String::from("New York"),
                            }
                        ]
                    },
                    Division {
                        teams: vec![
                            Team {
                                name: String::from("Bucks"),
                                market: String::from("Milwaukee"),
                            },
                            Team {
                                name: String::from("Cavaliers"),
                                market: String::from("Cleveland"),
                            }
                        ]
                    },
                ]
            },
        ]
    };

    println!("- rankings:\n{:#?}\n", rankings);

    let mut raw_teams: Vec<Vec<Vec<Team>>> = rankings
        .conferences
        .iter_mut()
        .map(|c| c.divisions.iter_mut().map(|d| d.teams.clone()).collect())
        .collect();

    println!("- raw_teams:\n{:#?}\n", raw_teams);

    let flattened_teams = raw_teams
        .iter_mut()
        .fold(Vec::new(), |mut acc, val| {
            acc.append(val);
            acc
        })
        .iter_mut()
        .fold(Vec::new(), |mut acc, val| {
            acc.append(val);
            acc
        });

    println!("- flattened_teams:\n{:#?}\n", flattened_teams);
}

Playground link

First, I converted Rankings to Vec<Vec<Vec<Team>>> using iter_mut() and map(), then flattened Vec<Vec<Vec<Team>>> to Vec<Team> using iter_mut() and fold().

But I just wrote that code avoiding compile errors, which mean the code could be refactored better using idiomatic patterns. I think I might overuse mutability, and two conversion process can be simplified using appropriate iterator functions. Thanks for any advices.

\$\endgroup\$
2
\$\begingroup\$

First of all, there is no need to use iter_mut() on conferences, as you never change the original rankings. As we clone the teams later, we can simply use

let mut raw_teams: Vec<Vec<Vec<Team>>> = rankings
    .conferences
    .iter()
    .map(|c| c.divisions.iter().map(|d| d.teams.clone()).collect())
    .collect();

Now that we have a Vec<Vec<Vec<Team>>, we can call flatten:

let flattened_teams: Vec<Team> = raw_teams.into_iter().flatten().flatten().collect();

I used into_iter() as your original code left raw_teams empty.

However, we can skip raw_teams entirely if we use flat_map instead of map(…).flatten():

let flattened_teams: Vec<Team> = rankings
    .conferences
    .iter()
    .flat_map(|c| &c.divisions) // need to borrow due to iter()
    .flat_map(|d| &d.teams)     // need to borrow due to iter()
    .cloned()                   // clone the elements
    .collect();

If we don't want to borrow, we can of course just move everything into flattened_teams by simply removing cloned() and &:

let flattened_teams : Vec<Team> = rankings
    .conferences
    .into_iter()
    .flat_map(|c| c.divisions)
    .flat_map(|d| d.teams)
    .collect()

None of these functions use mut.

\$\endgroup\$
  • \$\begingroup\$ Note you can move the cloned() outside, right before the collect(). \$\endgroup\$ – JayDepp Mar 7 at 19:36
  • \$\begingroup\$ Edited, @JayDepp, as it makes both variants symmetric. \$\endgroup\$ – Zeta Mar 8 at 7:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.