Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert regex Captures into HashMap in Rust?

I have a Regex with an unknown number of named groups with unknown names. I want to match a string to that regex, and get a HashMap<&str, &str> with the name of the groups as key and the captured strings as value.

How can I do this? Will I have to use regex.captures(str).iter() and then somehow map and filter and collect into a map? Or is there some shortcut?

like image 675
Anders Avatar asked Jan 01 '23 09:01

Anders


1 Answers

It is tricky because the regex can have multiple matches, and each capture can be matched multiple times in a single global match.

Maybe something like this (playground):

fn main() {
    let re = Regex::new(r"(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})").unwrap();
    let text = "2012-03-14";
    let caps = re.captures(text).unwrap();
    let dict: HashMap<&str, &str> = re
        .capture_names()
        .flatten()
        .filter_map(|n| Some((n, caps.name(n)?.as_str())))
        .collect();
    println!("{:#?}", dict);
}

That outputs:

{
    "y": "2012",
    "d": "14",
    "m": "03"
}

The code is simple once you realize that the capture names are not available from the Match itself, but from the parent Regex. You have to do the following:

  1. Call capture_names(), that will be an iterable of Option<&str>.
  2. flatten() the iterable, that will remove the None and unwrap the &str values.
  3. filter_map() the capture names into a list of tuples (name, value) of type (&str, &str). The filter is needed to remove captures that are not present (thanks to @Anders).
  4. collect()! This just works because HashMap<K, V> implements the trait FromIterator<(K, V)>, so an iterator of (&str, &str) collects into a HasMap<&str, &str>.
like image 156
rodrigo Avatar answered Jan 05 '23 19:01

rodrigo