Reputation: 171
I have a list of objects like below
public class Price{
private long id;
private String productid;
private long sequence;
private BigDecimal price;
}
i will have list of objects like below
Price1 -> {1, "p1", 1, 1.0}
Price2 -> {2, "p1", 2, 2.0}
Price3 -> {3, "p1", 3, 3.0}
Price4 -> {4, "p2", 1, 1.0}
each product id can have multiple entries of prices(ordered by sequence). The latest price comes with latest sequence. in the above data, product p1 price is 3, p2's price is 1.
I need to filter the above list of java objects to get only 2 entries(id 3 and 4). the output should come with below list
Price3 -> {3, "p1", 3, 3.0}
Price4 -> {4, "p2", 1, 1.0}
Any help with Java8 streams?
Upvotes: 0
Views: 142
Reputation: 1652
Use Collectors.groupingBy
to group the prices per product and then Collectors.maxBy
to get the latest price from the list. The result is a map with latest price per products.
Map<String, Price> lastPricePerProduct = data.stream().collect(
Collectors.groupingBy(Price::getProductid, Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(Price::getSequence)), Optional::get));
More elegant solution with Collectors.toMap
and BinaryOperator.maxBy
:
Map<String, Price> lastPricePerProduct = data.stream().collect(
Collectors.toMap(Price::getProductid,Function.identity(),
BinaryOperator.maxBy(Comparator.comparing(Price::getSequence))));
Upvotes: 2
Reputation: 3
You can try this:
List<Price> list = new ArrayList<>();
Price p1 = new Price(1,"p1",1,new BigDecimal(1.0));
Price p2 = new Price(2,"p1",2,new BigDecimal(2.0));
Price p3 = new Price(3,"p1",3,new BigDecimal(3.0));
Price p4 = new Price(4,"p3",1,new BigDecimal(1.0));
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
List<Price> priceList = list.stream().sorted(Comparator.comparing(Price::getSequence).reversed())
.filter(distinctByKey(Price::getProductid))
.collect(Collectors.toList());
System.out.print(priceList);
public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor)
{
Map<Object, Boolean> map = new ConcurrentHashMap<>();
return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
Output: [Price{id=3, productid='p1', sequence=3, price=3}, Price{id=4, productid='p3', sequence=1, price=1}]
Upvotes: 0
Reputation: 545
You can do something like this:
prices.stream().collect(Collectors.groupingBy(Price::getProductid)).entrySet().forEach(e -> e.getValue().stream().max(Comparator.comparing(Price::getSequence)).get());
NOTE: I have not tested above code so it may have some incorrect semantics.
Explanation:
Collectors.groupingBy(Price::getProductid)
entrySet().forEach()
max(Comparator.comparing(Price::getSequence)).get()
Let me know if you require any further assistance.
Upvotes: 1
Reputation: 1821
Here you need to use groupingBy
rather than filter
to get desired output. Please refer below example:
public class Main {
public static void main(String[] args) {
List<Price> data = Arrays.asList(
new Price(1, "p1", 1, new BigDecimal(1.0)),
new Price(2, "p1", 2, new BigDecimal(2.0)),
new Price(3, "p1", 3, new BigDecimal(3.0)),
new Price(4, "p2", 1, new BigDecimal(1.0)));
Map<String, Object> result = data.stream().collect(Collectors.groupingBy(Price::getProductid,
Collectors.collectingAndThen(
Collectors.reducing((Price p1, Price p2) -> p1.getSequence() > p2.getSequence() ? p1 : p2),
Optional::get)));
System.out.println(result.values());
}
}
Output:
[Price(id=3, productid=p1, sequence=3, price=3), Price(id=4, productid=p2, sequence=1, price=1)]
Price
class:
@AllArgsConstructor
@Data
public class Price {
private long id;
private String productid;
private long sequence;
private BigDecimal price;
}
Upvotes: 1
Reputation: 81
As java.util.stream.Stream#predicate accepts "a non-interfering, stateless predicate",
I think you should use a Collection
for that.
I would use java.util.stream.Stream#collect with a Collector
.
java.util.stream.Collectors makes it convenient.
Upvotes: 0