Java 8: Stream a list and map to another list based on different filters
Solution 1
The only way I see is putting the condition in the map
call. If you use filter
you lose the "else" part.
List<String> myList = source.stream()
.map(item -> {
if (!(item.get(bar) instanceof JSONObject)) {
return item.get(bar).toString();
} else {
return item.getJSONObject(attribute).get("key").toString();
}
})
.collect(Collectors.toList());
or, as Holger suggested in a comment, use the ternary conditional operator:
List<String> myList = source.stream()
.map(i -> (i.get(bar) instanceof JSONObject ? i.getJSONObject(attribute).get("key") : i.get(bar)).toString())
.collect(Collectors.toList());
Solution 2
What about something like this?
List<String> myList = source.stream()
.map(json -> !(json.get(bar) instanceof JSONObject) ?
json.get(bar).toString() :
json.getJSONObject(attribute).get("key").toString())
.collect(Collectors.toList());
Not tested but you get the idea.
Solution 3
You can use the function partitioningBy
that takes a Predicate
and returns Map<Boolean, List<T>>
. The true
key contains the values that are true for the predicate and the false
key contains the other values.
So you can rewrite your code like this :
Map<Boolean, List<String>> partition = source.stream()
.collect(Collectors.partitionBy(json -> !(json.get(bar) instanceof JSONObject));
In this case, no need to use the filter function.
List<String> valuesWhenTrue = partition.get(Boolean.TRUE).stream().map(item -> item.get(attribute).toString()).collect(Collectors.toList());
List<String> valuesWhenFalse = partition.get(Boolean.FALSE).stream().map(json.getJSONObject(attribute).get("key").toString()).collect(Collectors.toList());
Solution 4
How about extract the if-else into a private function
private String obtainAttribute(JSONObject json){
if (!(json.get(bar) instanceof JSONObject)) {
return json.get(bar).toString();
}
return json.getJSONObject(attribute).get("key").toString();
}
and call it in your lambda expression.
List<String> myList = source.stream()
.map(item -> obtainAttribute(item))
.collect(Collectors.toList());
Related videos on Youtube
Comments
-
iamkenos almost 2 years
I have the following code:
public boolean foo(List<JSONObject> source, String bar, String baz) { List<String> myList = newArrayList(); source.forEach(json -> { if (!(json.get(bar) instanceof JSONObject)) { myList.add(json.get(bar).toString()); } else { myList.add(json.getJSONObject(attribute).get("key").toString()); } }); /** * do something with myList and baz */ }
I'm just wondering if there's a way to do the
if-else
condition inline using a filter.Something along the lines of:
List<String> myList = source.stream() .filter(json -> !(json.get(bar) instanceof JSONObject)) .map(item -> item.get(attribute).toString()) .collect(Collectors.toList());
If I go by the approach above, I will miss the supposed to be "else" condition. How can I achieve what I want using a more
java-8
way?Thanks in advance!
-
Lino over 6 yearsyou could move your
if-else
to the map operation, but it would just get unreadable. Java8 is not always the best approach, why not just use the simple old iterative way?
-
-
Lino over 6 yearswhy use streams in the first place, is the question the OP should ask himself
-
Eran over 6 years@Eugene true, but it does remove the explicit creation of the List and adding elements to it.
-
Eugene over 6 years@Eran which makes it worse in a way, there is no way to pass the expected size to
Collectors.toList()
while the OP's approach you could. It's not your answer I'm debating btw, but the approach in general -
Yazan Al-Sanie over 6 years@Eran What about partitioningBy and then mapping results into a list depending on the result of the partitioning ?!
-
Eran over 6 years@AhmadAlsanie I thought about it, but it would require two parts of processing - first a stream pipeline to produce the Map, and then processing the two values of the Map to add values to the List. I'm not sure how elegant that would be.
-
Holger over 6 years@Eugene: if you worry about the list’s initial capacity, you can use
Arrays.asList( … stream op … .toArray(ElementType[]::new) )
to get a list without capacity increase operations (if the stream can predict the size). But this is rarely an issue (or consideration, as you note by the OP’s original code which doesn’t attempt to specify an initial capacity to the list either)…