Home > Back-end >  Is there a way to filter eligible substrings from an ArrayList of Strings using Java Streams?
Is there a way to filter eligible substrings from an ArrayList of Strings using Java Streams?

Time:03-22

Is there a way to achieve this using Java Streams?

List<String> list = new ArrayList<>();
list.add("x=aa,y=bb,y=cc");
list.add("y=dd,z=ee,w=ff");
list.add("w=gg,w=hh,w=ii");

and I want to filter above ArrayList to contain only string values of type 'y'. Like below

list=["bb","cc","dd"]

I am able to achieve this using multiple lines of code using String.split(",") and inserting eligible substring to a new ArrayList. But is there a clean way to achieve this using Java Streams?

CodePudding user response:

You asked:

is there a clean way to achieve this using Java Streams?

Yes. Indeed, you can process your inputs, filter out the unwanted parts, and produce a finished list, all using streams.

First define some example data, a list of inputs.

Generate a stream from that list. This stream has 3 elements, given our example data.

Call Stream#flatMap to return a new stream whose elements are made from pieces of the original stream’s elements. The new stream has 9 elements, produced by breaking each of the 3 original elements into 3 parts each (3 * 3 = 9).

Filter the newer longer stream for elements whose text starts with our targeted y= prefix.

Transform each of the string elements that passed our filter’s predicate test. Use simple string manipulation to replace "y=" with an empty string. So a value such as y=bb becomes bb.

Collect transformed strings by gathering into a new List object.

List < String > inputs =
        List.of(
                "x=aa,y=bb,y=cc" ,
                "y=dd,z=ee,w=ff" ,
                "w=gg,w=hh,w=ii"
        );
List < String > results =
        inputs
                .stream()                                                 // Generate stream whose elements are each of the list’s elements.
                .flatMap( input -> Arrays.stream( input.split( "," ) ) )  // Break each element into parts, feeding each part into a new longer stream.
                .filter( pairing -> pairing.startsWith( "y=" ) )          // Filter out any strings not beginning with `y=`. 
                .map( pairing -> pairing.replace( "y=" , "" ) )           // A value such as `y=bb` becomes `bb`.
                .toList();                                                // Collect transformed strings into a new list.

When run.

results.toString() = [bb, cc, dd]

To put results in sorted order, add a call to .sorted() before the toList().

CodePudding user response:

Here is one way.

List<String> list = new ArrayList<>();
list.add("x=aa,y=bb,y=cc");
list.add("y=dd,z=ee,w=ff");
list.add("w=gg,w=hh,w=ii");
List<String> result = getListOf(list,"y");
System.out.println(result);

prints

[bb, cc, dd]
  • stream the strings and split on ","
  • then split those on "=" which returns arrays like arr = [x,aa]
  • then filter on arr[0] and return arr[1] if it matches.
  • and return the resultant list.
public static List<String> getListOf(List<String> items, String key) {
    return items.stream()
            .flatMap(str -> Arrays.stream(str.split(",")))
            .map(str -> str.split("="))
            .filter(s -> s[0].equals(key))
            .map(s -> s[1])
            .toList();
    
}

You can avoid flatMapping by using mapMulti (Java 16) Note: stole idea of startsWith from Basil Bourque's answer.

public static List<String> getListOf(List<String> items, String key) {
     final String kk = key "="; 
     return items.stream().<String>mapMulti((str,putOnStream)-> {
            for (String s : str.split(",")) {
                  if (s.startsWith(kk)) {    
                      putOnStream.accept(s.substring(kk.length()));
                  }
             }})
             .toList();
}
  • Related