5

How to process a list of string and collec it into Map or Immutable map only for those whose value is present

String anotherParam = "xyz";
Map.Builder<String,String> resultMap = ImmutableMap.builder(..)

 listOfItems.stream()
            .filter(Objects::nonNull)
            .distinct()
            .forEach(
                    item -> {
                        final Optional<String> result =     
    getProcessedItem(item,anotherParam);

                        if (result.isPresent()) {

    resultMap.put(item, result.get());
                        }
                    });
        return resultMap.build();

Please tell, is there a better way to achieve this via collect?

2
  • Both listOfItems() and getProcessedItem(...) can yield null ? Commented Aug 24, 2016 at 6:18
  • list of items can have null String element and return type for getProcessedItem(..) is Optional<String> Commented Aug 24, 2016 at 6:58

1 Answer 1

8

If you have access to Apache Commons library you can make use of Pair.class

Map<String, String> resultMap = ImmutableMap.copyof(listOfItems()
    .stream()
    .filter(Objects::nonNull)
    .distinct()
    .map(it -> Pair.of(it, getProcessedItem(it,anotherParam))
    .filter(pair -> pair.getValue().isPresent())
    .collect(toMap(Pair::getKey, pair -> pair.getValue().get())))

But it's a good practice to make special data classes which describes your mapping item->result more specificly

Here is an example, create class like this:

static class ItemResult(){
    public final String item;
    public final Optional<String> result;

    public ItemResult(String item, Optional<String> result){
        this.item = item;
        this.result = result;
    }

    public boolean isPresent(){
        return this.result.isPresent();
    }

    public String getResult(){
        return result.get();
    }
}

And use it like that:

Map<String, String> resultMap = ImmutableMap.copyOf(listOfItems()
    .stream()
    .filter(Objects::nonNull)
    .distinct()
    .map(it -> new ItemResult(it, getProcessedItem(it,anotherParam))
    .filter(ItemResult::isPresent)
    .collect(toMap(ItemResult::item, ItemResult::getResult)))

You can read here why Google gave up the idea of tuples and pairs and don't use them in most cases

If after all you don't want to use any other class you can leverage api of the Optional:

Map.Builder<String,String> resultMap = ImmutableMap.builder(..)

listOfItems.stream()
        .filter(Objects::nonNull)
        .distinct()
        .forEach(item -> getProcessedItem(item,anotherParam)
                         .ifPresent(result -> resultMap.put(item result));
    return resultMap.build();
Sign up to request clarification or add additional context in comments.

9 Comments

Do you see any issue with my implementation ? Is there away achieve the same without ItemResult and Pair ? Could we also avoid Immutable.copyof ?
As I can see, there are no critical issues with your implementation but you are writing code as it was in a simple for loop and not using abilities of Streams
@sidss I updated the answer and added example of implementation without ItemResult and Pair
great it works.. Please tell is it recommended to use externally declared builder ? Which one would you recommend, the one with collect or this ?
Both approaches produce equal result, so I think it's a matter of taste and your team code style.
|

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.