计算对象图中的平均多个数字

时间:2014-09-22 12:44:24

标签: java map lambda average java-8

我有格式的地图

Map<String, List<TableDTO>>

public class TableDTO {
    private String countryName;
    private String sourceName;
    private int year;
    private Double usageValue;
    private Double powerUsers;

    //Setter & Getters

}

我想找到usageValues和powerUsers的平均值,并且仍然保持TableDTO结构,而usageValue可以为null,如果它null则完全忽略该对象。

<Chrome, <UK, Lorem, 2013, 2.90, 5.4>>
<Chrome, <US, Lorem, 2013, 4.10, 1.5>>
<Chrome, <EU, Lorem, 2013, 1.20, 0.22>>
<Chrome, <Asia, Lorem, 2013, 3.90, -1.10>>

<IE, <UK, Lorem, 2013, 1.40, 24.4>>
<IE, <US, Lorem, 2013, 0.90, 14.4>>
<IE, <EU, Lorem, 2013, 2.10, 0>>
<IE, <Asia, Lorem, 2013, 0.90, 0.4>>

<FF, <UK, Lorem, 2013, 0.10, 2.14>>
<FF, <US, Lorem, 2013, 1.10, 4.0>>
<FF, <EU, Lorem, 2013, , 4.4>>
<FF, <Asia, Lorem, 2013, 2.90, 4.4>>

预期结果

<1, <UK, Lorem, 2013, 1.47, 10.65>>
<2, <US, Lorem, 2013, 2.03, 6.63>>
<3, <Asia, Lorem, 2013, 2.57, 1.23>>

目前在结果中我已经用索引替换了键,现在这很好。您会注意到,由于欧盟的FF具有空值,因此整个欧盟被忽略,但其余的我已经计算了平均值。

如何使用Java 8中的Lambda表达式完成此操作,还是必须迭代?

更新1: 这是我现在所得到的:

1. 
Map<String, List<TableDTO>> dump = mapOfAllData.values()
                .stream()
                .flatMap(list -> list.stream())
                .collect(Collectors.groupingBy(TableDTO::getCountryName));

Which give me a map with country names and the DTO orderd

2. 
  dump.values().stream().flatMap(list -> list.stream())
                .filter((o -> !o.getUsageValue().isEmpty()))
                .collect(Collectors.mapping(TableDTO::getUsageValue, Collectors.averagingDouble(Double::parseDouble)));

基本上获得平均值,但不会删除usageValue为空的DTO,我正在尝试解决此问题。

更新2:

我设法从地图中删除了不需要的国家/地区。

我想弄清楚如何找到两个元素的平均值,我有这个表达式

 newMap.values().stream().flatMap(list -> list.stream())
                .collect(Collectors.mapping(TableDTO::usageValue, Collectors.averagingDouble(s -> s.isEmpty() ? Double.NaN : Double.parseDouble(s))));
                       // Collectors.mapping(TableDTO::powerUsers, Collectors.averagingDouble(c -> c.isEmpty() ? Double.NaN : Double.parseDouble(c))));

但无法获得powerUsers的平均值。

1 个答案:

答案 0 :(得分:2)

要理解,您希望List<TableDTO> groupBy countryNamesourceNameyear的平均值超过每个usagePower,但平均值在不同的字段上?

