multithr3at3d
multithr3at3d

Reputation: 548

Convert key value pair string to HashMap in one line

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

Answers (1)

pretzelhammer
pretzelhammer

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"));
}

playground

The second map call was necessary to remove the leading : character from the value string.

Upvotes: 3

Related Questions