Simon Schüpbach
Simon Schüpbach

Reputation: 2683

Java streams, create new object based on two different objects

What is the best way to create a new object based on two different objects.

I would like to use java streams.

My two start objects

public class EventA{
    Long id;
    String name;
    ...
    Long locationID;
}

public class EventB{
    Long id
    String Name;
    ...
    Long locationID;
}

And my result class

public class Result{
    Long locationID;
    String eventAName;
    String eventBName;

    public Result(...){...}
}

I have two object arrays like

List<EventA> eventAList;
List<EventB> eventBList;

I like to get an array of Result objects. Every EventA name should be copied to the resultList. If an EventB at the same location exists I would like to save the name in eventBName.

All I have done so far is

List<Result> resultList = eventAList.stream().map(e -> new Result(e.locationID, e.name, null)).collect(Collectors.toList());

I don't know how to pass the value from EventB to the constructor

Upvotes: 1

Views: 10128

Answers (3)

George Karanikas
George Karanikas

Reputation: 1244

You can do something like the following and work on enhancements afterwards (e.g. create a map for eventBlist by locationId as key in order to have faster search)

Function<EventA, SimpleEntry<EventA, Optional<EventB>>> mapToSimpleEntry = eventA -> new SimpleEntry<>(eventA,
    eventBList.stream()
    .filter(e -> Objects.equals(e.getLocationID(), eventA.getLocationID()))
    .findFirst());

Function<SimpleEntry<EventA, Optional<EventB>>, Result> mapToResult = simpleEntry -> {
    EventA eventA = simpleEntry.getKey();
    Optional<EventB> eventB = simpleEntry.getValue();
    return new Result(eventA.getLocationID(), eventA.getName(), eventB.map(EventB::getName).orElse(null));
};

eventAList.stream()
    .map(mapToSimpleEntry)
    .map(mapToResult)
    .collect(Collectors.toList());

Upvotes: 1

Simon Sch&#252;pbach
Simon Sch&#252;pbach

Reputation: 2683

I found a working way

I adjust the constructor of the Result class to

public Result(Long locationID, String eventAName, EventB eventB){
    this.locationID = locationID;
    this.eventAName = eventAName;
    this.eventBName = eventB.name;
}

and then inside my java stream

List<Result> resultList = eventAList.stream().map(ea -> new Result(ea.locationID, ea.name, eventBList.stream().filter(eb -> eb.locationID.equals(ea.locationID)).findFirst().orElse(new EventB()).get()).collect(Collectors.toList());

Upvotes: 1

Florent Bayle
Florent Bayle

Reputation: 11920

When creating your Result, you can use a stream to iterate on the values in eventBList to retain only the ones with the same locationID as your eventAList value, then take the value you found, and map() it to it's Name value, or null if it doesn't exists:

List<Result> resultList = eventAList.stream().map(a -> new Result(a.locationID, a.name,
    eventBList.stream().filter(b -> b.locationID.equals(a.locationID)).findAny().map(b -> b.Name).orElse(null)
)).collect(Collectors.toList());

For better performances, you can use a temporary Map:

final Map<Long, String> eventBMap = eventBList.stream().collect(Collectors.toMap(b -> b.locationID, b -> b.Name));

List<Result> resultList = eventAList.stream().map(a -> new Result(a.locationID, a.name,
    eventBMap.get(a.locationID)
)).collect(Collectors.toList());

Upvotes: 3

Related Questions