lambdas in removeIf - java

HashSet<Integer> liczby = new HashSet<Integer>();
liczby.add(1);
liczby.add(2);
liczby.add(3);
liczby.add(4);
liczby.removeIf ((Integer any) -> { return liczby.contains(3); });
for(Iterator<Integer> it = liczby.iterator(); it.hasNext();){
Integer l2 = it.next();
System.out.println(l2);
}
I can't understand why removeIf deletes not only 3 but also 1 and 2 condition should be satisfied only by 3...

Think of it this way... as long as the set contains 3 it will keep removing hence the current outcome.
If you want to remove the 3 only then do this:
liczby.removeIf(e -> e == 3);

The lambda is applied on each element and check if 3 is present, if yes it will delete the element :
1 -> 3 is present -> delete 1
2 -> 3 is present -> delete 2
3 -> 3 is present -> delete 3
4 -> 3 is not present -> don't delete 4
To remove all 3 element, you can use one of those solutions :
liczby.removeIf(any -> any.equals(3));
//-------------------------------------------------
liczby.removeIf(new Integer(3)::equals);
//-------------------------------------------------
Integer toRemove = 3;
liczby.removeIf(toRemove::equals);
TIPS
Your lambda can be simplified as :
liczby.removeIf(any -> liczby.contains(3));
For-each loop might be easier to use for simple iteration :
for(Integer i : liczby){
System.out.println(i);
}

Related

Apply map() to only a subset of elements in a Java stream?

Is it possible, with the Java stream API to apply a map() not on the whole stream (a.k.a. not on every element which is streamed), but only on a set of elements which pass a filter? The filter however should not filter out elements. The result should be a stream of the original elements, but on some of them, a map() has been applied.
Pseudocode:
List<Integer>.stream.filterAndMap(x -> if (x>10) {x+2}).toList();
if … else …
Just return the value unchanged if it does not meet your requirement for modification.
List < Integer > integers = List.of( 1 , 7 , 42 );
List < Integer > modified =
integers
.stream()
.map( integer -> {
if ( integer > 10 ) { return integer + 2; }
else { return integer; }
} )
.toList();
modified.toString() = [1, 7, 44]
Ternary operator
Or shorten that code by using a ternary operator as commented by MC Emperor.
List < Integer > integers = List.of( 1 , 7 , 42 );
List < Integer > modified =
integers
.stream()
.map( integer -> ( integer > 10 ) ? ( integer + 2 ) : integer )
.toList();
See how starting with intention-named pseudo-code pays always off.
Naming
The pseudo-instruction filterAndMap(x -> if (x>10) {x+2}) is violating SRP at least by the name containing "And" to clue two responsibilities.
To resolve with Java Streams-API would suggest:
list.stream()
.filter(predicate) // filter means discarding elements from result
.map(mappingFunction) // map applies to filtered elements only
.toList();
But then the resulting list is filtered by the predicate or condition, hence not having original size.
Rephrase intention: Conditionally Map
Search for [java] conditionally map. Some answers show if statements or ternary operator to implement the conditional.
Still the primary step is map. Inside implement the conditional. Whether using a lambda or a function-reference, this decides how to map:
if (predicate.apply(x)) {
return modify(x);
}
return x; // default: unmodified identity
This is Basil's approach .map( integer -> ( integer > 10 ) ? ( integer + 2 ) : integer ).
Why streams?
Assume some of the list elements stay the same, where only some are modified on condition. Like your requirement states:
stream of the original elements, but on some of them, a map() has been applied.
Wouldn't it be clearer then, to use a for-each loop with conditional modification to have the list elements modified in-place:
List<Integer> intList = List.of(1, 7, 42);
// can also be a intList.forEach with Consumer
for (i : intList) {
if (i > 10) {
i = i + 2;
}
});

Java: removeIf sum of grouping of 2 fields equals 0

I have a list of items. Each item has 3 properties: name, section and amount. The amount can be positive/negative. The same item can be several times in the same section.
I want to group the items in the list by section and name. If the sum of this grouping equals 0 - remove all the items with this section and name.
I got this far
items.stream().collect(Collectors.groupingBy(ItemData::getSection))....
Now I need the second grouping and compare it to 0 in order to add it as a predicate to removeIf
items.removeIf(item -> predicate(item));
You can first group the ItemData based on section and name with sum of amount as value
Map<String,Long> result = details.stream()
.collect(Collectors.groupingBy(det -> det.getSection()+"_"+det.getName(),
Collectors.summingLong(det->det.getAmount())));
And then remove the entries from Map having value !=0
result.entrySet().removeIf(det->det.getValue() != 0);
And then remove the elements from items list having entry in Map with key comparison
items.removeIf(item -> result.containsKey(item.getSection()+"_"+item.getName()));
You can also eliminate the entries having value 0 by using stream
Map<String,Long> result = details.stream()
.collect(Collectors.groupingBy(det -> det.getSection()+"_"+det.getName(),
Collectors.summingLong(det->det.getCount())))
.entrySet()
.stream()
.filter(entry->entry.getValue()==0)
.collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue));
Alternatively you could use a BiPredicate which accepts an Item and List<Item>
BiPredicate<Item,List<Item>> biPred =
(item, list) -> list.stream()
.filter(i -> i.getName().equals(item.getName()) &&
i.getSection().equals(item.getSection()))
.mapToLong(Item::getAmount)
.sum() == 0;
myList.removeIf(e -> biPred.test(e, myList));

