【问题标题】:Convert a map of lists into a list of maps将列表映射转换为映射列表
【发布时间】:2021-03-10 21:50:46
【问题描述】:

我有一张列表地图,例如:

Map("a" -> [1,2,3], "b" -> [4,5,6], "c" -> [7])

我需要制作:

[
Map("a" -> 1, "b" -> 4, "c" -> 7),
Map("a" -> 1, "b" -> 5, "c" -> 7),
Map("a" -> 1, "b" -> 6, "c" -> 7),
Map("a" -> 2, "b" -> 4, "c" -> 7),
Map("a" -> 2, "b" -> 5, "c" -> 7),
Map("a" -> 2, "b" -> 6, "c" -> 7),
Map("a" -> 3, "b" -> 4, "c" -> 7),
Map("a" -> 3, "b" -> 5, "c" -> 7),
Map("a" -> 3, "b" -> 6, "c" -> 7),
]

我正在为我的容器类型使用一个名为 Vavr 的 Java 库,但我不介意看到以任何语言完成的实现。

【问题讨论】:

    标签: java list dictionary combinations combinatorics


    【解决方案1】:

    您可以首先遍历地图条目并将列表元素表示为Map<String,Integer> 并获得一个地图列表流,然后reduce 这个单个列表。

    Try it online!

    Map<String, List<Integer>> mapOfLists = new LinkedHashMap<>();
    mapOfLists.put("a", List.of(1, 2, 3));
    mapOfLists.put("b", List.of(4, 5, 6));
    mapOfLists.put("c", List.of(7));
    
    List<Map<String, Integer>> listOfMaps = mapOfLists.entrySet().stream()
            // Stream<List<Map<String,Integer>>>
            .map(entry -> entry.getValue().stream()
                    // represent list elements as Map<String,Integer>
                    .map(element -> Map.of(entry.getKey(), element))
                    // collect a list of maps
                    .collect(Collectors.toList()))
            // intermediate output
            //[{a=1}, {a=2}, {a=3}]
            //[{b=4}, {b=5}, {b=6}]
            //[{c=7}]
            .peek(System.out::println)
            // reduce a stream of lists to a single list
            // by sequentially multiplying the list pairs
            .reduce((list1, list2) -> list1.stream()
                    // combinations of elements,
                    // i.e. maps, from two lists
                    .flatMap(map1 -> list2.stream()
                            .map(map2 -> {
                                // join entries of two maps
                                Map<String, Integer> map =
                                        new LinkedHashMap<>();
                                map.putAll(map1);
                                map.putAll(map2);
                                return map;
                            }))
                    // collect into a single list
                    .collect(Collectors.toList()))
            .orElse(null);
    
    // output
    listOfMaps.forEach(System.out::println);
    //{a=1, b=4, c=7}
    //{a=1, b=5, c=7}
    //{a=1, b=6, c=7}
    //{a=2, b=4, c=7}
    //{a=2, b=5, c=7}
    //{a=2, b=6, c=7}
    //{a=3, b=4, c=7}
    //{a=3, b=5, c=7}
    //{a=3, b=6, c=7}
    

    另见:
    Generate all possible string combinations by replacing the hidden # number sign
    How to get all possible combinations from two arrays?

    【讨论】:

      【解决方案2】:

      这是一个使用递归的解决方案:

      public static void main(String[] args) {
          Map<String, int[]> map = new HashMap<>();
          map.put("a", new int[]{1, 2, 3});
          map.put("b", new int[]{4, 5, 6});
          map.put("c", new int[]{7});
          List<Map<String, Integer>> combinations = new ArrayList<>();
          System.out.println(getCombinations(
                  map, combinations, 0,
                  new HashMap<String, Integer>(), map.values().size()));
      }
      
      private static List<Map<String, Integer>> getCombinations(
              Map<String, int[]> map, List<Map<String, Integer>> combinations, int i,
              Map<String, Integer> current, int len) {
          if (i >= len) {
              combinations.add(current);
          } else {
              String key = (String) map.keySet().toArray()[i];
              int[] value = map.get(key);
              for (int num : value) {
                  current.put(key, num);
                  getCombinations(map, combinations, i + 1,
                          new HashMap<>(current), len);
              }
          }
          return combinations;
      }
      

      输出:

      [
        {a=1, b=4, c=7}, {a=1, b=5, c=7}, {a=1, b=6, c=7}, 
        {a=2, b=4, c=7}, {a=2, b=5, c=7}, {a=2, b=6, c=7}, 
        {a=3, b=4, c=7}, {a=3, b=5, c=7}, {a=3, b=6, c=7}
      ]
      

      【讨论】:

        【解决方案3】:

        这是一种方法:

        public static void main(String[] args) {
            Map<String, List<Integer>> map = new HashMap<>();
            map.put("a", Arrays.asList(1, 2, 3));
            map.put("b", Arrays.asList(4, 5, 6));
            map.put("c", Arrays.asList(7));
        
            List<Map.Entry<String, List<Integer>>> list =
                    new ArrayList<>(map.entrySet());
        
            List<Map<String, Integer>> result = new ArrayList<>();
            combine(list, 0, new HashMap<>(), result);
            System.out.println(result);
        }
        
        static void combine(List<Map.Entry<String, List<Integer>>> list, int n,
                            Map<String, Integer> part,
                            List<Map<String, Integer>> result) {
            if (n >= list.size()) {
                result.add(new HashMap<>(part));
            } else {
                Map.Entry<String, List<Integer>> e = list.get(n);
                for (Integer i : e.getValue()) {
                    part.put(e.getKey(), i);
                    combine(list, n + 1, part, result);
                }
                part.remove(e.getKey());
            }
        }
        

        输出:(为格式化而编辑)

        [
          {a=1, b=4, c=7}, {a=1, b=5, c=7}, {a=1, b=6, c=7},
          {a=2, b=4, c=7}, {a=2, b=5, c=7}, {a=2, b=6, c=7},
          {a=3, b=4, c=7}, {a=3, b=5, c=7}, {a=3, b=6, c=7}
        ]
        

        【讨论】:

          【解决方案4】:

          我最终的做法与这里的答案不同。

          我已经有一门用于执行笛卡尔积的课程,例如:

          public class CartesianProduct {
              public static <T> Seq<Seq<T>> on(Seq<Seq<T>> sets) {
                  if (sets.size() >= 1) {
                      var javaList = sets.map(Value::toJavaList).toJavaList();
                      var out = computeCombinations(javaList);
                      return Vector.ofAll(out.stream().map(Vector::ofAll));
                  }
                  return Vector.empty();
              }
          
              private static <T> List<List<T>> appendElements(
                      List<List<T>> combinations, List<T> extraElements) {
                  return combinations.stream()
                          .flatMap(oldCombination ->
                                  extraElements.stream().map(extra -> {
                                      List<T> combinationWithExtra =
                                              new ArrayList<>(oldCombination);
                                      combinationWithExtra.add(extra);
                                      return combinationWithExtra;
                                  }))
                          .collect(Collectors.toList());
              }
          
              private static <T> List<List<T>> computeCombinations(List<List<T>> lists) {
                  List<List<T>> currentCombinations = List.of(List.of());
                  for (List<T> list : lists) {
                      currentCombinations = appendElements(currentCombinations, list);
                  }
                  return currentCombinations;
              }
          }
          

          所以我做到了:

          Seq<Seq<Integer>> allValuesProduct = CartesianProduct.on(mapOfLists.values());
          Seq<Map<String, Integer>> listOfMaps =
                  allValuesProduct.map(values -> HashMap.tabulate(
                          values.length(),
                          i -> Tuple.of(
                                  mapOfLists.keySet().toList().get(i),
                                  values.get(i))));
          

          虽然我确信一定有一种更干净的方法,根本不涉及突变。

          【讨论】:

            【解决方案5】:

            这就是我要解决的方法

            import java.util.ArrayList;
            import java.util.List;
            import java.util.Map;
            import java.util.TreeMap;
            
            public class molToLom {
            
                public static void main(String[] args) {
                    //This is what we have: A map of lists (mol)
                    TreeMap<String, List<Integer>> mol = new TreeMap<String, List<Integer>>();
                    mol.put("a", List.of(1, 2, 3));
                    mol.put("b", List.of(4, 5, 6));
                    mol.put("c", List.of(7));
                    //we want a list of maps (lom)
                    ArrayList<Map<String, Integer>> lom = new ArrayList<>();
                    //Add an empty element. We'll need that later
                    lom.add(new TreeMap<String, Integer>());
                    //Now run through our map
                    mol.forEach((tag, list) -> {
                        //We rebuild our lom on every iteration. Otherwise we would get incomplete maps in our list
                        ArrayList<Map<String, Integer>> tLom = new ArrayList<>();
                        //Run through our list of maps and ...
                        lom.forEach(map -> {
                            //... combine every element in this list with every element in the list from our initial map entry
                            list.forEach(i -> {
                                Map<String, Integer> m = new TreeMap<>(map);
                                m.put(tag, i);
                                tLom.add(m);
                            });
                        });
                        //replace our last lom with the newly created one. Could be lom = tLom if lom was a field
                        lom.clear();
                        lom.addAll(tLom);
                    });
                    System.out.println(lom);
                }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-02-16
              • 1970-01-01
              • 1970-01-01
              • 2011-10-23
              • 2021-05-10
              • 2022-10-02
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多