使用Java将Map<String,List<String>>转换为List<Map<String,String>>(笛卡尔产品)。

问题描述 投票:0回答:2

我试图使用Java创建一个地图的笛卡尔乘积,其中包含了 String 钥匙和 List<String> 的值。我需要在创建列表时保留键值,就像这样。

  • 从一个 Map<String, List<String>> 对象
  • 至a List<Map<String, String>> 对象

我还要求,当一个 List<String> 原始地图中的值是空的,在创建笛卡尔积时仍应将其作为一个单一的值来处理,以避免乘以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
2个回答
1
投票

下面是一个简单的例子,如何将Map>转换为List&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());
    }
}


0
投票

不确定你是否可以用流的方式来做这个,因为它需要递归。

一个非流版本可以是

private static <T,T1> List<Map<T,T1>> createRuleListFromMap(Map<T, List<T1>> ruleMap) {
    //Make iterator
    Iterator<Map.Entry<T, List<T1>>> iterator = ruleMap.entrySet().iterator();
    //Run initial generate list with empty arrayList
    return generateList(new ArrayList<>(),iterator);

}

private static <T,T1> List<Map<T,T1>> generateList(List<Map<T,T1>> mapList,Iterator<Map.Entry<T, List<T1>>> iterator) {
    //If there are any entries still in map
    if(iterator.hasNext()){
        //Get next entry
        Map.Entry<T, List<T1>> entry = iterator.next();

        //This is where this iterations maps will go
        List<Map<T,T1>> newMapList = new ArrayList<>();

        //Where comination of previous + this iterations maps
        List<Map<T,T1>> combinedMapList = new ArrayList<>();

        //For every value in current entry
        //make into map and add to newMapList
        for(T1 t1 : entry.getValue()){
            Map<T,T1> newMap = new HashMap<>();
            newMap.put(entry.getKey(),t1);
            newMapList.add(newMap);
        }

        //if map list is still empty, due to previous loop not running, 
        //create single null entry
        if(newMapList.isEmpty()){
            Map<T,T1> newMap = new HashMap<>();
            newMap.put(entry.getKey(),null);
            newMapList.add(newMap);
        }

        //Combine previous map with current
        for (Map<T,T1> newMap : newMapList){
            for(Map<T,T1> map : mapList){
                Map<T,T1> combinedMap = new HashMap<>();
                combinedMap.putAll(map);
                combinedMap.putAll(newMap);
                combinedMapList.add(combinedMap);
            }
        }
        //Return combinedMap if not empty
        //otherwise return this iterations map
        //Only required for first iteration since above 
        //loop won't run whilst previous mapList is empty
        return generateList(combinedMapList.isEmpty() ? newMapList : combinedMapList,iterator);
    }
    //When no more elements return the mapList
    return mapList;
}

也许可以重构,但应该能提供一个基本的概念。

© www.soinside.com 2019 - 2024. All rights reserved.