Reputation: 75
I have list of Objects say
class Address {
private String houseno;
private String street;
private String city;
private String landmark;
private String country;
}
class Obj1 {
private String id;
private String name;
private String mail;
private List<Address> address;
}
List<Obj1> obj1s = new ArrayList<>();
I have list of obj1s filled with data where I need to extract it with some filters. I need to retrieve all the obj1s where city and country must not be duplicate. If either of the address list contains same city and country which is same in other obj1, then I don't need in that list.
I am trying the same but not working.
obj1s.stream().collect(Collectors.toMap((obj1.getAddress()), p -> p, (p, q) -> p)).values();
It can be done with loops and if else conditions, but looking for java streams and betters solutions. Is there any good way to solve this?
city+country can be duplicate across all obj1s. In address list, it will be unique
Upvotes: 0
Views: 3350
Reputation: 3400
We need something like "distinct" which is kind of like a stateful filter. So we can create our own filter having its own state of previously seen pairs of cities/countries. This can be achieved with having a filter with its own Set:
public static <T> Predicate<T> distinctAddress() {
Set<Object> seen = ConcurrentHashMap.newKeySet();
return t -> {
boolean r = ((Obj1)t).getAddress().stream().map((a) -> seen.contains(new AbstractMap.SimpleImmutableEntry<>(a.getCity(), a.getCountry()))).anyMatch((b) -> b);
((Obj1)t).getAddress().stream().forEach((a) -> seen.add(new AbstractMap.SimpleImmutableEntry<>(a.getCity(), a.getCountry())));
return !r;
};
}
List<Obj1> list = obj1s.stream().filter(distinctAddress()).collect(Collectors.toList());
Edit: I just realized you mention an address will always be unique inside an address list. In that case you can shorten the Predicate as so (as you wouldn't already have it in the Set because of the same list):
public static <T> Predicate<T> distinctAddress() {
Set<Object> seen = ConcurrentHashMap.newKeySet();
return t -> ((Obj1)t).getAddress().stream().map((a) -> seen.add(new AbstractMap.SimpleImmutableEntry<>(a.getCity(), a.getCountry()))).filter((e) -> e).count() > 0;
}
List<Obj1> list = obj1s.stream().filter(distinctAddress()).collect(Collectors.toList());
Upvotes: 1
Reputation: 4076
You can do like this:
Map<String, List<Obj1>> result= objs.stream()
.flatMap(obj -> {
Map<String, Obj1> map = obj.address.stream()
.map(address -> address.country + address.city)
.collect(Collectors.toMap(String::valueOf, str -> obj));
return map.entrySet().stream();
}
).collect(Collectors.groupingBy(
Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList()))
);
It returs a map which it's key is generated for each address by address.name and address.country , and it's value is a list of Obj1 objects that currently have this address.
Please make sure each list of addresses has unique elements before testing.
Upvotes: 1