2016-08-15 84 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对象是从这里很容易。