Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use Java streams to get nth element from a list

I have a list of Strings such as "/100" "/100/200". I wish to split these strings by /, and would then get a List of List of Integers such as [[100],[100,200]]. I want to traverse this list of list and get nth element from each list if that list is long enough, else move to the next list.

It is known that each inner list would be maximum of length n.

Example :

n= 3
slashString -> "/100/200/300","/200/300","/100/200/400"

In the above case I want a List of Integers as 300,400.

List<Integer> output = slashString.stream()
        .map(x->Arrays.stream(x.split("/")).collect(Collectors.toList()))
        .filter(x->x.size()==3)

I am able to think till above. How would I eventually collect the 3rd element across all the list of integers.

like image 950
raizsh Avatar asked Jul 08 '18 07:07

raizsh


4 Answers

You are almost there.

After filtering list of size 3, get the third element and convert it to an Integer.

Also, note that splitting a String /100/200 will give you a String[] (["", "100", "200"]) with an empty string as the first element. So, I have skipped the first element using skip(1)

List<Integer> result = slashString.stream()
            .map(x-> Arrays.stream(x.split("/"))
                    .skip(1)
                    .collect(Collectors.toList()))
            .filter(x -> x.size() >= 3)
            .map(list -> Integer.valueOf(list.get(2)))
            .collect(Collectors.toList());
like image 154
user7 Avatar answered Sep 19 '22 11:09

user7


Just map each List to the 3rd element of the List and collect:

List<Integer> list = Stream.of ("100/200/300","200/300","100/200/400")
    .map(x->Arrays.stream(x.split("/")).collect(Collectors.toList()))
    .filter(x->x.size()==3)
    .map(l->Integer.valueOf (l.get(2)))
    .collect(Collectors.toList());

Note that you have to eliminate the leading / of the input Strings. Otherwise the lengths of the 1st and 3rd Lists will be 4, and they won't pass the filter. Or you can require the List size to be 4 instead of 3 (and change l.get(2) to l.get(3)).

like image 39
Eran Avatar answered Sep 20 '22 11:09

Eran


Remove all but the third term using regex, filter out empties, voila!

List<Integer> list = Stream.of("100/200/300", "200/300", "100/200/400")
    .map(s -> s.replaceAll("^([^/]/[^/]/)?([^/]+)?(/.*)?", "$2"))
    .filter(s -> !s.isEmpty())
    .map(Integer::valueOf)
    .collect(Collectors.toList());

The regex always matches the whole string, and replaces it with the 3rd term, which was captured as group 2, but because everything is optional, group 2 (the final result) is blank if there isn’t a 3rd term.

This approach only ever deals with Strings, which keeps things simpler by avoiding ugly array code.

like image 42
Bohemian Avatar answered Sep 20 '22 11:09

Bohemian


You do not need to make intermediate lists. Instead turn each string into a stream that's either empty or contains just the nth element with skip(n).limit(1) and use flatMap to merge all the little streams together:

Pattern delimiter = Pattern.compile("/");
int n = 3;

List<Integer> result = slashString.stream()
        .flatMap(s -> delimiter.splitAsStream(s).skip(n).limit(1))
        .map(Integer::valueOf)
        .collect(toList());
like image 37
Misha Avatar answered Sep 18 '22 11:09

Misha