Reputation: 1891
I have the following entities:
ID | PROPERTY
FOO | A
FOO | Z
JOHN | A
DOE | Z
Now I want to group these entities by ID and the take the one with property 'A' if one or both valuesare present , otherwise Z.
Thus the output should be:
FOO | A
JOHN | A
DOE | Z
I was thinking about the groupingBy stream method, but I don't know how what collectors to use neither if is the correct solution:
list.stream()
.collect(Collectors.groupingBy(o -> o.getId(), ? another collector function ?)
.collect(Collectors.toList()));
Upvotes: 0
Views: 238
Reputation: 2210
// Utility class with methods you can reuse for other purposes too
public final class StreamUtil {
private StreamUtil() {}
public static <T, U> Predicate<T> distinctBy(Function<T, U> keyFunction) {
final Set<U> uniqueKeys = synchronizedSet(new HashSet<>());
return item -> uniqueKeys.add(keyFunction.apply(item));
}
}
and solution for your question
entities.stream()
.sorting(Comparator.comparing(Entity::getProperty)) // order entities according to priorities
.filter(StreamUtil.distinctBy(Entity::getId)) // take first entity only per key
... // here you have stream as per you requirements. You can collect() it or continue mapping/filtering
This solution is slower than other suggested here because of sorting. However, it has its advantage - easier to read, while slowness will kick in on large collections only.
Upvotes: 1
Reputation: 7279
Try this:
entities.stream()
.collect(toMap(YourEntityType::getId,
YourEntityType::getProperty,
(x, y) -> "A".equals(x) ? x : y));
I've just noticed that @Holger posted exactly the same thing in his comment, while I was editing my answer. There are two minor differences though:
"A".equals(x)
is safer than x.equals("A")
. The latter will fail with NPE in case x
is null
.Since you want the result in form of a list, then this option should work for you:
entities.stream()
.collect(collectingAndThen(
toMap(YourEntityType::getId,
identity(),
(x, y) -> "A".equals(x.getProperty()) ? x : y,
m -> new ArrayList<YourEntityType>(m.values()));
Upvotes: 1
Reputation: 12000
If you insisted on using grouping collector, the ? another collector function ?
would look like this:
public static void main(String[] args) {
record C(String id, String property) {}
List<C> list = List.of(new C("FOO","A"),new C("FOO","Z"),new C("JOHN","A"),new C("DOE","Z"),new C("XXX","X"));
System.out.println(list);
Object result = list.stream()
.collect(Collectors.groupingBy(o -> o.id, Collectors.collectingAndThen(Collectors.reducing((l,r) -> "A".equals(l.property) ? l : r), Optional::get)));
System.out.println(result);
}
, which Idea suggest to correct to almost @ETO's solution.
(Apologize, having records I am lazy to use getters.)
Upvotes: 1