2016-08-15 41 views
1
列表

假設我有一個多圖所示:「移調」多地圖地圖

MiddleName -> null 
EmailAddress -> [email protected], [email protected], [email protected] 
FirstName -> Toni, Mary, Paul 
LastName -> Barry, null ,White 
Id -> null 

說明了每個「值」項可以完全空或包含相同數量的值作爲具有更多條目(我不知道是哪個)的條目,即使有些條目爲空。

我希望它「轉」它像這樣的地圖列表:

MiddleName -> null 
EmailAddress -> [email protected] 
FirstName -> Toni 
LastName -> Barry 
Id -> null 

MiddleName -> null 
EmailAddress -> [email protected] 
FirstName -> John 
LastName -> null 
Id -> null 

MiddleName -> null 
EmailAddress -> [email protected] 
FirstName -> Paul 
LastName -> White 
Id -> null 

我試圖用java8流要做到這一點,但能做到這一點在「老」的風格,以及。

搜索stackoverflow我發現了一個類似的問題[1],還有一些相關的[2,3,4]給了我一些想法,但並不完全適應。現在

,我沒有實現我自己的解決方案,我想,但坦率地說,這是可能是最醜陋的代碼我多年來寫的作品...

List result = (...) 
map.forEach(h -> { 
    MultiValueMap<String, String> m = new LinkedMultiValueMap(); 
    h.entrySet().stream().forEach(e -> { 
     String[] ss = e.getValue() == null ? null : e.getValue().toString().split(","); 
     if (ss != null) { 
      Arrays.asList(ss).stream().forEach(s -> m.add(e.getKey(), s)); 
     } 
    }); 
    if (m.size() > 0) { 
     int x = m.values().stream().max((o1, o2) -> o1.size() - o2.size()).get().size(); 
     for (int i = 0; i < x; i++) { 
      Map<String, String> n = (Map) h.clone(); 
      Iterator<Map.Entry<String, String>> it = n.entrySet().iterator(); 
      while(it.hasNext()){ 
       Map.Entry<String, String> e = it.next(); 
       List<String> ss = m.get(e.getKey()); 
       if(ss!=null) { 
        e.setValue(ss.get(i)); 
       } 
      } 
      result.add(n); 
     } 
    } 
}); 

第一遍只是爲了分割字符串因爲它最初是逗號分隔的字符串然後我可以找到任意值的元素的最大數目,我將這些條目中的所有值循環該次數,併爲結果創建每個值的映射。坦率地說,我上週五寫了這段代碼,我不能正確閱讀它...

所以,這首先是一個簡單的事情,我結束了這個混亂,有沒有更好的方式來做它?

在此先感謝。

[1] Java8 streams : Transpose map with values as list

[2] reversing keys/values - create new instance of HashMap

[3] "Transpose" a hashmap for key->value to value->key?

[4] Java invert map

回答

0

我會坦率地儘量避免在首位這種情況是,並開始使用真實的物體而不是地圖(甚至你想要的地圖列表應該是一個List<Person>),但我會這樣做:

Map<String, List<String>> multiMap = ...; 
List<Map<String, String>> result = new ArrayList<>(); 

// find an entry with a non-null value, and get the size of the 
// list 
OptionalInt sizeOfLists = 
    multiMap.values() 
      .stream() 
      .filter(Objects::nonNull) 
      .mapToInt(List::size) 
      .findAny(); 

// for each index, create a person and put each key and the 
// corresponding value at that index in that map 
sizeOfLists.ifPresent(size -> { 
    for (int i = 0; i < size; i++) { 
     int index = i; 
     Map<String, String> person = new HashMap<>(); 
     result.add(person); 
     multiMap.entrySet() 
       .stream() 
       .filter(entry -> entry.getValue() != null) 
       .forEach(entry -> person.put(entry.getKey(), entry.getValue().get(index))); 
    } 
}); 

請注意,您的代碼並不那麼糟糕。但是,如果您給變量賦予有意義的名稱而不是h,m,e,ss,那麼它會更具可讀性。

+0

嗨,謝謝你的回覆。不幸的是,我無法避免這種情況,因爲這是更大的作業流程的一部分。你的代碼非常易讀,只要看看它就能理解它。我會嘗試一下,因爲我的實際情況比這更復雜一點。關於我的變量命名你當然是對的...... :)我會讓你知道它是怎麼回事。乾杯。 – amsmota

0

這個怎麼樣(如果我理解正確的一切):

Multimap<String, String> map = ArrayListMultimap.create(); 
    map.put("MiddleName", null); 
    map.putAll("EmailAddress", ImmutableList.of("[email protected]", "[email protected]", "[email protected]")); 

    // that's the key with the "biggest" collection within the map 
    int biggest = map.asMap().entrySet().stream().collect(Collectors.reducing(0, entry -> entry.getValue().size(), Integer::max)); 

    Multimap<String, String> newMap = ArrayListMultimap.create(); 

    // "padding" the collection when required 
    map.keySet().stream().forEach(key -> { 
     int currentSize = map.get(key).size(); 
     newMap.putAll(key, map.get(key)); 
     if (currentSize < biggest) { 
      newMap.putAll(key, Collections.nCopies(biggest - currentSize, (String) null)); 
     } 
    }); 

    System.out.println(newMap); // {MiddleName=[null, null, null], EmailAddress=[[email protected], [email protected], [email protected]]} 
} 

映射到一些Person對象是從這裏很容易。