I have a map of lists like:
Map("a" -> [1,2,3], "b" -> [4,5,6], "c" -> [7])
I need to produce:
[
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),
]
I am using a Java library called Vavr for my container types but I don't mind seeing the implementation done in any language.
You can first iterate through map entries and represent list elements as Map<String,Integer>
and get a stream of lists of maps , and then reduce
this stream to a single list.
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}
See also:
• Generate all possible string combinations by replacing the hidden #
number sign
• How to get all possible combinations from two arrays?
Here is a solution using recursion:
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;
}
Output:
[
{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}
]
Here is one way to do it:
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());
}
}
Output: (edited for formatting)
[
{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}
]
I ended up doing it differently to the answers here.
I already had a class for performing Cartesian Product like:
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;
}
}
So I did:
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))));
Although I'm sure there must be a cleaner way that doesn't involve mutation at all.
This is how I would solve this
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);
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.