2

I have a List of Maps with certain keys that map to String values.

Something like List<Map<String,String>> aMapList;

Objective : Stream over this List of maps and collect values of a single key in all Maps.

How I'm doing this ->

key = "somekey";
aMapList.stream().map(a -> a.get(key)).collect(Collectors.averagingInt());

The Problem: I get exceptions due to a.get(key) if there is no such key! because averaging this will give a null. How do I check or make lambda ignore any such maps and move on.

I do know that I can add a filter on a -> a.contains(key) and then proceed. Edit : I can also add more filters or simple check multiple conditions on one filter. Possible Solution:

    aMapList.stream().filter(a -> a.contains(key)).
         map(a -> a.get(key)).collect(Collectors.averagingInt());

Can this be made prettier? Instead of halting the operation, simply skip over them? Is there some more generic way to skip over exceptions or nulls. For eg. We can expand the lambda and put a try-catch block, but I still need to return something, what if I wish to do an equivalent of "continue".

Eg.

(a -> {return a.get(key) }).

Can be expanded to -->

(a -> {try{return a.get(key)} 
catch(Exception e){return null} }).

The above still returns a null, instead of just skipping over.

I'm selecting the best answer for giving two options, But I do not find any of them prettier. Chaining filters seems to be the solution to this.

9
  • 1
    Why do you get exceptions? Shouldn't you get null? Commented Aug 11, 2016 at 3:21
  • I know one solution. I'm just asking if I can do this in a shorter way. Too many filters look ugly and make it confusing. Commented Aug 11, 2016 at 3:21
  • @cricket_007, because future operations will throw exception. I will get null, but then it will break later and throw the exception. Commented Aug 11, 2016 at 3:24
  • 1
    Then you'll need to filter the null at some point or ignore them in a map. Basically, there doesn't seem to be a "simpler" way than what you have Commented Aug 11, 2016 at 3:27
  • According to @DannyChen, contains(key) returning true does not guarantee that get(key) will return a none-null-value. For some Map implementation you can put null values. Commented Aug 11, 2016 at 3:44

4 Answers 4

2

How about wrapping the result with Optional:

List<Optional<String>> values = aMapList.stream()
            .map(a -> Optional.ofNullable(a.get(key)))
            .collect(Collectors.toList());

Later code will know to expect possible empty elements.


The solution you propose has a potential bug for maps that allow null values. For example:

Map<String, String> aMap = new HashMap<>();
aMap.put("somekey", null);

aMapList.add(aMap);

aMapList.straem()
    .filter(a -> a.contains("somekey")) // true returned for contains
    .map(a -> a.get("somekey")) // null returned for get
    .collect(Collectors.toList());
Sign up to request clarification or add additional context in comments.

1 Comment

I forgot to add that in the filter I can also filter on null values. Sorry about that. I mean, I can filter out any number of things by chaining filters and thus filter out null's too. I was wondering if there was a way of doing something like "try{}, catch{ignore}". Where any such cases are ignored and we move onto the list.
2

Based on the Map documentation, and on your comment under your question, you're not actually getting an exception from a.get(key). Rather, that expression produces a null value, and you're having problems later when you run into these null values. So simply filtering out these null values right away should work just fine:

aMapList.stream()
    .map(a -> a.get(key))
    .filter(v -> v != null)
    .collect(Collectors.toList());

This is prettier, simpler, and performs better than the workaround in your question.

I should mention that I usually prefer the Optional<> type when dealing with null values, but this filtering approach works better in this case since you specifically said you wanted to ignore elements where the key doesn't exist in a map list.

6 Comments

Should be v != null.
@LukeLee v != null also has a potential bug when the map has a key with null value.
Currently I am more worried about not getting the correct result for common cases.
@LukeLee: Right. Derrr. :-) @Danny Chen, I'm assuming the OP has already checked key for a null value outside of this statement.
yes. That will work. My actual lambda is a bit larger than this and I'm doing some more things like count() later. I did not write it here because it served no purpose.
|
0

The simplest I could come up with was:

aMapList.stream()
        .filter(map -> map.containsKey(key))
        .map(map -> map.get(key))
        .collect(Collectors.toList());

By formatting the lambda in this fashion, it is easier to see the distinct steps that the code processes.

2 Comments

I already suggested this as a possible solution. What I'm trying to ask is if there is some way for my lambda to continue working even if there is some exception.
I am proposing formatting that makes the lambda easier to read and understand. I think this lambda (which as you say is basically the same as yours) is the simplest solution.
0

Although I reckon this is not exactly a prettier approach, you could do:

aMapList.stream().map(a -> a.containsKey(key) ? a.get(key) : null).collect(Collectors.toList());

2 Comments

a.get(key) returns null, so checking if it contains seems pointless
besides what cricket said, I would rather add another filter :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.