Sridhar Patnaik
Sridhar Patnaik

Reputation: 1118

Java 8 Streams - Handle Nulls inside Collectors.groupingBy

I have a list of objects in variable "data". I want to filter them (on ActivityType='Deal_Lost') and then group them using one of the field (DealLostReason) and store in a map with DealLostReason and respective count.

Below method works perfectly fine when there are no NULL values returned by c.getDealLostReason(). However, if there is a null value in any of the record, it just returns null instead of grouped values.

Map<Object, Long> collect = data.stream().
    filter(c -> c.getActivityType().equals(ActivityTypeEnum.Deal_Lost))
    .collect(Collectors.groupingBy(c -> { 
        return c.getDealLostReason(); }, Collectors.counting()))));

Can anyone help me on how to handle null values inside lambda expression. I want the null values to be calculated as "Other". i.e. if any record has null, it should be calculated as "OTHER". Below is the SQL query I am trying to achieve.

USE egcity;
SELECT 
    CASE WHEN dealLostReason IS NULL THEN 'Other' ELSE dealLostReason END,
    COUNT(*) from LeadActivity
WHERE 
        activity_date_time>='2021-04-01' AND activity_date_time<='2021-05-01'
AND activityType = 'Deal_Lost'
GROUP BY dealLostReason;

enter image description here

Upvotes: 2

Views: 3813

Answers (2)

Joost Papendorp
Joost Papendorp

Reputation: 95

nulls should be avoided at the source. That said, handling nulls mid-stream can be done using Optionals. Here's a dirty little gist to get you started.

    Map<Object, Long> collect = data.stream()
            .map( x -> Optional.of( x ).orElseGet( () -> Something.defaultSomething() ) )
            .filter( c -> c.getActivityType().equals(ActivityTypeEnum.Deal_Lost))
            .collect( Collectors.groupingBy( c ->
            {
                return c.getDealLostReason();
            }, Collectors.counting()));

with the defaultSomething some implementation of the type representing the absent value.

Even better would be to postpone the optional-unwrapping to the very last moment; in this case the query construction: i.e. Don't need to know, don't wanna know.

Upvotes: -1

Ryuzaki L
Ryuzaki L

Reputation: 40078

You can use ternary operator to map null having objects counts to OTHER

Map<Object, Long> collect = data.stream()
           .filter(c->c.getActivityType().equals(ActivityTypeEnum.Deal_Lost))
           .collect(Collectors.groupingBy(c -> c.getDealLostReason() == null ? "OTHER" : c.getDealLostReason(),Collectors.counting())));
                                      

Upvotes: 2

Related Questions