【问题标题】:How to transform a Map<String, List<String>> into a List<Map<String, String>> and get Cartesian product?如何将 Map<String, List<String>> 转换为 List<Map<String, String>> 并获得笛卡尔积?
【发布时间】:2021-04-25 23:47:01
【问题描述】:

使用 Java 我正在尝试创建包含 String 键和 List&lt;String&gt; 值的映射的笛卡尔积。创建列表时,我需要保留键的值,如下所示:

  • 来自Map&lt;String, List&lt;String&gt;&gt; 对象
  • List&lt;Map&lt;String, String&gt;&gt; 对象

我还要求当原始映射中的List&lt;String&gt; 值为空时,在创建笛卡尔积时仍应将其视为单个值,以避免乘以 0 并且不创建映射。比如下面的地图:

{
    "Location Number" = {"100", "500"}
    "Department Number" = {"11", "22", "33"}
    "District Number" = {}
    "Preferred Language" = {"en-US"}
}

翻译成:

{
    {
        "Location Number" = "100"
        "Department Number" = "11"
        "District Number" = {}
        "Preferred Language" = "en-US"
    },
    {
        "Location Number" = "100"
        "Department Number" = "22"
        "District Number" = {}
        "Preferred Language" = "en-US"
    },
    {
        "Location Number" = "100"
        "Department Number" = "33"
        "District Number" = {}
        "Preferred Language" = "en-US"
    },
    {
        "Location Number" = "500"
        "Department Number" = "11"
        "District Number" = {}
        "Preferred Language" = "en-US"
    },
    {
        "Location Number" = "500"
        "Department Number" = "22"
        "District Number" = {}
        "Preferred Language" = "en-US"
    },
    {
        "Location Number" = "500"
        "Department Number" = "33"
        "District Number" = {}
        "Preferred Language" = "en-US"
    }
}

下面是我目前用来完成类似翻译的代码,但它没有保留我需要的密钥。我不知道这是否可以在一般情况下使用 Java 8 Streams 来完成。

private static List<List<String>> createRuleListFromMap(Map<String, List<String>> ruleMap) {
    List<List<String>> ruleList = new ArrayList<>();
    cartesianProduct(ruleMap.values()).forEach(ruleList::add);
    return ruleList;
}

private static <T> Stream<List<T>> cartesianProduct(Collection<? extends Collection<T>> collections) {
    return cartesianProduct(new ArrayList<Collection<T>>(collections), Collections.emptyList());
}

private static <T> Stream<List<T>> cartesianProduct(List<? extends Collection<T>> collections, List<T> current) {
    return collections.isEmpty() ? Stream.of(current) : collections.get(0).stream().flatMap(e -> {
        List<T> list = new ArrayList<>(current);
        list.add(e);
        return cartesianProduct(collections.subList(1, collections.size()), list);
    });
}

【问题讨论】:

    标签: java list dictionary java-stream cartesian-product


    【解决方案1】:

    以下是如何将Map&lt;String,List&lt;String&gt;&gt; 转换为List&lt;Map&lt;String,String&gt;&gt; 的简单示例:

    import lombok.extern.slf4j.Slf4j;
    import org.junit.Test;
    
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    @Slf4j
    public class StreamTest {
        @Test
        public void test() {
            var map = new HashMap<String, List<String>>();
            map.put("key1", Arrays.asList("value1", "value2", "value3"));
            map.put("key2", Arrays.asList("value4", "value5", "value6"));
    
            var list = map.entrySet().stream()
                    .flatMap(e -> e.getValue().stream()
                            .map(v -> Map.of(e.getKey(), v)))
                    .collect(Collectors.toList());
    
            log.info(list.toString());
        }
    }
    

    【讨论】:

    • 这为每个条目提供了一个映射(键映射到条目列表中的元素),而不是笛卡尔积。
    【解决方案2】:

    您可以先将列表的元素表示为Map&lt;String,String&gt;,然后获取地图列表流,然后将reduce 这个流转换为单个列表 em> 通过对内部映射按顺序求和得到笛卡尔积

    Try it online!

    Map<String, List<String>> map = new LinkedHashMap<>();
    map.put("Location Number", List.of("100", "500"));
    map.put("Department Number", List.of("11", "22", "33"));
    map.put("District Number", List.of("")); // NOT an empty list!
    map.put("Preferred Language", List.of("en-US"));
    
    List<Map<String, String>> list = map.entrySet().stream()
            .map(entry -> entry.getValue().stream()
                    // list elements as Map<String,String>
                    .map(str -> Map.of(entry.getKey(), str))
                    // List<Map<String,String>>
                    .collect(Collectors.toList()))
            // intermediate output
            .peek(System.out::println)
            // stream of lists to a single list
            .reduce((list1, list2) -> list1.stream()
                    // combinations of inner maps
                    .flatMap(map1 -> list2.stream()
                            // join entries of two maps
                            .map(map2 -> {
                                Map<String, String> mp =
                                        new LinkedHashMap<>();
                                mp.putAll(map1);
                                mp.putAll(map2);
                                return mp;
                            }))
                    // list of combinations
                    .collect(Collectors.toList()))
            // returns List<Map<String,String>
            // otherwise an empty list
            .orElse(Collections.emptyList());
    
    // final output
    list.forEach(System.out::println);
    

    中间输出:

    [{Location Number=100}, {Location Number=500}]
    [{Department Number=11}, {Department Number=22}, {Department Number=33}]
    [{District Number=}]
    [{Preferred Language=en-US}]
    

    最终输出:

    {Location Number=100, Department Number=11, District Number=, Preferred Language=en-US}
    {Location Number=100, Department Number=22, District Number=, Preferred Language=en-US}
    {Location Number=100, Department Number=33, District Number=, Preferred Language=en-US}
    {Location Number=500, Department Number=11, District Number=, Preferred Language=en-US}
    {Location Number=500, Department Number=22, District Number=, Preferred Language=en-US}
    {Location Number=500, Department Number=33, District Number=, Preferred Language=en-US}
    

    另见:Convert a map of lists into a list of maps

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-25
      • 2016-07-21
      • 2020-04-01
      • 1970-01-01
      • 2015-11-21
      • 2021-12-21
      • 1970-01-01
      相关资源
      最近更新 更多