我希望powerUsersDoubleDouble.parseDouble,而不是像您的代码那样的字符串以及您对package stackoverflow; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import javax.annotation.Nullable; public class TableDTO { private final String countryName; private final String sourceName; private final int year; @Nullable private final Double usageValue; private final Double powerUsers; public TableDTO(final String countryName, final String sourceName, final int year, final Double usageValue, final Double powerUsers) { this.countryName = countryName; this.sourceName = sourceName; this.year = year; this.usageValue = usageValue; this.powerUsers = powerUsers; } public String getCountryName() {return countryName;} public String getSourceName() {return sourceName;} public int getYear() {return year;} @Nullable public Double getUsageValue() {return usageValue;} public Double getPowerUsers() {return powerUsers;} @Override public String toString() { return "TableDTO [countryName=" + countryName + ", sourceName=" + sourceName + ", year=" + year + ", usageValue=" + usageValue + ", powerUsers=" + powerUsers + "]"; } public static void main(final String[] args) { final java.util.Map<String, java.util.List<TableDTO>> data = new LinkedHashMap<>(); final List<TableDTO> chrome = new ArrayList<>(); chrome.add(new TableDTO("UK", "Lorem", 2013, 2.90, 5.4)); chrome.add(new TableDTO("US", "Lorem", 2013, 4.10, 1.5)); chrome.add(new TableDTO("EU", "Lorem", 2013, 1.20, 0.22)); chrome.add(new TableDTO("Asia", "Lorem", 2013, 3.90, -1.10)); data.put("Chrome", chrome); final List<TableDTO> ie = new ArrayList<>(); ie.add(new TableDTO("UK", "Lorem", 2013, 1.40, 24.4)); ie.add(new TableDTO("US", "Lorem", 2013, 0.90, 14.4)); ie.add(new TableDTO("EU", "Lorem", 2013, 2.10, 0.)); ie.add(new TableDTO("Asia", "Lorem", 2013, 0.90, 0.4)); data.put("IE", ie); final List<TableDTO> fx = new ArrayList<>(); fx.add(new TableDTO("UK", "Lorem", 2013, 0.10, 2.14)); fx.add(new TableDTO("US", "Lorem", 2013, 1.10, 4.0)); fx.add(new TableDTO("EU", "Lorem", 2013, null, 4.4)); fx.add(new TableDTO("Asia", "Lorem", 2013, 2.90, 4.4)); data.put("FX", fx); data.values() .stream() .flatMap(List::stream) .collect(Collectors.groupingBy(dto -> Arrays.asList(dto.getCountryName(), dto.getSourceName(), dto.getYear()))) .values() .stream() .filter(list -> list.stream().map(TableDTO::getUsageValue).noneMatch(Objects::isNull)) .map( values -> { final TableDTO root = values.iterator().next(); final double usageValueAvg = values.stream().map(TableDTO::getUsageValue).filter(Objects::nonNull) .collect(Collectors.averagingDouble(Double::doubleValue)); final double powerUsersAvg = values.stream().map(TableDTO::getPowerUsers) .collect(Collectors.averagingDouble(Double::doubleValue)); return new TableDTO(root.getCountryName(), root.getSourceName(), root.getYear(), usageValueAvg, powerUsersAvg); }).forEach(System.out::println); ; } } 的使用建议。

此代码应该这样做:

TableDTO [countryName=UK, sourceName=Lorem, year=2013, usageValue=1.4666666666666666, powerUsers=10.646666666666667]
TableDTO [countryName=US, sourceName=Lorem, year=2013, usageValue=2.033333333333333, powerUsers=6.633333333333333]
TableDTO [countryName=Asia, sourceName=Lorem, year=2013, usageValue=2.5666666666666664, powerUsers=1.2333333333333334]

结果是:

flatMap

并解释说:我已经采取了一些代码来完成它。

  • data

    的值执行data.values() .stream() .flatMap(List::stream)
    TableDTO
  • 通过某些键对您的hashCode进行分组:我们不关心密钥,唯一重要的是它正确实现了equalsArrays.asListArrays.hashCode完成这项工作。否则,创建一个使用数组并使用equals / .collect(Collectors.groupingBy(dto -> Arrays.asList(dto.getCountryName(), dto.getSourceName(), dto.getYear()))) .values() .stream() 的类Tuple。

    TableDTO

    由于我们不想要列表,我们选择值并使用流。

  • 我们过滤了包含空usageValue的<{1}}:

        .filter(list -> list.stream().map(TableDTO::getUsageValue).noneMatch(Objects::isNull))
    
  • 然后我们制作了一张地图,以及您未能找到解决方案的地方:由于该群组,所有TableDTO共享相同的countryNamesourceName和{ {1}}价值。但不是yearusageValue

    因为列表不能为空,所以我们得到第一个元素。

    powerUsers
  • 在另一个结果中,我们计算过滤 .map( values -> { final TableDTO root = values.iterator().next(); 的任何空值的两个平均值。

    usageValue
  • 然后我们根据三个分组键和两个平均值返回一个新的 final double usageValueAvg = values.stream().map(TableDTO::getUsageValue).filter(Objects::nonNull) .collect(Collectors.averagingDouble(Double::doubleValue)); final double powerUsersAvg = values.stream().map(TableDTO::getPowerUsers) .collect(Collectors.averagingDouble(Double::doubleValue));

    TableDTO
  • 我们打印它,瞧! :)

              return new TableDTO(root.getCountryName(), root.getSourceName(), root.getYear(), usageValueAvg,
                  powerUsersAvg);
    
            })
    

我希望它能解决你的问题。

我在Eclipse中对它进行了测试,它进行了编译,但是javac可能会失败,因为编译器与Lambdas的工作方式不同。