Reputation: 548
I have a string with contents like the following:
key1:value1 key2:value2 key3:value3 ...
I want to end up with a HashMap<&str, &str>
(or equivalent), which maps the keys and values from the string (e.g. a hash lookup of "key1" should return "value1").
Currently, I am able to easily fill up the HashMap with the following:
let mut hm = HashMap::new();
for item in my_string.split_ascii_whitespace() {
let splits = item.split(":").collect::<Vec<&str>>();
hm.insert(splits[0], splits[1]);
}
However, what if I wanted to do it in one line (even at cost of readability, for "code golf"-ish purposes)? I know how to do this with a HashSet
, so I'd imagine it would look somewhat similar; perhaps something like this (which doesn't actually compile):
let hm: HashMap<&str, &str> = HashMap::from_iter(my_string.split_ascii_whitespace().map(|s| s.split(":").take(2).collect::<Vec<&str>>()));
I have tried different combinations of things similar to the above, but I can't seem to find something that will actually compile.
Upvotes: 2
Views: 1968
Reputation: 15135
My approach toward solving this problem was remembering that an Iterator<Item=(K, V)>
can be collected into an HashMap<K, V>
so upon knowing that I worked backwards to try to figure out how I could turn a &str
into an Iterator<Item=(&str, &str)>
which I managed to do using the String find
and split_at
methods:
use std::collections::HashMap;
fn one_liner(string: &str) -> HashMap<&str, &str> {
string.split_whitespace().map(|s| s.split_at(s.find(":").unwrap())).map(|(key, val)| (key, &val[1..])).collect()
}
fn main() {
dbg!(one_liner("key1:value1 key2:value2 key3:value3"));
}
The second map
call was necessary to remove the leading :
character from the value string.
Upvotes: 3