Recursively or iteratively retrieve key-value combinations from a HashMap
Asked Answered
F

3

2

I want to retrieve k,v-pairs from a HashMap. The entrys are like this:

a = 3,4
b = 5,6

and so on. I need combinations of these values.

a=3, b=5
a=3, b=6
a=4, b=5
a=4, b=6

I don't know how many keys and how many entrys the values have. With entrySet I can get the values but not combinations. It looks like recursion but how?

Here's my code:

HashMap<String, String[]> map = new HashMap<String, String[]>();

BufferedReader file = new BufferedReader(new FileReader("test.txt"));
String str;

while ((str = file.readLine()) != null) {
    
    // ... logic
    
    map.put(key, value);
}

System.out.println("number of keys: " + map.size());
for (Map.Entry<String, String[]> entry : map.entrySet()) {
    for (String value : entry.getValue()) {
        System.out.println(entry.getKey() + ": " + value);
    }
}
file.close();
Fascista answered 16/3, 2011 at 9:0 Comment(2)
I'm not clear: is "a" your key and "3" (from a's list) your value, or is "3" your key from a and "5" your value from b?Sama
a is the key, 3 and 4 are strings. therefore "String[]". the value is String[]Fascista
P
5

You can try the following code:

public void mapPermute(Map<String, String[]> map, String currentPermutation) {
    String key = map.keySet().iterator().next(); // get the topmost key

    // base case
    if (map.size() == 1) {          
        for (String value : map.get(key)) {
            System.out.println(currentPermutation + key + "=" + value);
        }
    } else {
        // recursive case
        Map<String, String[]> subMap = new HashMap<String, String[]>(map);

        for (String value : subMap.remove(key)) {
            mapPermute(subMap, currentPermutation + key + "=" + value + ", ");
        }
    }
}

No guarantees on memory efficiency or speed. If you want to preserve the order of the keys in the map, you will have to pass in a TreeMap and change the code to use a TreeMap under the recursive case.

As the base case suggests, I'm assuming you have at least one entry in your map.

Pulchi answered 16/3, 2011 at 10:25 Comment(0)
S
0

It looks to me like you really want a MultiMap. In particular, ArrayListMultimap allows duplicate entries:

ArrayListMultimap<String, String> map = ArrayListMultimap.create();

for each line in file:
    parse key k
    for each value in line:
        parse value v
        map.put(k, v);

for (Map.Entry<String, String> entry : map.entries()) {
    String key = entry.getKey();
    String value = entry.getValue();
}

If you want a cartesian product of maps, you could compute that directly using recursion, or you could iterate over the maps: create a list of iterators and iterate odometer-style; when iterator N reaches its end, advance iterator N+1 and reset iterators 1..N.


Just poked around and found this SO question.

So I'd recommend you use guava's Sets.cartesianProduct for the cartesian product. Here's my poking around code, which you could adapt to your input logic:

String key1 = "a";
Set<Integer> values1 = Sets.newLinkedHashSet(Arrays.asList(1, 2, 3, 4));
String key2 = "b";
Set<Integer> values2 = Sets.newLinkedHashSet(Arrays.asList(5, 6, 7));
String key3 = "c";
Set<Integer> values3 = Sets.newLinkedHashSet(Arrays.asList(8, 9));

List<String> keys = Arrays.asList(key1, key2, key3);
Set<List<Integer>> product = Sets.cartesianProduct(values1, values2, values3);
for (List<Integer> values : product) {
    for (int i = 0; i < keys.size(); ++i) {
        String key = keys.get(i);
        int value = values.get(i);
        System.out.print(key + "=" + value + "; ");
    }
    System.out.println();
}
Sama answered 16/3, 2011 at 9:9 Comment(5)
A HashMultimap would prevent duplicate entries. But I'm still really unclear on your goal.Sama
ok. i get a text-input. this gets into the map. key is a. the values are stored in a String[]. i need the values later as separate strings. so that is why it is String[] and not just string. i need every combination from the key-value-pairs as in my first post.Fascista
In that case you just need to check if the key-value pair already exists in the map.Noncontributory
I think I just got that you want the product of the maps--as in #714608Sama
well, it is more like this: #711170 the method given in your link... how can this be applied to my case? sorry, i am not a really good programmer...Fascista
S
0

You can obtain a Cartesian product of map key-value combinations using a map and reduce approach.

Try it online!

Map<String, String[]> map = Map.of(
        "a", new String[]{"3", "4"},
        "b", new String[]{"5", "6"});
List<Map<String, String>> comb = map.entrySet().stream()
        // Stream<List<Map<String,String>>>
        .map(e -> Arrays.stream(e.getValue())
                .map(v -> Map.of(e.getKey(), v))
                .collect(Collectors.toList()))
        // summation of pairs of list into a single list
        .reduce((list1, list2) -> list1.stream()
                // combinations of inner maps
                .flatMap(map1 -> list2.stream()
                        // concatenate into a single map
                        .map(map2 -> {
                            Map<String, String> m = new HashMap<>();
                            m.putAll(map1);
                            m.putAll(map2);
                            return m;
                        }))
                // list of combinations
                .collect(Collectors.toList()))
        // otherwise, an empty list
        .orElse(Collections.emptyList());
// output, order may vary
comb.forEach(System.out::println);

Output, order may vary:

{a=3, b=5}
{a=3, b=6}
{a=4, b=5}
{a=4, b=6}

See also: Cartesian product of map values

Sill answered 13/8, 2021 at 13:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.