Java 8查询 – 基于多个对象列表的关键字分组计算

huangapple go评论72阅读模式
英文:

Java 8 Query - Grouping calculation based on Key with Multiple Lists of Objects

问题

我需要一些关于使用Java 8解决方案来计算总额的帮助。

假设我有3个物品对象,每个对象都包含一个(2或3个)税对象的列表。请在下面找到此对象的详细信息 -

物品 1
(列表)
税收 1 - Id-1 | 名称-A | 描述 | 金额-Rs.5 | 更多对象
税收 2 - Id-2 | 名称-B | 描述 | 金额-Rs.10 | 更多对象
税收 3 - Id-3 | 名称-C | 描述 | 金额-Rs.8 | 更多对象

物品 2
(列表)
税收 1 - Id-1 | 名称-A | 描述 | 金额-Rs.9 | 更多对象
税收 3 - Id-3 | 名称-C | 描述 | 金额-Rs.10 | 更多对象

现在,
我已经创建了一个新的对象(聚合),该对象需要根据Id进行总计
例如:上述信息应该有3个根据Id总计的聚合对象
税收-1 | 名称-A | 描述 | 金额-Rs.14
税收-2 | 名称-B | 描述 | 金额-Rs.10
税收-3 | 名称-C | 描述 | 金额-Rs.18

我能够通过循环遍历每个对象并将其添加到一个Map<String, Aggregate>中来实现这一点。如果键已经存在,我将从聚合对象中提取金额值并将金额添加到现有金额中。如果不存在,我将创建一个新的聚合对象并将其添加到地图中,但我希望在Java 8中是否有更优化的方法来做到这一点。

全局变量 - Map<String, Aggregate> taxAggreg

for (Item item : Items) {
    List<Tax> taxes = item.getTaxes;
    for (Tax tax : taxes) {
        createTax(Tax tax);
    }
}

private void createTax(Tax tax) {
    if (!taxAggreg.isEmpty() && taxAggreg.containsKey(tax.getTaxId())) {
        Aggregate aggreg = taxAggreg.get(tax.getTaxId());
        aggreg.setTaxAmount(aggreg.getTaxAmount().add(tax.getTaxAmount()));
        taxAggreg.put(tax.getTaxId(), aggreg);
    } else {
        taxAggreg.put(tax.getTaxId(), new Aggregate(tax.getTaxId(), tax.getName(),
            tax.getTaxAmount()));
    }
}
英文:

I needed some help with a Java 8 solution to calculate totals.<br>
Suppose that I have 3 item objects and each of these objects contain a list of (2 or 3) tax objects. Please find below a detailed imaging of this object -

Item 1<br>
(List)<br>
Tax 1 - Id-1 | Name-A | Desc | Amount-Rs.5 | More objects<br>
Tax 2 - Id-2 | Name-B | Desc | Amount-Rs.10 | More objects<br>
Tax 3 - Id-3 | Name-C | Desc | Amount-Rs.8 | More objects<br>
<br>
Item 2<br>
(List)<br>
Tax 1 - Id-1 | Name-A | Desc | Amount-Rs.9 | More objects<br>
Tax 3 - Id-3 | Name-C | Desc | Amount-Rs.10 | More objects<br>

Now,<br>
I have created a new Object (Aggregate) that needs to be the total based on Id<br>
For ex: The above information should be have 3 aggregate objects with amounts summed <br>
Tax-1 | Name-A | Desc | Amount-Rs.14<br>
Tax-2 | Name-B | Desc | Amount-Rs.10<br>
Tax-2 | Name-B | Desc | Amount-Rs.18<br>
<br>
I was able to achieve this by looping through each object and adding it to a map <String Id,Object Aggregate>. If the keys existed, I would pull out the amount value from the Object Aggregate and add the amount to the existing amount. If not, I would create a new Aggregate object and add it to the map but I was hoping if there was a more optimised way to do this in Java 8<br>

Global variable - Map&lt;String, Aggregate&gt; taxAggreg

for (Item item: Items) {
    List &lt; Tax &gt; taxes = item.getTaxes;
    for (Tax tax: taxes) {
        createTax(Tax tax);
    }
}

private void createTax(Tax tax) {
    if (!taxAggreg.isEmpty() &amp;&amp; taxAggreg.containsKey(tax.getTaxId())) {
        Aggregate aggreg = taxAggreg.get(tax.getTaxId());
        aggreg.setTaxAmount(aggreg.getTaxAmount().add(tax.getTaxAmount()));
        taxAggreg.put(tax.getTaxId(), aggreg);
    } else {
        taxAggreg.put(tax.getTaxId(), new Aggregate(tax.getTaxId(), tax.getName(),
            tax.getTaxAmount()));
    }
}

