Java: how to extract and populate values to a Map from a String based on a provided pattern?

问题: I have 2 Strings, one is pattern that contains parameters and the other is title. I would like to extract parameter values from the title and store them to a Map based o...

问题:

I have 2 Strings, one is pattern that contains parameters and the other is title.

I would like to extract parameter values from the title and store them to a Map based on the provided pattern. The parameters in the pattern are prefixed with a $.

Example 1:

pattern = "home/$service/$source-$metadataId"
title = "home/serviceA/test-ABC"

then the output should be a map that has all the following key value pairs:

service = serviceA
source = test
metadataId = ABC

Example 2:

pattern = "home/$service/$source/$region/$year/$month/$day-$metadataId"
title = "home/serviceA/test/NA/2019/3/3-ABC"

then the output should be a map that has all the following key value pairs:

service = serviceA
source = test
region = NA
year = 2019
month = 3
day = 3
metadataId = ABC

Please let me know whether there is any library that can do this in Java or how would you achieve it in plain Java.

Note:

  1. The parameter names don't contain any special characters. (eg: punctuations)
  2. All the parameter names start with a $

回答1:

No library that I know of. This problem is very specific to certain cases. But you can write your own library that handles more cases like that. Here is a little program in Java that will work for all the cases you described (can be further extended). Hopefully it gives you some idea.

    String pattern = "home/$service/aaa/$source-$metadataId";
    String title = "home/serviceA/aaa/test-ABC";

    String patternNew = pattern.replaceAll("/\$|-\$", "/");

    // assuming both the strings contain same number of tokens.
    String[] keyTokens = (patternNew).split("/|-");
    String[] valueTokens = (title).split("/|-");

    Map<String, String> map = new HashMap<String, String>();

    for (int n = 1; n < keyTokens.length; n++) {
        String key = (keyTokens[n]);
        String value = (valueTokens[n]);

        if(key.equals(value))
            continue;

        map.put(key, value);
    }

    for (String name : map.keySet()) {
        System.out.print(name);
        System.out.print(" = " + map.get(name));
        System.out.println();
    }

回答2:

String[] p = pattern.split("/\$|-");
String[] t = titlesplit("/|-");

 Map<String, String> map = new HashMap<>();
 for(int i=1; i<t.length; i++){
       map.put(p[i], t[i]);
  }

回答3:

  1. Use regular expression similar to $(w+) to split pattern into literal chunks and placeholder names. For the first example you will get the following literal chunks: "home/", "/", "-", "" (last chunk is empty string), and the following placeholder names: "service", "source", "metadataId".
  2. Now you construct regular expression from literal chunks like this: home/(.*)/(.*)-(.*). Do not forget to properly quote literal chunks.
  3. Apply this regular expression and get values of captured groups: "serviceA", "test", "ABC".
  4. Combine into map placeholder names collected at step 1 with capturing groups values obtained at step 3.

回答4:

import java.util.*;
import java.io.*;
public class Solution {
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String pattern = br.readLine();
        String title = br.readLine();
        System.out.println(parseString(pattern,title).toString());
    }   

    private static Map<String,String> parseString(String pattern,String title){
        Map<String,String> map = new HashMap<>();

        String[] pat_tokens = pattern.split("/");
        String[] title_tokens = title.split("/");;

        for(int i=0;i<pat_tokens.length;++i){
            String[] sub_tokens = pat_tokens[i].split("\-");
            String[] title_sub_tokens = title_tokens[i].split("\-");
            for(int j=0;j<sub_tokens.length;++j){
                if(sub_tokens[j].charAt(0) != '$') continue;
                map.put(sub_tokens[j],title_sub_tokens[j]);
            }
        }       

        return map;
    }
}

Well, you can just split the string based on / and on - for individual tokens. if the starting character of a string is not $, then it isn't a key you would like to have it in your map.


回答5:

Assuming that the parameter values (not just names) can only contain word characters, you can do this:

String pattern = "home/$service/$source/$region/$year/$month/$day-$metadataId";
String title = "home/serviceA/test/NA/2019/3/3-ABC";
String regex = "\Q" + pattern.replaceAll("\$(\w+)", "\\E(?<$1>\\w+)\\Q") + "\E";
Matcher m = Pattern.compile(regex).matcher(title);
if (m.find()) {
    Map<String, String> map = getNamedGroupCandidates(regex).stream().collect(Collectors.toMap(Function.identity(), m::group));
    System.out.println(map);
}

where getNamedGroupCandidates is from this post:

private static Set<String> getNamedGroupCandidates(String regex) {
    Set<String> namedGroups = new TreeSet<>();

    Matcher m = Pattern.compile("\(\?<([a-zA-Z][a-zA-Z0-9]*)>").matcher(regex);

    while (m.find()) {
        namedGroups.add(m.group(1));
    }

    return namedGroups;
}

I basically convert your "pattern" into a regex with named groups. I then get all the group names and use them to get the captured values. And finally I put all these into a single map.

  • 发表于 2019-03-21 04:09
  • 阅读 ( 117 )
  • 分类:sof

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除