简体   繁体   中英

Convert String to two Strings with stream

I have a String as below,

String str = "ID1_RELID1, ID2_RELID2,ID3_RELID3";

I want to convert it into two Strings as below,

String ids = "'ID1','ID2','ID3'";
String relids = "'RELID1','RELID2','RELID3'";

ie spliting each value by _ and joing with comma.

I tried below code,

String str = "ID1_RELID!, ID2_RELID2,ID3_RELID3";
Map<String, String> map = Arrays.stream(str.split(","))
        .collect(toMap(e -> e.split("_")[0], e -> e.split("_")[1]));
String ids = map.keySet().stream().map(e -> "'" + e + "'").collect(joining(","));
String relids = map.values().stream().map(e -> "'" + e + "'").collect(joining(","));

Its working fine but can we do it in min possible iterations ??

This is a bit off-topic answer because according to the question header a "stream" must be used which doesn't hold here. However, this approach is fast and memory friendly because there is no need to reserve memory for temporary arrays or maps.

String str = "ID1_RELID1,ID2_RELID2,ID3_RELID3";
StringTokenizer tok = new StringTokenizer(str, ",_");
StringBuilder[] sbs = new StringBuilder[] {new StringBuilder(),new StringBuilder()};
for(int i = 0; tok.hasMoreTokens(); i++) {
    sbs[i%2].append(i > 1 ? ",'" : "'").append(tok.nextToken()).append("'");
}
String ids = sbs[0].toString();
String relids = sbs[1].toString();

Not sure you really need to stream here. May be a regex and two StringBuilder s?

public static void main(String[] args) {
    String str = "ID1_RELID1, ID2_RELID2,ID3_RELID3";

    StringBuilder left = new StringBuilder();
    StringBuilder right = new StringBuilder();

    Pattern p = Pattern.compile("(\\w+)_(\\w+)(,?)");
    Matcher m = p.matcher(str);
    while (m.find()) {
        left.append("'").append(m.group(1)).append("'").append(",");
        right.append("'").append(m.group(2)).append("'").append(",");
    }

    if (left.length() > 0) {
        left.setLength(left.length() - 1);
        right.setLength(right.length() - 1);
    }

    System.out.println(left);
    System.out.println(right);

}

Here's a mutable reduction that performs the same in one pipeline. You assess it:

List<StringBuilder> stringBuilders = Arrays.stream(str.split(","))
    .map(s -> s.split("_"))
    .collect(
        () -> Arrays.asList(new StringBuilder(), new StringBuilder()),
        (List<StringBuilder> sb, String[] sa) -> {
            sb.get(0).append("'").append(sa[0]).append("',");
            sb.get(1).append("'").append(sa[1]).append("',");
        },
        List::addAll
    );

String ids = stringBuilders.get(0).toString();
String relids = stringBuilders.get(1).toString();

In short, the above uses two StringBuilder s, into which are accumulated the string parts (the first one holds your ids , the second one holds your relids ). The main stream has String[] elements, each holding the two parts of the original string elements, such as ID1_RELID1 => [ID1, RELID1] .

The other simple way is something like this:

first split str with two delimiters.( , & _ )

List<String> list = Arrays.asList(str.split("_|,"));

then join even items of list for ids and odd items of list for relids

String ids = IntStream.rangeClosed(0,list.size()-1)
                       .filter(i->(i & 1) == 0)
                        .mapToObj(i-> String.format("'%s'", list.get(i).trim()))
                       .collect(Collectors.joining(","));

String relids = IntStream.rangeClosed(0,list.size())
                           .filter(i->(i & 1) == 1)
                          .mapToObj(i-> String.format("'%s'", list.get(i).trim()))
                           .collect(Collectors.joining(","));

if you like in one pipeline you can go with:

 Map<Boolean, String> result= new HashMap();
 rangeClosed(0, list.size() - 1)
         .mapToObj(i -> new AbstractMap.SimpleEntry(i, list.get(i)))
         .collect(partitioningBy(entry -> (int) entry.getKey() % 2 == 0,
                 mapping(entry -> String.format("'%s'", entry.getValue().toString().trim()),
                            toList())))
            .forEach((k, v) -> result.put(k, String.join(",", v)));

then get result.get(true) or result.get(false)

however IMO it is so verbose

I'd simply use a regex.

String regex = "(ID[0-9]+)_(RELID[0-9^,])";
String str = "ID1_RELID1,ID2_RELID2,ID3_RELID3";
String ids = str.replaceAll(regex, "'$1'"); // "'ID1','ID2','ID3'"
String relids = str.replaceAll(regex, "'$2'"); // "'RELID1','RELID2','RELID3'"

As you can see, you don't need a stream at all. Regular expression are very powerful for simple string manipulations.

Without using Stream, you can do it as follows:

public class Main {
    public static void main(String[] args) {
        String str = "ID1_RELID1, ID2_RELID2,ID3_RELID3";
        String []substrs =str.split("_|,");

        StringBuilder sbIds=new StringBuilder();
        StringBuilder sbRelids=new StringBuilder();
        for(int i=1;i<=substrs.length;i++)
            if(i%2!=0)
                sbIds.append("'"+substrs[i-1].trim()+"',");
            else
                sbRelids.append("'"+substrs[i-1].trim()+"',");
        String ids=sbIds.toString().substring(0,sbIds.lastIndexOf(","));
        String relids=sbRelids.toString().substring(0,sbRelids.lastIndexOf(","));

        System.out.println(ids);
        System.out.println(relids);
    }
}

Output:

'ID1','ID2','ID3'
'RELID1','RELID2','RELID3'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM