user1639485
user1639485

Reputation: 808

Get a particular entry and its index from a Map using Java lambda expression

I have a Map<String, Boolean> with the following entries:

{‘John’:false,
 'Mike':false,
 'Tom':true,
 'Harry':false,
 'Bob': false}

I need to find the first entry and its index which has the value true. Here I should get Tom and 3 (say, index starts with 1).

I can iterate the map and get these values:

int itr=0;
for(Iterator<Map.Entry<String, Boolean>> entries = map.entrySet().iterator; entries.hasNext(); ) {
    itr++;
    Map.Entry<String, Boolean> entry = entries.next();
    if(entry.getValue()) {
       name = entry.getKey();
       index = itr;
    }
}

However, I am looking at a lambda expression for the same.

Upvotes: 1

Views: 1568

Answers (2)

Tagir Valeev
Tagir Valeev

Reputation: 100199

I think it's not possible to fulfill all the conditions below:

  1. Solution should be lazy (stop iterating map once the answer is found)
  2. Solution should not use explicitly iterator() or spliterator()
  3. Solution should conform the Stream API spec (in particular intermediate lambdas should not have side effects)
  4. Solution should not use third-party Stream extensions.

If you ok with violating #1, check the @Eugene's answer. If you ok with violating #2, then your code in the question is nice. If you ok with violating #3, you can do something like this:

AtomicInteger idx = new AtomicInteger();

String name = map.entrySet().stream()
   .peek(e -> idx.incrementAndGet())
   .filter(Map.Entry::getValue)
   .map(Map.Entry::getKey)
   .findFirst().orElse(null);
int itr = idx.get();

If you're ok to violate #4, you may consider using my free StreamEx library:

Map.Entry<String, Integer> entry = StreamEx.of(map.entrySet()) // (name, bool)
       .zipWith(IntStreamEx.ints().boxed()) // ((name, bool), index)
       .filterKeys(Map.Entry::getValue) // filter by bool
       .mapKeys(Map.Entry::getKey) // (name, index)
       .findFirst()
       .orElse(null);
if(entry != null) {
    String name = entry.getKey();
    int itr = entry.getValue();
}

Upvotes: 3

Eugene
Eugene

Reputation: 120848

Well if you can guarantee that Map is actually a LinkedHashMap (so that it preserves insertion order), you can do something like this:

List<Map.Entry<String, Boolean>> l = map.entrySet().stream().collect(Collectors.toList());
    IntStream.range(0, l.size())
            .mapToObj(i -> new AbstractMap.SimpleEntry<>(i, l.get(i)))
            .filter(e -> e.getValue().getValue())
            .map(e -> new AbstractMap.SimpleEntry<>(e.getValue().getKey(), e.getKey()))
            .findFirst();

Upvotes: 3

Related Questions