Home > front end >  Is there a Java equivalent of C#'s LINQ "Select" function
Is there a Java equivalent of C#'s LINQ "Select" function

Time:04-04

LINQ has Enuemerable.Select, does Java have any equivalent?

Say I have some class Record with three fields id, price, itemsSold and a list of these records from which I want to filter out the most popular item measured by which product sold the most items.

In LINQ I could do something like this

var mostPopularItem = records.GroupBy(sr => sr.Id)
       .Select(g => new
       {
           Id = g.Key,
           TotalSold = g.Sum(r => r.ItemsSold)
       })
       .OrderByDescending(i => i.TotalSold).First();

by using the Select(...) method to reconstruct into a suitable form. How would I do the same in Java? I could simply use streams to extract the actual number

records.stream().map(r -> r.getItemsSold()).sorted(Comparator.reverseOrder()).collect(Collectors.toList)).get(0)

but this would only give me an array of the items sold sorted in descending order. Optimally I would like to end up with an object that contains the id and the itemsSold.

CodePudding user response:

        List<Record> recordList = new ArrayList<>();
        recordList.add(new Record(1L, new BigDecimal(2), 4));
        recordList.add(new Record(1L, new BigDecimal(2), 5));
        recordList.add(new Record(1L, new BigDecimal(2), 7));
        recordList.add(new Record(2L, new BigDecimal(2), 10));

        Map.Entry<Long, Integer> mostPopularItem = recordList.stream().collect(groupingBy(Record::getId, summingInt(Record::getItemsSold))).entrySet().stream().max(Map.Entry.comparingByValue()).orElse(null);

Output:

Key = 1, Value = 16
public class Record {
    public Record(Long id, BigDecimal price, Integer itemsSold) {
        this.id = id;
        this.price = price;
        this.itemsSold = itemsSold;
    }
    private Long id;
    private BigDecimal price;
    private Integer itemsSold;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public Integer getItemsSold() {
        return itemsSold;
    }

    public void setItemsSold(Integer itemsSold) {
        this.itemsSold = itemsSold;
    }
}

CodePudding user response:

This should return the most popular item:

var itemsSoldById = records.stream()
        .collect(groupingBy(rec -> rec.getId(), summingInt(rec -> rec.getItemsSold())));

var mostPopularItem = itemsSoldById
        .entrySet()
        .stream()
        .max(Comparator.comparingInt(Map.Entry::getValue))
        .orElseThrow(()-> new IllegalStateException("The list of records is empty"));

Previous version before my edit that did not group records by id:

   var mostPopularItem = records.stream()
        .sorted(Comparator.comparing(rec -> rec.getItemsSold()))
        .reduce((rec1 , rec2) -> rec1.getItemsSold() > rec2.getItemsSold() ? rec1 : rec2)
        .orElseThrow(()-> new IllegalStateException("The list of records is empty"));

CodePudding user response:

Something like this?

package example.stackoverflow;

import java.util.List;
import java.util.stream.Collectors;

public class Select {
    public static void main(String[] args) {
        record Thing(String id, int price, int itemsSold){}
        List<Thing> records = List.of(
                new Thing("a", 5, 14),
                new Thing("b", 4, 33),
                new Thing("c", 6, 10),
                new Thing("a", 5, 21),
                new Thing("c", 6, 12)
        );
        record PopularThing(String id, int totalSold){}
        records.stream()
                .collect(Collectors.groupingBy(Thing::id, Collectors.summingInt(Thing::itemsSold)))
                .entrySet().stream().map(e -> new PopularThing(e.getKey(), e.getValue()))
                .max((a,b) -> a.totalSold - b.totalSold)
                .ifPresent(System.out::println);
    }
}

CodePudding user response:

The equivalent of Select in Java is the map-function on Stream. But in your case, you probably want to use the second parameter of groupingBy-collector to sum up the ItemsSold-values.

Java doesn't have is anonymous objects, like you create in your Select-call. So you would need to define a class or record to hold that data.

Using records, you could do this:

record TotalSoldRecord(String id, int totalSold) {}

var mostPopularItem = records.stream()
    // Grouping by ID into a Map<String, int>, where the keys are 
    // the recordIds and the values are the sum of "itemsSold".
    .collect(Collectors.groupingBy(Record::getId, 
        Collectors.summingInt(Record::getItemsSold)))
    .entrySet()
    .stream()
    .map(entry -> new TotalSoldRecord(entry.getKey(), entry.getValue()))
    // Finding the TotalSoldRecord with the maximum totalSold.
    .max(Comparator.comparingInt(TotalSoldRecord::totalSold));

Note: You could also use Map.Entry as the result instead of mapping it to TotalSoldRecord.

  • Related