2014-09-22 5 views
2

У меня есть карта форматаРасчет средних несколько чисел в карте объектов

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 полностью игнорирует этот объект.

<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, или мне нужно пройти через итерацию?

Update 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))); 

В основном получает в среднем, но не удаляет DTO, в котором usageValue пусто, что я пытаюсь в момент для решения.

Update 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.

+0

Это похоже на ваш предыдущий вопрос - было бы проще, если бы вы показали код, который у вас есть, и что не работает. – assylias

+0

@assylias, честно говоря, я все еще не обойдусь, моя проблема в том, что у меня нет наставника, чтобы помочь мне выучить Java, поэтому я больше полагаюсь на форумы и Javadocs – victor

+1

@victor: Особенно, если вы хотите Чтобы узнать, вы должны сначала попытаться решить проблему самостоятельно. Задавать почти тот же вопрос здесь означает не много успехов в обучении и предполагает, что вы должны подвергнуть сомнению свой метод обучения. – Holger

ответ

2

Чтобы понять, вы хотите, в среднем по каждому List<TableDTO> с groupBycountryName, sourceName, year, но в среднем находятся на отдельной области?

Я ожидаю, что usagePower и powerUsers будут Double, а не String, как ваш код, и ваше использование Double.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 и equals. Arrays.asList выполняет эту работу.В противном случае создайте класс Tuple, который принимает массив и использует Arrays.hashCode/equals.

    .collect(Collectors.groupingBy(dto -> Arrays.asList(dto.getCountryName(), dto.getSourceName(), dto.getYear()))) 
        .values() 
        .stream() 
    

    Поскольку нам не нужен список, мы выбираем значения и используем поток.

  • Мы фильтруем TableDTO, который содержит пустой usageValue:

    .filter(list -> list.stream().map(TableDTO::getUsageValue).noneMatch(Objects::isNull)) 
    
  • Затем мы делаем карту, и что там, где вы потерпели неудачу в поиске решения: из группы, все TableDTO долю той же countryName , sourceName и year значение. Но не usageValue и 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); 
    
         }) 
    
  • И мы печатаем его, и вуаля! :)

     .forEach(System.out::println); 
    

Я надеюсь, что решить вопрос.

Я тестировал его в Eclipse, он компилируется, но может быть неудачным с javac, поскольку компилятор не работает с Lambdas.

+0

, чтобы вы объяснили, что теперь я знаю, где я ошибся. Какова структура данных конечного результата. Map , но я получаю сообщение об ошибке и должен определить его как Stream, это правильно? Я получаю следующую ошибку: «Это ошибка Ошибка: (87, 21) java: несовместимые типы: нет экземпляра (s) переменной типа (ов) R существуют так, чтобы java.util.stream.Stream соответствует java.util.Map > " – victor

+0

Конечным результатом будет' Stream '. Вам нужно будет использовать' collect (Collectors.toList()) ', чтобы получить« Список ». И если вам нужно хранить информацию (например, Chrome "и т. Д.), То мой пример неверен: вам понадобится промежуточная операция' map' для преобразования каждого 'Map.Entry >' в 'List ' (при условии, что 'String' это имя браузера;)). – NoDataFound

+0

спасибо, но вот что я сделал для Each (element -> _finalRank.add ((TableDTO) element)), где _finalRank - это список victor

Смежные вопросы