计算 Map of Map 中各个列表的总和

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

Calculate sum of list within a Map of Map

问题

我有一个Trades列表它是一个按地区Region和状态Status分组的嵌套映射Map of Map)。Trade类具有一个属性金额amount字段

    Map<Region, Map<Status, List<Trade>>> groupedTrades;

    class Trade {
      double amount;
    }

我想要在列表中的交易Trades间对金额amount进行分组并按以下方式返回

    Map<Region, Map<Status, Double>> sumOfGroupedTradeAmounts;

其中Double是列表中所有交易金额字段的总和

在Java 8中我该如何实现这个需求
英文:

I have a list of Trades that is a map of map grouped by Region and Status. Trade class has an attribute amount field.

Map&lt;Region, Map&lt;Status, List&lt;Trade&gt;&gt;&gt; groupedTrades

class Trade {
  double amount;
}

I want to group the amounts across Trades within the list and return it as below

Map&lt;Region, Map&lt;Status, Double&gt;&gt; sumOfGroupedTradeAmounts

Double is a sum of all the amount fields within the list of trades.

How can I do this is in java8?

答案1

得分: 1

你可以这样做。基本上,你正在进行以下操作:

  • 进行嵌套的 entrySet 流处理。
  • 对外部映射条目进行流处理,以获取内部映射的值和外部映射的键。
  • 在内部 entry set 中,对值进行流处理,这些值是 List<Doubles>,然后将它们求和。
  • 然后,使用外部映射的键、内部映射的键和交易金额的总和,将它们放入指定的映射中,以得到一个双精度值。

请注意,我为 Trade 类添加了一个用于检索 amount 的 getter 方法。

class Trade {
	double amount;

	public double getAmount() {
		return amount;
	}
}

public static void main(String[] args) {
	Map<Region, Map<Status, Double>> result = groupedTrades
			.entrySet().stream()

            // 外部映射从这里开始,以 Region 为键
			.collect(Collectors.toMap(Entry::getKey, e -> e
					.getValue().entrySet().stream()

                     // 内部映射从这里开始,以 Status 为键
					.collect(Collectors.toMap(Entry::getKey,

                           // 对列表进行流处理并对金额求和。
							ee -> ee.getValue().stream()
									.mapToDouble(Trade::getAmount)
									.sum()))));
	
}

在给定以下结构的情况下,其中 Region 是数字,Status 是字母,

Map<Region, Map<Status, List<Trade>>> groupedTrades = Map.of(   
		new Region(1),                                          
		Map.of(new Status("A"),                                 
				List.of(new Trade(10), new Trade(20),           
						new Trade(30)),                         
				new Status("B"),                                
				List.of(new Trade(1), new Trade(2))),           
		new Region(2),                                          
		Map.of(new Status("A"),                                 
				List.of(new Trade(2), new Trade(4),             
						new Trade(6)),                          
				new Status("B"), List.of(new Trade(3),          
						new Trade(6), new Trade(9))));

result.forEach((k, v) -> {                           
	System.out.println(k);                           
	v.forEach((kk, vv) -> System.out                 
			.println("     " + kk + " -> " + vv));    
});                                                            

以下是示例输出。

2
     B -> 18.0
     A -> 12.0
1
     B -> 3.0
     A -> 60.0

如果你有兴趣对 所有 的值求和,你可以通过使用刚刚创建的嵌套的双精度值映射来实现。

double allSums =
         // 对内部映射进行流处理
         result.values().stream()
         // 将所有的 Double 放入单一流中
		.flatMap(m->m.values().stream())
         // 将 Double 拆箱为原始 double 值
		.mapToDouble(Double::doubleValue)
         // 求和
        .sum();
英文:

You can do it like this. Basically, you are:

  • Doing nested streaming of the entrySets.
  • Stream the outer map entries to get the inner map values and the outer map key.
  • in the inner entry set, stream the value, which is the List&lt;Doubles&gt; and sum them.
  • these are then returned in the specified map using the outer map key, inner map key and a sum of the Trade amounts resulting in a double value.

Note that I added a getter to the Trade class for amount retrieval.

class Trade {
	double amount;
	
	public double getAmount() {
		return amount;
	}
}
	
public static void main(String[] args) {
	Map&lt;Region, Map&lt;Status, Double&gt;&gt; result = groupedTrades
			.entrySet().stream()

            // outer map starts here, keying on Region
			.collect(Collectors.toMap(Entry::getKey, e -&gt; e
					.getValue().entrySet().stream()

                     // inner map starts here, keying on Status
					.collect(Collectors.toMap(Entry::getKey,

                           // stream the list and sum the amounts.
							ee -&gt; ee.getValue().stream()
									.mapToDouble(Trade::getAmount)
									.sum()))));
	
}

Given the following structure where Region and Status are numbers and letters respectively,

