How can I efficiently extract the key-value pairs from a string into a HashMap when
keyis always followed by:and then the valuevalueends with a,followed by anotherkey(sometimes whitespace and thenkey)valuecan contain, :throughout- no
valuewill include anykey - the order of the
keys are not fixed - the
keynames are known
For these key-value pairs
key1:value1, key2:this is, some value2, key3:anothe:r val,ue,
It should produce this HashMap:
"key1", "value1"
"key2", "this is, some value2"
"key3", "anothe:r val,ue"
I have tried the following code but it is no good with just a , as a delimiter as the value can contain commas throughout.
"key1:value1, key2:this is, some value2, key3:anothe:r val,ue,"
.split(",")
.map(|kv| kv.splitn(2, ":").collect::<Vec<&str>>())
.filter(|vec| vec.len() == 2)
.map(|vec| (vec[0].trim().into(), vec[1].trim().into()))
.collect()
My thought would be to provide a list of keys: ["key1", "key2", "key3"] to use as delimiters
UPDATE:
Using @Lucretiel answer I have come up with:
fn key_value<'a>(keys: &[&str], mut command: &'a str) -> HashMap<&'a str, &'a str> {
let mut hashmap = HashMap::new();
loop {
if let Some(key) = key(&keys, &command) {
command = &command[key.len() + 1..];
let value = value(&keys, &command);
let trim: &[_] = &[',', ' '];
command = &command[value.len()..].trim_start_matches(trim);
hashmap.insert(key, value);
} else {
break;
}
}
hashmap
}
fn key<'a>(keys: &[&str], command: &'a str) -> Option<&'a str> {
let regex = format!("^({}):", keys.join("|"));
let regex = regex::Regex::new(®ex).expect("Invalid regex");
match regex.shortest_match(&command) {
Some(position) => Some(&command[..position - 1]),
None => None,
}
}
fn value<'a>(keys: &[&str], command: &'a str) -> &'a str {
let regex = format!(r#",\s*({}):"#, keys.join("|"));
let regex = regex::Regex::new(®ex).expect("Invalid regex");
match regex.find(&command) {
Some(position) => &command[..position.start()],
None => command,
}
}