Have two classes and two corresponding lists:
class Click {
long campaignId;
Date date;
}
class Campaign {
long campaignId;
Date start;
Date end;
String type;
}
List<Click> clicks = ..;
List<Campaign> campaigns = ..;
And want to find all Click
s in clicks
that:
Have a corresponding
Campaign
incampaigns
list, i.e.,Campaign
with the samecampaignId
ANDThis
Campaign
hastype
= "prospective" ANDThis
Campaigns.start
<click.date
<Campaigns.end
So far I have the following implementation (which seems confusing and complex to me):
clicks.
stream().
filter(click -> campaigns.stream().anyMatch(
campaign -> campaign.getCampaignType().equals("prospecting") &&
campaign.getCampaignId().equals(click.getCampaignId()) &&
campaign.getStart().after(click.getDate()) &&
campaign.getEnd().before(click.getDate()))).
collect(toList());
I wonder if there is simpler solution for the problem.
One thing that stands out is that your 2nd requirement has nothing to do with the matching, it's a condition on campaigns
only. You'd have to test if this is any better for you:
clicks.stream()
.filter(click -> campaigns.stream()
.filter(camp -> "prospecting".equals(camp.type))
.anyMatch(camp ->
camp.campaignId == click.campaignId &&
camp.end.after(click.date) &&
camp.start.before(click.date)
)
)
.collect(Collectors.toList());
Otherwise, I have never seen a streams solution which does not involve streaming the 2nd collection inside the predicate of the 1st, so you can't do much better than what you did. In terms of readability, if it looks that confusing to you then create a method that test for the boolean condition and call it:
clicks.stream()
.filter(click -> campaigns.stream()
.filter(camp -> "pre".equals(camp.type))
.anyMatch(camp -> accept(camp, click))
)
.collect(Collectors.toList());
static boolean accept(Campaign camp, Click click) {
return camp.campaignId == click.campaignId &&
camp.end.after(click.date) &&
camp.start.before(click.date);
}
Finally, 2 unrelated suggestions:
- Don't use the old
Date
class, instead use the new java.time API's LocalDate. - If
Campaign
'stype
can only have some predefined values (like "submitted", "prospecting", "accepted"...) then anenum
would be a better fit than a generalString
.