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);
}
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.
1 Answer 1
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
.
-
\$\begingroup\$ Note you can move the
cloned()
outside, right before thecollect()
. \$\endgroup\$JayDepp– JayDepp2019年03月07日 19:36:17 +00:00Commented Mar 7, 2019 at 19:36 -
\$\begingroup\$ Edited, @JayDepp, as it makes both variants symmetric. \$\endgroup\$Zeta– Zeta2019年03月08日 07:00:41 +00:00Commented Mar 8, 2019 at 7:00
Explore related questions
See similar questions with these tags.