Reputation: 7685
I found no solution yet for reducing a collection of one type (e.g. MyData
) to one object of another type (e.g. MyResult
) using Java streams.
@Test
public void streams() {
List<MyData> collection = Arrays.asList(
new MyData("1", "cool"),
new MyData("2", "green"),
new MyData("3", "night"));
// How reduce the collection with streams?
MyResult result = new MyResult();
collection.stream().forEach((e) -> {
if (e.key.equals("2")) {
result.color = e.value;
}
});
MyResult expectedResult = new MyResult();
expectedResult.color = "green";
assertThat(result).isEqualTo(expectedResult);
}
public static class MyData {
public String key;
public String value;
public MyData(String key, String value) {
this.key = key;
this.value = value;
}
}
public static class MyResult {
public String color;
public String material;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MyResult myResult = (MyResult) o;
return Objects.equals(this.color, myResult.color) &&
Objects.equals(this.material, myResult.material);
}
@Override
public int hashCode() {
return Objects.hash(this.color, this.material);
}
}
Is there a solution using some kind of reduce or fold?
Upvotes: 5
Views: 15533
Reputation: 59950
Did you mean :
collection.stream()
.filter(e -> e.key.equals("2"))
.findFirst()
.orElse(null);//Or any default value
You can even throw an exception :
collection.stream()
.filter(e -> e.key.equals("2"))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No data found"));
Upvotes: 13
Reputation: 7685
I found a solution using Collector.of()
.
Collector<MyData, MyResult, MyResult> myCollector = Collector.of(
() -> new MyResult(),
(a, e) -> {
if (e.key.equals("2")) {
a.color = e.value;
}
},
(a1, a2) -> null, // Not used, therefore not working scaffold
a -> a
);
MyResult result = collection.stream().collect(myCollector);
After all it doesn't feel idiomatically.
Upvotes: 1
Reputation: 196
Are you trying to convert a list of some type to another based on a condition ?
MyResult result = new MyResult();
List<MyResult> resultList = collection.stream().filter(e -> e.key.equals("2")).map(e -> {
MyResult resultTemp = new MyResult();
result.color = e.value;
return result;
}).collect(Collectors.toList());
try this
Upvotes: 1
Reputation: 1005
The operation you're looking for is Map. Take a read through the Stream docs for some more details: https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html
Here is a code snippet for mapping your List of MyData objects into a List of MyResult objects.
List<MyResult> collect = collection.stream().map(myData -> {
MyResult myResult = new MyResult();
myResult.color = myData.value;
return myResult;
}).collect(Collectors.toList());
If you want to find just a single value simply add a filter transformation before the Map transformation.
List<MyResult> collect = collection.stream()
.filter(myData -> "2".equals(myData.key))
.map(myData -> {
MyResult myResult = new MyResult();
myResult.color = myData.value;
return myResult;
}).collect(Collectors.toList());
Upvotes: 0
Reputation: 14999
Well you can just first find the element you're looking for
Optional<MyData> found = stream.filter(data -> data.key == 2).findFirst();
then map it to your result
Optional<MyResult> result = found.map(data -> {
MyResult r = new MyResult();
r.color = data.color;
return r;
});
Now you have an Optional
that is empty if the original stream didn't contain an item with key 2, and a matching MyResult
otherwise.
Upvotes: 0