How to Break Out of a Map/Collect and Return Whatever Has Been Collected Up to That Point

How do I break out of a map/collect and return whatever has been collected up to that point?

Instead of using map directly, build up your own collection and then use the fact that break returns a value to abort early:

result = 
[0, 1, 2, 1, 0].each_with_object([]) { |val, accumulator|
if val < 2
accumulator << val
else
break accumulator
end
}
result # => [0, 1]

If we did just break (instead of break accumulator) then nil would be implicitly returned and result would just be set to nil.

This solution has the advantage of only allocating a single accumulator Array and only having to loop once.

How to break out or exit a method in Java?

Use the return keyword to exit from a method.

public void someMethod() {
//... a bunch of code ...
if (someCondition()) {
return;
}
//... otherwise do the following...
}

From the Java Tutorial that I linked to above:

Any method declared void doesn't return a value. It does not need to contain a return statement, but it may do so. In such a case, a return statement can be used to branch out of a control flow block and exit the method and is simply used like this:

return;

Break or return from Java 8 stream forEach?

If you need this, you shouldn't use forEach, but one of the other methods available on streams; which one, depends on what your goal is.

For example, if the goal of this loop is to find the first element which matches some predicate:

Optional<SomeObject> result =
someObjects.stream().filter(obj -> some_condition_met).findFirst();

(Note: This will not iterate the whole collection, because streams are lazily evaluated - it will stop at the first object that matches the condition).

If you just want to know if there's an element in the collection for which the condition is true, you could use anyMatch:

boolean result = someObjects.stream().anyMatch(obj -> some_condition_met);

Ignore duplicates when producing map using streams

This is possible using the mergeFunction parameter of Collectors.toMap(keyMapper, valueMapper, mergeFunction):

Map<String, String> phoneBook = 
people.stream()
.collect(Collectors.toMap(
Person::getName,
Person::getAddress,
(address1, address2) -> {
System.out.println("duplicate key found!");
return address1;
}
));

mergeFunction is a function that operates on two values associated with the same key. adress1 corresponds to the first address that was encountered when collecting elements and adress2 corresponds to the second address encountered: this lambda just tells to keep the first address and ignores the second.

Rendering an array.map() in React

Gosha Arinich is right, you should return your <li> element.
But, nevertheless, you should get nasty red warning in the browser console in this case

Each child in an array or iterator should have a unique "key" prop.

so, you need to add "key" to your list:

this.state.data.map(function(item, i){
console.log('test');
return <li key={i}>Test</li>
})

or drop the console.log() and do a beautiful oneliner, using es6 arrow functions:

this.state.data.map((item,i) => <li key={i}>Test</li>)

IMPORTANT UPDATE:

The answer above is solving the current problem, but as Sergey mentioned in the comments: using the key depending on the map index is BAD if you want to do some filtering and sorting. In that case use the item.id if id already there, or just generate unique ids for it.

How to remove null or empty list from an unmodifiable map

Collection#removeIf might be a good option to consider.

private Map<String, Object> getFinalMap(final Map<String, Object> inputMap) {
final Map<String, Object> resultMap = new HashMap<>(inputMap);
resultMap.entrySet().removeIf(e -> e.getValue() instanceof Collection && ((Collection) e.getValue()).isEmpty());

return resultMap;
}

If a stream-based approach is more appealing to you

private Map<String, Object> getFinalMap(final Map<String, Object> inputMap) {
return inputMap.entrySet()
.stream()
.filter(e -> !(e.getValue() instanceof Collection && ((Collection) e.getValue()).isEmpty()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

UPDATE

If you want to filter out null objects regardless of their type, here are slight changes to the methods mentioned above

1) removeIf

resultMap.entrySet()
.removeIf(e -> Objects.isNull(e.getValue()) ||
(e.getValue() instanceof Collection && ((Collection) e.getValue()).isEmpty());

2) stream-based

.filter(e -> Objects.nonNull(e.getValue()))
.filter(e -> !(e.getValue() instanceof Collection && ((Collection) e.getValue()).isEmpty()))

Cannot make filter-forEach-collect in one stream?

The forEach is designed to be a terminal operation and yes - you can't do anything after you call it.

The idiomatic way would be to apply a transformation first and then collect() everything to the desired data structure.

The transformation can be performed using map which is designed for non-mutating operations.

If you are performing a non-mutating operation:

 items.stream()
.filter(s -> s.contains("B"))
.map(s -> s.withState("ok"))
.collect(Collectors.toList());

where withState is a method that returns a copy of the original object including the provided change.


If you are performing a side effect:

items.stream()
.filter(s -> s.contains("B"))
.collect(Collectors.toList());

items.forEach(s -> s.setState("ok"))

How to skip over an element in .map()?

Just .filter() it first:

var sources = images.filter(function(img) {
if (img.src.split('.').pop() === "json") {
return false; // skip
}
return true;
}).map(function(img) { return img.src; });

If you don't want to do that, which is not unreasonable since it has some cost, you can use the more general .reduce(). You can generally express .map() in terms of .reduce:

someArray.map(function(element) {
return transform(element);
});

can be written as

someArray.reduce(function(result, element) {
result.push(transform(element));
return result;
}, []);

So if you need to skip elements, you can do that easily with .reduce():

var sources = images.reduce(function(result, img) {
if (img.src.split('.').pop() !== "json") {
result.push(img.src);
}
return result;
}, []);

In that version, the code in the .filter() from the first sample is part of the .reduce() callback. The image source is only pushed onto the result array in the case where the filter operation would have kept it.

update — This question gets a lot of attention, and I'd like to add the following clarifying remark. The purpose of .map(), as a concept, is to do exactly what "map" means: transform a list of values into another list of values according to certain rules. Just as a paper map of some country would seem weird if a couple of cities were completely missing, a mapping from one list to another only really makes sense when there's a 1 to 1 set of result values.

I'm not saying that it doesn't make sense to create a new list from an old list with some values excluded. I'm just trying to make clear that .map() has a single simple intention, which is to create a new array of the same length as an old array, only with values formed by a transformation of the old values.



Related Topics



Leave a reply



Submit