答案1

得分: 2

首先,将项目的标签列表展平,然后将Tax对象转换为Aggregate对象。然后使用Collectors.toMap按tax id进行映射,然后合并具有相同id的tax对象。

Map<Integer, Aggregate> resMap = items.stream()
    .flatMap(item -> item.getTaxes().stream())
    .map(tax -> new Aggregate(tax.getTaxId(), tax.getName(), tax.getTaxAmount()))
    .collect(Collectors.toMap(Tax::getTaxId, e -> e,
        (a, b) -> new Aggregate(a.getTaxId(), a.getName(),
            a.getTaxAmount().add(b.getTaxAmount()))));

在这里使用Collectors.toMapTax::getTaxId进行映射,这是toMap的第一个参数。下一个参数是映射的键,e -> e表示整个Aggregate对象作为键。最后,在合并函数的最后一个参数中,将用于将两个Aggregate对象合并为一个Aggregate对象,以便将相同键的所有值合并为一个Aggregate对象。

Collectors.toMap(Tax::getTaxId, e -> e,
    (a, b) -> new Aggregate(a.getTaxId(), a.getName(),
        a.getTaxAmount().add(b.getTaxAmount())))
英文:

First, flat the list of tag for item, then transform the Tax object into Aggregate object. Then map by tax id using Collectors.toMap then merge tax objects with same id.

Map&lt;Integer, Aggregate&gt; resMap = items.stream()
                .flatMap(item -&gt; item.getTaxes().stream())
                .map(tax -&gt; new Aggregate(tax.getTaxId(), tax.getName(), tax.getTaxAmount()))
                .collect(Collectors.toMap(Tax::getTaxId, e -&gt; e,    
                                  (a, b) -&gt; new Aggregate(a.getTaxId(), a.getName(), 
                                                   a.getTaxAmount().add(b.getTaxAmount()))));

Here using Collectors.toMap map by Tax::getTaxId which is the first parameter of toMap. The next one is the key of the map e -&gt; e for the whole Aggregate obj as key. And finally, the last parameter in the merge function will be used for merging two Aggregate objects into one Aggregate object so that all values of the same key merged into one Aggregate object

Collectors.toMap(Tax::getTaxId, e -&gt; e,    
                    (a, b) -&gt; new Aggregate(a.getTaxId(), a.getName(),
                                            a.getTaxAmount().add(b.getTaxAmount())))

答案2

得分: 0

也许类似这样的代码可以工作:

List<Aggregate> list1 = new ArrayList<Aggregate>();
List<Aggregate> list2 = new ArrayList<Aggregate>();

// 将数据添加到列表中...

// 现在为列表中每个对象创建带有其标识的映射
Map<String, Aggregate> aggregateMap1 = list1.stream().collect(Collectors.toMap(Aggregate::getId, Function.identity()));
Map<String, Aggregate> aggregateMap2 = list2.stream().collect(Collectors.toMap(Aggregate::getId, Function.identity()));

// 现在合并列表并将金额相加
List<Aggregate> result = aggregateMap1.keySet().stream().map(key -> {
    Aggregate aggr1 = aggregateMap1.get(key);
    Aggregate aggr2 = aggregateMap2.get(key);
    if (aggr2 != null) {
        aggr1.setAmount(aggr1.getAmount() + aggr2.getAmount());
    }
    return aggr1;
}).collect(Collectors.toList());

未经测试,还可以进行微调。

英文:

Maybe something like this could work:

    List&lt;Aggregate&gt; list1 = new ArrayList&lt;Aggregate&gt;();      
    List&lt;Aggregate&gt; list2 = new ArrayList&lt;Aggregate&gt;();    

    // Add your data to lists...

    // Now create maps with the identity for each object in lists
    Map&lt;String, Aggregate&gt; aggregateMap1 = list1.stream().collect(Collectors.toMap(Aggregate::getId, Function.identity()));
    Map&lt;String, Aggregate&gt; aggregateMap2 = list2.stream().collect(Collectors.toMap(Aggregate::geId, Function.identity()));

    // Now merge lists and add the amounts together
    List&lt;Aggregate&gt; result = aggregateMap1.keySet().forEach(key -&gt; aggregateMap1.merge(key,
            aggregateMap2.get(key),
            (aggr1, aggr2) -&gt; {
                aggr1.setAmount(aggr1.getAmount() + aggr2.getAmount());
                return aggr1;
            })).values().stream().collect(Collectors.toList());;

Not tested. Also could be fine tuned.

huangapple
  • 本文由 发表于 2020年7月30日 20:01:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/63172738.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定