Map&lt;Region, Map&lt;Status, List&lt;Trade&gt;&gt;&gt; groupedTrades = Map.of(   
		new Region(1),                                          
		Map.of(new Status(&quot;A&quot;),                                 
				List.of(new Trade(10), new Trade(20),           
						new Trade(30)),                         
				new Status(&quot;B&quot;),                                
				List.of(new Trade(1), new Trade(2))),           
		new Region(2),                                          
		Map.of(new Status(&quot;A&quot;),                                 
				List.of(new Trade(2), new Trade(4),             
						new Trade(6)),                          
				new Status(&quot;B&quot;), List.of(new Trade(3),          
						new Trade(6), new Trade(9))));

result.forEach((k, v) -&gt; {                           
	System.out.println(k);                           
	v.forEach((kk, vv) -&gt; System.out                 
			.println(&quot;     &quot; + kk + &quot; -&gt; &quot; + vv));   
});                                                            

Here is the sample output.

2
     B -&gt; 18.0
     A -&gt; 12.0
1
     B -&gt; 3.0
     A -&gt; 60.0

If you're interested in suming all of the values. you can do it as follows by using the just created nested map of doubles.

double allSums =
         // Stream the inner maps 
         result.values().stream()
         // put all the Doubles in a single stream
		.flatMap(m-&gt;m.values().stream())
         // unbox the Doubles to primitive doubles
		.mapToDouble(Double::doubleValue)
         // and sum them
        .sum();

</details>



# 答案2
**得分**: 0

仅使用流,可能如下所示:

```java
Map<Region, Map<Status, Double>> result =
    groupedTrades
        .entrySet()
        .stream()
        .collect(Collectors.toMap(
                 Map.Entry::getKey, // 保留键不变
                 entry -> mapToDouble(entry.getValue(), MyClass::sumOfTrades))); // 应用 sumOfTrades(见下文)

我们定义一个小辅助函数,以保持流的可读性:

// 接受包含交易列表的映射,并应用函数映射器,在这些列表上返回 T(可能为 double)
private static <T> Map<Status, T> mapToDouble(Map<Status, List<Trade>> trades, Function<List<Trade>, T> mapper) {
    return trades.entrySet()
            .stream()
            .collect(Collectors.toMap(
                    Map.Entry::getKey, // 保留键不变
                    entry -> mapper.apply(entry.getValue())));
}

// 接受交易列表并返回其总和
private static double sumOfTrades(List<Trade> trades) {
    return trades.stream().mapToDouble(Trade::getAmount).sum();
}

额外内容 - 所有交易的总和:

double sum =
    groupedTrades
        // 获取“内部”映射的集合
        .values()
        .stream()
        // 仅获取它们的值作为列表集合
        .map(Map::values)
        // 将集合映射为列表的流
        .flatMap(Collection::stream)
        // 将列表流映射为交易值的流
        .flatMap(Collection::stream)
        // 将交易映射为 double 值 - “amount”
        .mapToDouble(Trade::getAmount)
        // 获取总和
        .sum();
英文:

Using only streams it might look like as follows:

Map&lt;Region, Map&lt;Status, Double&gt;&gt; result =
    groupedTrades
        .entrySet()
        .stream()
        .collect(Collectors.toMap(
                 Map.Entry::getKey, // Leave key as is
                 entry -&gt; mapToDouble(entry.getValue(), MyClass::sumOfTrades))); // applies sumOfTrades (See below)

And we define little helper function, in order to keep our stream readable:

// Takes map which contains lists of trades and and applies function mapper, which returns T (might be double) on those lists
private static &lt;T&gt; Map&lt;Status, T&gt; mapToDouble(Map&lt;Status, List&lt;Trade&gt;&gt; trades, Function&lt;List&lt;Trade&gt;, T&gt; mapper) {
    return trades.entrySet()
            .stream()
            .collect(Collectors.toMap(
                    Map.Entry::getKey, // Leave key as is
                    entry -&gt; mapper.apply(entry.getValue())));
}

// Takes a list of trades and returns its sum
private static double sumOfTrades(List&lt;Trade&gt; trades) {
    return trades.stream().mapToDouble(Trade::getAmount).sum();
}

Bonus - sum of all trades:

double sum =
    groupedTrades
        // get collection of &quot;inner&quot; maps
        .values()
        .stream()
        // get only their values as collection of lists 
        .map(Map::values)
        // map the collection into stream of lists
        .flatMap(Collection::stream)
        // map the collection stream of lists into stream of Trade values
        .flatMap(Collection::stream)
        // map Trade into double value - &quot;amount&quot;
        .mapToDouble(Trade::getAmount)
        // get the sum
        .sum();

huangapple
  • 本文由 发表于 2020年9月29日 00:16:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/64105901.html
匿名

发表评论

匿名网友

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

确定