根据键将映射中的值相互合并
Merge values from values in a map with each other based on key
我被这个奇怪的问题卡住了 2 个小时(我很笨)。
我有一张地图,其值类似于 <String,Set>
- A = 1,2,3
- B = 4,5
- C=6
我正在寻找的输出是
- A=1&B=4&C=6
- A=1&B=5&C=6
- A=2&B=4&C=6
- A=2&B=5&C=6
- A=3&B=4&C=6
- A=3&B=5&C=6
到目前为止我做了什么:-
- 根据第一个键遍历映射并将值存储在数组中
- 遍历第二个键,但我不确定如何将第一个结果的值合并到这个结果中。
- 忘记第三个和后续密钥。
这听起来像是一个非常愚蠢的问题,但我无法弄清楚。我试图避免到达这里的场景,但我无法改变!!
请帮忙。
注意:- 我也使用了 multiset,但它接受了值的重复值,所以不得不使用 set.Hence 以 map<String,Set>
结尾。
下面的代码可以处理任意大小的 Map,空 除外,以及任意大小的每个 Set,空 除外。空 Map/Set 将引发错误。
当然,扩展代码以处理 empty 应该相当简单,一旦您定义了预期的输出。
Map<String, Set<String>> map = new LinkedHashMap<>();
map.put("A", new LinkedHashSet<>(Arrays.asList("1", "2", "3")));
map.put("B", new LinkedHashSet<>(Arrays.asList("4", "5")));
map.put("C", new LinkedHashSet<>(Arrays.asList("6")));
List<String> result = null;
for (Map.Entry<String, Set<String>> entry : map.entrySet()) {
String key = (result == null ? "" : "&") + entry.getKey() + "=";
Set<String> values = entry.getValue();
if (values.isEmpty())
throw new IllegalArgumentException("Empty set not supported");
List<String> crossJoin = new ArrayList<>((result == null ? 1 : result.size()) * values.size());
if (result == null)
for (String value : values)
crossJoin.add(key + value);
else
for (String left : result)
for (String value : values)
crossJoin.add(left + key + value);
result = crossJoin;
}
if (result == null)
throw new IllegalArgumentException("Empty map not supported");
for (String value : result)
System.out.println(value);
输出
A=1&B=4&C=6
A=1&B=5&C=6
A=2&B=4&C=6
A=2&B=5&C=6
A=3&B=4&C=6
A=3&B=5&C=6
如果您的代码保留了一段时间并且您可能想要更改格式或对空案例的处理,那么这里的答案更易于阅读并且更易于维护。它自然地处理具有空映射和没有一组允许值的键的情况。
我假设顺序对你来说不重要,但你可以用列表替换集合以获得更多 predictable/consistent 顺序。
/**
* A nested pair class to handle the mappings like A:1.
*/
private static class Pair{
public Pair(String k,String v){
this.k = k;
this.v = v;
}
String k;
String v;
public String toString(){
return k+"="+v;
}
}
/**
* Given a set like
* {{A:"s1",B:"s4","C:s5"}
* {A:"s1",B:"s4","C:s6"}
* {A:"s2",B:"s4","C:s5"}
* {A:"s2",B:"s4","C:s6"}
* {A:"s3",B:"s4","C:s5"}
* {A:"s3",B:"s4","C:s6"}}
*
* Converts the contents to string like:
* A=s1&B=s4&C=s5
* A=s1&B=s4&C=s6
* A=s2&B=s4&C=s5
* A=s2&B=s4&C=s6
* A=s3&B=s4&C=s5
* A=s3&B=s4&C=s6
* @param pairCombinations
* @return
*/
public static String format(Set<Set<Pair>> pairCombinations){
String result = "";
for(Set<Pair> pairSet : pairCombinations){
result += formatPairSet(pairSet)+"\n";
}
return result;
}
private static String formatPairSet(Set<Pair> pairSet) {
String result = "";
boolean isFirst = true;
for(Pair pair : pairSet){
result+= (isFirst? "" : "&");
result += pair.k+"="+pair.v;
isFirst = false;
}
return result;
}
/**
* Given a map of the form:
* A:{"s1","s2","s3"}
* B:{"s4"}
* C:{"s5","s6"}
*
* Will return all possible combinations of pairs in
* the map as:
* {{A:"s1",B:"s4","C:s5"}
* {A:"s1",B:"s4","C:s6"}
* {A:"s2",B:"s4","C:s5"}
* {A:"s2",B:"s4","C:s6"}
* {A:"s3",B:"s4","C:s5"}
* {A:"s3",B:"s4","C:s6"}}
* @param map
* @return
*/
public static Set<Set<Pair>> getCombinations(Map<String,Set<String>> map){
Set<Set<Pair>> combinations = new HashSet<Set<Pair>>();
if(map.entrySet().isEmpty()){
return combinations;
}
combinations.add(new HashSet<Pair>());
Set<Pair> pairsForKey = null;
for(Entry<String,Set<String>> mapEntry : map.entrySet()){
//Ex: {C:"s5",C:"s6"}
pairsForKey = getPairs(mapEntry);
//Ex: {{A:s1,B:s4},{A:s2,B:s4},{A:s3,B:s4}}
combinations = join(combinations,pairsForKey);
}
return combinations;
}
private static Set<Pair> getPairs(Entry<String, Set<String>> kWithValidVs) {
Set<Pair> pairs = new HashSet<Pair>();
for(String v : kWithValidVs.getValue()){
pairs.add(new Pair(kWithValidVs.getKey(),v));
}
return pairs;
}
/**
* Given a set of combinations like:
* {{A:s1,B:s4},{A:s2,B:s4},{A:s3,B:s4}} and a set to join with it like {C:s5,C:s6},
* will join the set to the existing combinations like:
* {{A:s1,B:s4,C:s5},{A:s2,B:s4,C:s5},{A:s3,B:s4,C:s5},{A:s1,B:s4,C:s6},{A:s2,B:s4,A:s6},{A:s3,B:s4,C:s6}}
* @param combinations
* @param set
* @return
*/
private static Set<Set<Pair>> join(Set<Set<Pair>> combinations,
Set<Pair> set) {
Set<Set<Pair>> join = new HashSet<Set<Pair>>();
//comment this 'if' if you want keys with empty lists to eliminate all combos.
if(set.isEmpty()){
return combinations;
}
for(Set<Pair> combo : combinations){
for(Pair pair : set){
Set<Pair> newCombo = new HashSet<Pair>();
newCombo.addAll(combo);
newCombo.add(pair);
join.add(newCombo);
}
}
return join;
}
public static void main(String[] args){
Map<String,Set<String>> map = new HashMap<String,Set<String>>();
Set<String> aVals = new HashSet<String>();
aVals.add("1");
aVals.add("2");
aVals.add("3");
Set<String> bVals = new HashSet<String>();
bVals.add("4");
Set<String> cVals = new HashSet<String>();
cVals.add("5");
cVals.add("6");
map.put("A", aVals);
map.put("B", bVals);
map.put("C", cVals);
Set<Set<Pair>> combinations =getCombinations(map);
String printableResult = format(combinations);
System.out.print(printableResult);
}
输出:
A=2&B=4&C=6
A=2&B=4&C=5
A=1&B=4&C=6
B=4&C=5&A=3
B=4&A=3&C=6
A=1&B=4&C=5
我被这个奇怪的问题卡住了 2 个小时(我很笨)。
我有一张地图,其值类似于 <String,Set>
- A = 1,2,3
- B = 4,5
- C=6
我正在寻找的输出是
- A=1&B=4&C=6
- A=1&B=5&C=6
- A=2&B=4&C=6
- A=2&B=5&C=6
- A=3&B=4&C=6
- A=3&B=5&C=6
到目前为止我做了什么:-
- 根据第一个键遍历映射并将值存储在数组中
- 遍历第二个键,但我不确定如何将第一个结果的值合并到这个结果中。
- 忘记第三个和后续密钥。
这听起来像是一个非常愚蠢的问题,但我无法弄清楚。我试图避免到达这里的场景,但我无法改变!!
请帮忙。
注意:- 我也使用了 multiset,但它接受了值的重复值,所以不得不使用 set.Hence 以 map<String,Set>
结尾。
下面的代码可以处理任意大小的 Map,空 除外,以及任意大小的每个 Set,空 除外。空 Map/Set 将引发错误。
当然,扩展代码以处理 empty 应该相当简单,一旦您定义了预期的输出。
Map<String, Set<String>> map = new LinkedHashMap<>();
map.put("A", new LinkedHashSet<>(Arrays.asList("1", "2", "3")));
map.put("B", new LinkedHashSet<>(Arrays.asList("4", "5")));
map.put("C", new LinkedHashSet<>(Arrays.asList("6")));
List<String> result = null;
for (Map.Entry<String, Set<String>> entry : map.entrySet()) {
String key = (result == null ? "" : "&") + entry.getKey() + "=";
Set<String> values = entry.getValue();
if (values.isEmpty())
throw new IllegalArgumentException("Empty set not supported");
List<String> crossJoin = new ArrayList<>((result == null ? 1 : result.size()) * values.size());
if (result == null)
for (String value : values)
crossJoin.add(key + value);
else
for (String left : result)
for (String value : values)
crossJoin.add(left + key + value);
result = crossJoin;
}
if (result == null)
throw new IllegalArgumentException("Empty map not supported");
for (String value : result)
System.out.println(value);
输出
A=1&B=4&C=6
A=1&B=5&C=6
A=2&B=4&C=6
A=2&B=5&C=6
A=3&B=4&C=6
A=3&B=5&C=6
如果您的代码保留了一段时间并且您可能想要更改格式或对空案例的处理,那么这里的答案更易于阅读并且更易于维护。它自然地处理具有空映射和没有一组允许值的键的情况。
我假设顺序对你来说不重要,但你可以用列表替换集合以获得更多 predictable/consistent 顺序。
/**
* A nested pair class to handle the mappings like A:1.
*/
private static class Pair{
public Pair(String k,String v){
this.k = k;
this.v = v;
}
String k;
String v;
public String toString(){
return k+"="+v;
}
}
/**
* Given a set like
* {{A:"s1",B:"s4","C:s5"}
* {A:"s1",B:"s4","C:s6"}
* {A:"s2",B:"s4","C:s5"}
* {A:"s2",B:"s4","C:s6"}
* {A:"s3",B:"s4","C:s5"}
* {A:"s3",B:"s4","C:s6"}}
*
* Converts the contents to string like:
* A=s1&B=s4&C=s5
* A=s1&B=s4&C=s6
* A=s2&B=s4&C=s5
* A=s2&B=s4&C=s6
* A=s3&B=s4&C=s5
* A=s3&B=s4&C=s6
* @param pairCombinations
* @return
*/
public static String format(Set<Set<Pair>> pairCombinations){
String result = "";
for(Set<Pair> pairSet : pairCombinations){
result += formatPairSet(pairSet)+"\n";
}
return result;
}
private static String formatPairSet(Set<Pair> pairSet) {
String result = "";
boolean isFirst = true;
for(Pair pair : pairSet){
result+= (isFirst? "" : "&");
result += pair.k+"="+pair.v;
isFirst = false;
}
return result;
}
/**
* Given a map of the form:
* A:{"s1","s2","s3"}
* B:{"s4"}
* C:{"s5","s6"}
*
* Will return all possible combinations of pairs in
* the map as:
* {{A:"s1",B:"s4","C:s5"}
* {A:"s1",B:"s4","C:s6"}
* {A:"s2",B:"s4","C:s5"}
* {A:"s2",B:"s4","C:s6"}
* {A:"s3",B:"s4","C:s5"}
* {A:"s3",B:"s4","C:s6"}}
* @param map
* @return
*/
public static Set<Set<Pair>> getCombinations(Map<String,Set<String>> map){
Set<Set<Pair>> combinations = new HashSet<Set<Pair>>();
if(map.entrySet().isEmpty()){
return combinations;
}
combinations.add(new HashSet<Pair>());
Set<Pair> pairsForKey = null;
for(Entry<String,Set<String>> mapEntry : map.entrySet()){
//Ex: {C:"s5",C:"s6"}
pairsForKey = getPairs(mapEntry);
//Ex: {{A:s1,B:s4},{A:s2,B:s4},{A:s3,B:s4}}
combinations = join(combinations,pairsForKey);
}
return combinations;
}
private static Set<Pair> getPairs(Entry<String, Set<String>> kWithValidVs) {
Set<Pair> pairs = new HashSet<Pair>();
for(String v : kWithValidVs.getValue()){
pairs.add(new Pair(kWithValidVs.getKey(),v));
}
return pairs;
}
/**
* Given a set of combinations like:
* {{A:s1,B:s4},{A:s2,B:s4},{A:s3,B:s4}} and a set to join with it like {C:s5,C:s6},
* will join the set to the existing combinations like:
* {{A:s1,B:s4,C:s5},{A:s2,B:s4,C:s5},{A:s3,B:s4,C:s5},{A:s1,B:s4,C:s6},{A:s2,B:s4,A:s6},{A:s3,B:s4,C:s6}}
* @param combinations
* @param set
* @return
*/
private static Set<Set<Pair>> join(Set<Set<Pair>> combinations,
Set<Pair> set) {
Set<Set<Pair>> join = new HashSet<Set<Pair>>();
//comment this 'if' if you want keys with empty lists to eliminate all combos.
if(set.isEmpty()){
return combinations;
}
for(Set<Pair> combo : combinations){
for(Pair pair : set){
Set<Pair> newCombo = new HashSet<Pair>();
newCombo.addAll(combo);
newCombo.add(pair);
join.add(newCombo);
}
}
return join;
}
public static void main(String[] args){
Map<String,Set<String>> map = new HashMap<String,Set<String>>();
Set<String> aVals = new HashSet<String>();
aVals.add("1");
aVals.add("2");
aVals.add("3");
Set<String> bVals = new HashSet<String>();
bVals.add("4");
Set<String> cVals = new HashSet<String>();
cVals.add("5");
cVals.add("6");
map.put("A", aVals);
map.put("B", bVals);
map.put("C", cVals);
Set<Set<Pair>> combinations =getCombinations(map);
String printableResult = format(combinations);
System.out.print(printableResult);
}
输出:
A=2&B=4&C=6
A=2&B=4&C=5
A=1&B=4&C=6
B=4&C=5&A=3
B=4&A=3&C=6
A=1&B=4&C=5