我遇到了一个简单的任务。我想要做的是将Map<K,Set<V>>
转换为List<Map<K,V>>
获取所有可能的组合:
Map {
{'k1' => set{'v11', 'v12'}},
{'k2' => set{'v21', 'v22', 'v23'}},
{'k3' => set{'v31'}}
}
预期结果:
List
{
Map{'k1'=>'v11', 'k2'=>'v21', 'k3'=>'v31'},
Map{'k1'=>'v11', 'k2'=>'v22', 'k3'=>'v31'},
Map{'k1'=>'v11', 'k2'=>'v23', 'k3'=>'v31'},
Map{'k1'=>'v12', 'k2'=>'v21', 'k3'=>'v31'},
Map{'k1'=>'v12', 'k2'=>'v22', 'k3'=>'v31'},
Map{'k1'=>'v12', 'k2'=>'v23', 'k3'=>'v31'}
}
对我感到羞耻,需要你的帮助。
为downvoters更新
我不是一个要求社区为我做功课的学生,我不会问在经过一周的努力之后我是否真的不需要帮助。好吧,原则上我会在符合条件的情况下立即为此问题分配全部代表赏金(800左右)。
更新2
在我之前的更新中尊重我的承诺,我开始了两个赏金的序列,其中最大的一个归于我的救世主,剩下的就是对其他可能的解决方案的奖励。
更新3
这是技术性的,交付承诺的消息(请参阅之前的更新)。我要感谢所有SO社区在真正需要时提供帮助。非常特别感谢“Tudor”和“Ed Staub”及时参与和响应。第二个赏金将分配给最优雅的解决方案,为新人和每个感兴趣的人提供良好的机会。
答案 0 :(得分:6)
使用递归!因此,在递归的每个级别,您都会查看地图keyset()
中的另一个键。您可以在该Set<V>
的迭代添加元素中添加要添加到列表中的当前地图。
你可以把它想象成一棵树。在根节点,您有一个空列表。然后树i
的每个后续级别表示从i
集合中选择哪个元素。
以下是代码以及包含测试用例的主要方法:
import java.util.*;
public class Test {
// method called to generate combinations using map, putting the combinations in list
public static <K,V> void combinations( Map<K,Set<V>> map, List<Map<K,V>> list ) {
recurse( map, new LinkedList<K>( map.keySet() ).listIterator(), new HashMap<K,V>(), list );
}
// helper method to do the recursion
private static <K,V> void recurse( Map<K,Set<V>> map, ListIterator<K> iter, Map<K,V> cur, List<Map<K,V>> list ) {
// we're at a leaf node in the recursion tree, add solution to list
if( !iter.hasNext() ) {
Map<K,V> entry = new HashMap<K,V>();
for( K key : cur.keySet() ) {
entry.put( key, cur.get( key ) );
}
list.add( entry );
} else {
K key = iter.next();
Set<V> set = map.get( key );
for( V value : set ) {
cur.put( key, value );
recurse( map, iter, cur, list );
cur.remove( key );
}
iter.previous();
}
}
public static void main( String[] args ) {
Map<Integer,Set<Integer>> map = new HashMap<Integer,Set<Integer>>() {{
put( 1, new HashSet<Integer>() {{
add( 11 );
add( 12 );
}} );
put( 2, new HashSet<Integer>() {{
add( 21 );
add( 22 );
add( 23 );
}} );
put( 3, new HashSet<Integer>() {{
add( 31 );
}} );
}};
List<Map<Integer,Integer>> list = new LinkedList<Map<Integer,Integer>>();
combinations( map, list );
for( Map<Integer,Integer> combination : list ) {
System.out.println( combination );
}
}
}
答案 1 :(得分:2)
提示:使用递归生成组合“树”。
编辑:好的,我有空闲时间,并决定试一试:
/**
*
* @param <K> The type of the key
* @param <V> The type of the value
* @param index The index of the current key to inspect
* @param current The current map being built by recursion
* @param map The original input
* @param list The result
*/
public static <K, V> void Combine(int index, Map<K, V> current,
Map<K, Set<V>> map,
List<Map<K, V>> list) {
if(index == map.size()) { // if we have gone through all keys in the map
Map<K, V> newMap = new HashMap<K, V>();
System.out.println(current);
for(K key: current.keySet()) { // copy contents to new map.
newMap.put(key, current.get((K)key));
}
list.add(newMap); // add to result.
} else {
Object currentKey = map.keySet().toArray()[index]; // take the current key
for(V value: map.get(currentKey)) {
current.put((K)currentKey, value); // put each value into the temporary map
Combine(index + 1, current, map, list); // recursive call
current.remove(currentKey); // discard and try a new value
}
}
}
我已经测试了几个案例,我认为这是正确的。让我知道。
您可以从仅采用map
作为输入的其他方法调用此方法,创建默认参数index
,current
和list
并返回list
作为输出。< / p>
答案 2 :(得分:1)
非常更新。
这是Guava头的解决方案。如果番石榴不是你的东西,它应该很容易转换。这个不是递归的。它输出一对字符串对的列表,我认为这是你真正想要的。
static ImmutableMap<String,ImmutableSet<String>> input = ImmutableMap.of(
"k1", ImmutableSet.of("v11", "v12"),
"k2", ImmutableSet.of("v21", "v22", "v23"),
"k3", ImmutableSet.of("v31", "v32", "v33", "v34"));
static class Pair<T,U>
{
T left; U right;
static <T,U> Pair of(T l, U r)
{ return new Pair(l, r); }
Pair(T l, U r) { left = l; right = r; }
}
static List<Pair<Pair<String, String>,Pair<String, String>>> transform(Map<String,ImmutableSet<String>> input)
{
List<Pair<Pair<String, String>,Pair<String, String>>> output = Lists.newArrayList();
// Not a real copy - very cheap.
ImmutableList<Map.Entry<String, ImmutableSet<String>>> entryList = ImmutableList.copyOf(input.entrySet());
for ( int i = 0; i < entryList.size(); i++ )
{
final Map.Entry<String, ? extends Set<String>> leftEntry = entryList.get(i);
for ( int j = i+1; j < entryList.size(); j++ )
{
final Map.Entry<String, ? extends Set<String>> rightEntry = entryList.get(j);
for ( String v1 : leftEntry.getValue() )
for ( String v2 : rightEntry.getValue() )
output.add(Pair.of(
Pair.of(leftEntry.getKey(), v1),
Pair.of(rightEntry.getKey(), v2)));
}
}
return output;
}
答案 3 :(得分:0)
首先感谢您提供的案例和提供的实施!我们遇到了同样的问题,为我们节省了大量时间!我们留下了一个python实现,对于那些可能用另一种语言来处理相同问题的人:)
def combinations(themap, maplist):
return recurse(themap, themap.keys(), {}, maplist, 0)
def recurse(themap, iterator, curmap, maplist, iteratoridx):
if(iteratoridx>=len(iterator)):
entry = {}
for key in curmap.keys():
entry[key]=curmap[key]
maplist.append(entry)
else:
key = iterator[iteratoridx]
theset = themap.get(key)
for value in theset:
curmap[key]=value
recurse(themap, iterator, curmap, maplist, iteratoridx+1)
curmap.pop(key, None)
iteratoridx=iteratoridx-1
maplist=[]
grid={"A":[1, 2, 3], "B":[4, 5, 6], "C":[7, 8, 9]}
combinations(grid, maplist)
print(maplist)
答案 4 :(得分:-1)
假设我们有变量&#34; source&#34;从类型Map<K,Set<V>>
将源转换为所需类型,您可以使用下一个代码:
List<Map<K,V>> target = new ArrayList<Map<K,V>>();
for(K key : source.keySet()){
Map<K,V> map = new HashMap<K, V>();
for(V val : source.get(key)){
map.put(key, val)
}
target.add(map);
}
答案 5 :(得分:-1)
List<Map<K,V>> target = new ArrayList<Map<K,V>>();
for(K key : source.keySet()){
for(V val : source.get(key)){
Map<K,V> map = new HashMap<K, V>();
map.put(key, val)
target.add(map);
}
}