I will try to simplify a situation I am facing. There are 2 entities:
class Foo {
String fooProperty;
@OneToOne
Bar bar;
}
class Bar {
String barProperty;
}
There is also a DTO class with the attributes from Foo and Bar:
class FooDto {
String fooProperty;
String barProperty;
public static FooDto from(Foo foo) {
return new FooDto(foo.fooProperty, foo.bar.barProperty);
}
}
FooDto is used to deliver huge amounts of data and the conversion can easily be done using streams:
fooList.stream().map(FooDto::from).(...)
Now, there is a change in requirements and a Foo can have many Bars:
@OneToMany
List<Bar> bar;
It is also required that it should be generated one FooDto for each containing Bar, like so:
Foo(fooProperty="foo1", bar=[Bar(barProperty="bar1"), Bar(barProperty="bar2")])
will be converted to:
[FooDto(fooProperty="foo1", barProperty="bar1"), FooDto(fooProperty="foo1", barProperty="bar2")]
I managed to solve it using a intermediate list but I am still unsure it was the best solution.
I was wondering if it is possible to do something like that using purely streams. I can use maps and filters and generate a result with a equal/smaller size than the original input but I can't figure out how to generate a result with more elements.
The operation that you are looking for is called flatMap
.
It operates on a stream of Foo
objects, takes as argument a function that converts a Foo
object into a stream of FooDto
objects and returns a stream combined stream of FooDto
objects:
class FooDto {
String fooProperty;
String barProperty;
public FooDto(String fooProperty, String barProperty) {
this.fooProperty = fooProperty;
this.barProperty = barProperty;
}
public static Stream<FooDto> from(Foo foo) {
return foo.bars.stream().map(bar -> new FooDto(foo.fooProperty, bar.barProperty));
}
}
and then:
List<FooDto> result = fooList.stream().flatMap(FooDto::from).(Collectors.toList());