Lambda – the Java 8 stream is composed of three fields and aggregated by sum and count to produce single line output

I know that similar problems have been raised in the forum, but none of them seem to have completely solved my problem Now I'm new to Java 8, so please wait patiently

Input:
name    category    type    cost
prod1       cat2     t1      100.23
prod2       cat1     t2      50.23
prod1       cat1     t3      200.23
prod3       cat2     t1      150.23
prod1       cat2     t1      100.23


Output:
Single line (name,category,type) summing the cost and count of products.




Product {
    public String name;
    public String category;
    public String type;
    public int id;
    public double cost;

}

I need to group this by name, category and type, and generate a result that summarizes the data and produces the total cost and quantity of each product Most examples show grouping by two fields and aggregating using a single condition

For the suggestion of forumn, I came up with this grouping:

public class ObjectKeys {

    ArrayList<Object> keys;

    public ObjectKeys(Object...searchKeys) {

         keys = new ArrayList<Object>();

            for (int i = 0; i < searchKeys.length; i++) {
                keys.add( searchKeys[i] );
            }
    }

}

Then use it as follows:

Map<String,Map<String,List<Product>>>> productsByNameCategoryType =
    products.stream().collect(groupingBy(new ObjectKeys(l.name(),l.category(),l.type())))

But how do I link counts and sums to the code above? Especially for groups in more than 2 fields Is there a better way?

As I mentioned, my java 8 is not so good, please help

Solution

premise

class Product {
    public String name;
    public String category;
    public String type;
    public int id; 
    //todo:implement equals(),toString() and hashCode()
 }

class Item{
   public Product product;
   public double cost;
}

Summary method

You can use collectors #groupingby & amp;; Summarize items grouped by product Collectors#summarizingDouble.

List<Item> items = ...; 
Map<Product,DoubleSummaryStatistics> stat = items.stream().collect(groupingBy(
            it -> it.product,Collectors.summarizingDouble(it -> it.cost)
));

// get some product summarizing
long count = stat.get(product).getCount();
double sum = stat.get(product).getSum();

//list all product summarizing
stat.entrySet().forEach(it ->
  System.out.println(String.format("%s - count: %d,total cost: %.2f",it.getKey(),it.getValue().getCount(),it.getValue().getSum()));
);

Merge items with the same product

First, you need to add a qty field in the item class:

class Item{
   public int qty;
   //other fields will be omitted

   public Item add(Item that) {
        if (!Objects.equals(this.product,that.product)) {
            throw new IllegalArgumentException("Can't be added items"
                     +" with diff products!");
        }
        return from(product,this.cost + that.cost,this.qty + that.qty);
    }

    private static Item from(Product product,double cost,int qty) {
        Item it = new Item();
        it.product = product;
        it.cost = cost;
        it.qty = qty;
        return it;
    }

}

Then you can use collectors #tomap to merge items with the same product:

Collection<Item> summarized = items.stream().collect(Collectors.toMap(
        it -> it.product,Function.identity(),Item::add
)).values();

last

You can see two ways to do the same thing, but the second method is easier to run on the stream As well as the two tests I checked on GitHub, you can click to see more details: summarizing items & Merge items

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>