skumar
skumar

Reputation: 1015

Java8 Stream - use filter collect foreach in single line

Is't possible to use filter(),collect() and foreach() in single statement instead of multiple statements?

I have a map and need to filter based on some condition and set some values to the content and return the map. My current looks like below, but i want all 3 in single statement.

Map inputMap (contains all info)

Map<String, Person> returnMap; 
            returnMap = map.entrySet().stream()
            .filter(p ->  p.getValue().getCourse() == 123)
            .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));

            returnMap.entrySet().stream().forEach((entry) -> {
                Person person= entry.getValue();                
                person.setAction("update");
                person.setLastUpdatedTime(new Date());
            });

can this be converted to,

  Map<String, Person> returnMap; 
                returnMap = map.entrySet().stream()
                .filter(p ->  p.getValue().getCourse() == 123)
                .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue())) 
 .forEach((entry) -> {
                    Person person= entry.getValue();                
                    person.setAction("update");
                    person.setLastUpdatedTime(new Date());
                });

(this code doesn't work)

Upvotes: 14

Views: 85560

Answers (3)

Hank D
Hank D

Reputation: 6481

I don't understand the need to separate it into two steps as has been suggested. It seems like the transformation could be done before the terminal operation, like this:

returnMap = map.entrySet().stream()
                .filter(p -> p.getValue().getCourse() == 123)
                .map(e -> {
                    Person p = e.getValue();
                    p.setAction("update");
                    p.setLastUpdatedTime(new Date());
                    return e;
                })
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

If you're squeamish about side effects to the Map.Entry being streamed, you could return a new AbstractMap.SimpleEntry with the updated Person as its value, but none of the solutions so far is free of side-effects, since the values in the original Map are being modified. Regardless, here is the variation:

returnMap = map.entrySet().stream()
                .filter(p -> p.getValue().getCourse() == 123)
                .map(e -> {
                    Person p = e.getValue();
                    p.setAction("update");
                    p.setLastUpdatedTime(new Date());
                    return new AbstractMap.SimpleEntry<>(e.getKey(), p);
                })
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Upvotes: 2

Holger
Holger

Reputation: 298459

There is no sense in insisting on doing it with one operation. Regardless of how you write it down, these are two operations.

But one thing you should consider, is, that there are more ways than entrySet().stream() to process all elements:

Map<String, Person> returnMap = map.entrySet().stream()
    .filter(p ->  p.getValue().getCourse() == 123)
    .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));

returnMap.values().forEach(person -> {          
    person.setAction("update");
    person.setLastUpdatedTime(new Date());
});

If you still insist on making it look like a single operation, you can do it this way:

Map<String, Person> returnMap = map.entrySet().stream()
    .filter(p ->  p.getValue().getCourse() == 123)
    .collect(Collectors.collectingAndThen(
        Collectors.toMap(p -> p.getKey(), p -> p.getValue()),
        tmp -> {
            tmp.values().forEach(person -> {          
                person.setAction("update");
                person.setLastUpdatedTime(new Date());
            });
            return tmp;
        })
    );

This is syntactically a single statement, still, it does exactly the same as the former variant.

Upvotes: 11

wake-0
wake-0

Reputation: 3966

The problem is that forEach does not return an object so you have to handle it different. You can do it this way:

Map<String, Person> returnMap = new HashMap<>(); 
map.entrySet().stream()
              .filter(p ->  p.getValue().getCourse() == 123)
              .forEach((entry) -> {
                    Person person = entry.getValue();                
                    person.setAction("update");
                    person.setLastUpdatedTime(new Date());
                    returnMap.put(entry.getKey(), person);
                });

Upvotes: 25

Related Questions