user7519940
user7519940

Reputation: 23

How can I filter a list inside a for?

I recently had this problem. This is my code

for(int i=1; i<=repeticiones;i++){
    posiblesComunes.removeIf(p->!periodos.get(i).contains(p));
}

periodos is a List(Set(String)), posiblesComunes is a Set(String)

What I need to do is get only the strings that are in all of the Set(String) in periodos. I tried to do that using a for, but I get the next message:

Local variable i defined in an enclosing scope must be final or effectively final

Is there any way to fix that? Or another way to get those elements? Thanks!

edit: Just an example

periodos = {("1-1-16","6-12-16"),("1-1-16","2-8-15"),("3-7-08","1-1-16")}

What I need to get is "1-1-16", the one in common.

edit2:

an example of periodosComunes (for the for loop):

periodosComunes = ("1-1-16","6-2-16")

Upvotes: 0

Views: 193

Answers (4)

Abhijit Sarkar
Abhijit Sarkar

Reputation: 24528

How about this:

Set<String> periodosComunes = Set.of("1-1-16","6-2-16");
List<Set<String>> periodos = List.of(
  Set.of("1-1-16","6-12-16"),
  Set.of("1-1-16","2-8-15"),
  Set.of("3-7-08","1-1-16")
);

List<String> result = periodosComunes.stream()
  .filter(x -> periodos.stream()
      .allMatch(y -> y.contains(x))
  )
  .collect(Collectors.toList());

// result = [1-1-16]

I used collection literals from Java 9 to save me some typing, but that's irrelevant to the solution.

Upvotes: 2

Jean-Baptiste Yun&#232;s
Jean-Baptiste Yun&#232;s

Reputation: 36401

That should work (if I understand well your needs):

import java.util.stream.*;
import java.util.*;
public class P {

  public static void main(String []a) {
    // Init.
    Set<String> periodosComunes = new HashSet<>(); periodosComunes.add("1-1-16"); periodosComunes.add("6-2-16");
    Set<String> s1 = new HashSet<>(); s1.add("1-1-16"); s1.add("6-12-16");
    Set<String> s2 = new HashSet<>(); s2.add("1-1-16"); s2.add("2-8-15");
    Set<String> s3 = new HashSet<>(); s3.add("1-1-16"); s3.add("3-7-08");
    List<Set<String>> periodos = new ArrayList<>(); periodos.add(s1); periodos.add(s2); periodos.add(s3);

    // Computes the set of commons...
    Set<String> r =  periodosComunes.stream().filter(p->periodos.stream().allMatch(s->s.contains(p))).collect(Collectors.toSet());
    System.out.println(r);                                                   
  }
}

The set of common periods initially in periodosCommunes is in r. Now you can use that set to remove appropriate ones in the original set if needed:

periodosComunes.removeIf(s->!r.contains(s));

Upvotes: 0

Holger
Holger

Reputation: 298153

You can not access the local variable i from a lambda expression, because it gets modified during the loop. The simplest fix is to capture the current value of i in another immutable variable:

for(int i=1; i<=repeticiones;i++) {
    int finalI = i;
    posiblesComunes.removeIf(p -> !periodos.get(finalI).contains(p));
}

Note that the variable of a for-each loop does not have this problem:

for(Set<String> set: periodos.subList(1, repeticiones))
    posiblesComunes.removeIf(p -> !set.contains(p));

but in the end, you are overusing Java 8 features here. This operation can be done with the original Collections API since Java 2:

for(Set<String> set: periodos.subList(1, repeticiones))
    posiblesComunes.retainAll(set);

which would also work with your original loop:

for(int i=1; i<=repeticiones; i++)
    posiblesComunes.retainAll(periodos.get(i));

Here, you could also add a short-cut, as the set never grows, so if there are no common elements, you can stop once the set became empty

for(int i=1; i<=repeticiones && !posiblesComunes.isEmpty(); i++)
    posiblesComunes.retainAll(periodos.get(i));

Upvotes: 3

Edwin Buck
Edwin Buck

Reputation: 70909

Not all lists provide iterators with the ability to remove their items; however, if you select the right kind of list, this is built into the Iterator interface.

Iterator i = list.iterator();
while (i.hasNext()) {
    if (i.next().equals(bad)) {
        i.remove();
    }
}

The simplicity of the solution is enough you might consider skipping the Stream based approach, and unlike some kinds of modifications, Iterator removal will not throw a ConcurrentModificationException.

Upvotes: 1

Related Questions