Java streams list processing order [duplicate]

This question already has an answer here:
Java streams lazy vs fusion vs short-circuiting
(1 answer)
Closed 4 years ago.
Can someone please explain the output of below java streams code:
List<Integer> l = new ArrayList<>();
l.add(0);
l.add(1);
l.add(2);
l.add(3);
l.add(4);
l.add(4);
l.stream()
.distinct()
.map(v -> {
System.out.println("In Map " + v);
return v;
}).forEach(v -> {
System.out.println("In ForEach " + v);
});
I expected:
In Map 0
In Map 1
In Map 2
In Map 3
In Map 4
In ForEach 0
In ForEach 1
In ForEach 2
In ForEach 3
In ForEach 4
But it prints:
In Map 0
In ForEach 0
In Map 1
In ForEach 1
In Map 2
In ForEach 2
In Map 3
In ForEach 3
In Map 4
In ForEach 4
I think it is pretty obvious :) It goes in Map and for each value does the foreach call. It's like nested loops. Get value 1 - process it, get value 2 process it ;) And that's what you get
The Stream pipeline is lazily evaluated. Meaning it performs intermediate operations on Stream elements only when such elements are required by the terminal operation - forEach in your case.
If the terminal operation required only a single element (for example findFirst()), the map() operation would be executed only for the first element of the Stream.

Java8 filter collect both type of value

Is there a way to collect both filtered and not filtered value in java 8 filter ?
One way is:
.filter( foo -> {
if(!foo.apply()){
// add to required collection
}
return foo.apply();
}
Is there a better alternative ?
Map<Boolean, List<Foo>> map =
collection.stream().collect(Collectors.partitioningBy(foo -> foo.isBar());
You can use a ternary operator with map, so that the function you apply is either the identity for some condition, In below example I calculating square of even numbers and keeping odd numbers as it is.
List<Integer> temp = arrays.stream()
.map(i -> i % 2 == 0 ? i*i : i)
.collect(Collectors.toList());
In your case it would be like this :
List<Integer> temp = arrays.stream()
.map(!foo.apply() -> ? doSomething: doSomethingElse)
.collect(Collectors.toList());

Using takeWhileWithIndex to iterate over array of index and then emit an observable

I have a list of number that every number represents an index.
I would like to go through the list one by one and for every number emit an observable.
How can I do that?
I have this snippet, but I having troubles understanding how to continue...
List<Short> seats = Arrays.asList(new Short[]{0,2,3,1});
rx.Observable.from(seats).takeWhileWithIndex((seatIdx, idx) -> {
// Here I would like to emit an Observable
updateGame(seatIdx, idx == 0 ? true : false)
.subscribe(results -> {
System.out.println(results);
});
return !seats.isEmpty();
}).subscribe();
There must be a better way to do this...
If you have any idea...
Thanks!
EDIT
I would like to go one step further and after omitting the loop of 4 iterations (which I don't care about its results), I would like to continue with the chain and concatenate more Observables.
List<Short> seats = Arrays.asList(new Short[]{0,2,3,1});
rx.Observable.range(0, seats.size())
.zipWith(seats, (idx, seatIdx) -> {
return updateGame(seatIdx, idx == 0 ? true : false);
})
.takeLast(1)
.flatMap(x -> x)
.concatWith(calculatePayout(...))
.concatWith(persistGame(...))
.subscribe(results -> {
System.out.println(results);
}, e -> {
}, () -> {
System.out.println("COMPLETED");
});
Notice, I use 'takeLast(1)' in order to avoid calling 'calculatePay' 4 times! - is there more elegant way to do this?
zip with Iterable gives you what you need:
List<Short> seats = Arrays.asList(new Short[]{0,2,3,1});
Observable.range(0, seats.size())
.zipWith(seats,
(idx, seatIdx) ->
updateGame(seatIdx, idx == 0 ? true : false))
.flatMap(x -> x)
.doOnNext(System.out::println)
.subscribe();

Categories