2

Here's a seemingly trivial coding task that I just can't seem to find an elegant solution for. Perhaps it's a Sunday afternoon 'dumbness' thing but I'm just not seeing it.

I have a Map that represents a set of names for a group:

  1. Map<String, Set<String>> namesForGroup;

With content that looks something like this:

1 {"Grp1": {"a", "b", "c", "d"},
2 "Grp2": {"e", "f", "g", "h"}}

What I'd like is an elegant way to extract a random Key Value pair from that Map. For example:

1 ["Grp1", "a"]
2 ["Grp1", "d"]
3 ["Grp2", "g"]
4 ["Grp1", "c"]
5 ["Grp2", "e"]

There is no requirement for any strong randomness or any even distribution of the chosen values. A rough approximation is all that is required as I'm just generating some test data for a performance benchmark I'm writing for a new feature I've written at work.

Thanks

2 Answers2

0

Getting random elements from a set is a bit more complicated than getting random elements from a list, but here's how it would work:

private static String[] getRandomPair(Map<String, Set<String>> map) {
    Random random = new Random();

    String[] groups = map.keySet().toArray(new String[0]);
    String randomGroup = groups[random.nextInt(groups.length)];

    String[] names = map.get(randomGroup).toArray(new String[0]);
    String randomName = names[random.nextInt(names.length)];

    return new String[] { randomGroup, randomName };
}

Sample use:

public static void main(String[] args) {
    Map<String, Set<String>> namesForGroup = new HashMap<>();
    namesForGroup.put("Grp1", new HashSet<>(Arrays.asList("a", "b", "c", "d")));
    namesForGroup.put("Grp2", new HashSet<>(Arrays.asList("e", "f", "g", "h")));
    System.out.println(Arrays.toString(getRandomPair(namesForGroup)));
    System.out.println(Arrays.toString(getRandomPair(namesForGroup)));
    System.out.println(Arrays.toString(getRandomPair(namesForGroup)));
    System.out.println(Arrays.toString(getRandomPair(namesForGroup)));
}

Prints e.g.:

[Grp2, e]
[Grp1, a]
[Grp2, e]
[Grp1, b]
Marvin
  • 13,325
  • 3
  • 51
  • 57
0

Your question is a duplicate of a few others, sort of. It is perhaps unique from the duplicate links in that you need to find a random element of a set, twice, in a nested fashion. You can take a random map entry from your map's entry set, grab that key, and then take a random element from that entry's value, which itself is another set.

To do this, it might help if we create a helper method which can find a random element in a Java Set. This is needed because sets are inherently unordered. We can't just access some index, because there isn't one (well, maybe in TreeSet this might make sense, but not in a regular Set).

public static < T > T getRandomSetEntry(Set<T> set) {
    int size = set.size();
    int item = new Random().nextInt(size);
    int i = 0;
    for (T obj : set) {
        if (i == item)
            return obj;
        i++;
    }

    return null;
}

public static void main(String args[]) {
    Map<String, Set<String>> namesForGroup = new HashMap<>();
    Set<String> set1 = new HashSet<>();
    set1.add("a");
    set1.add("b");
    set1.add("c");
    set1.add("d");
    Set<String> set2 = new HashSet<>();
    set2.add("e");
    set2.add("f");
    set2.add("g");
    set2.add("h");

    namesForGroup.put("Grp1", set1);
    namesForGroup.put("Grp2", set2);

    Set<Map.Entry<String, Set<String>>> set = namesForGroup.entrySet();
    Map.Entry<String, Set<String>> entry = getRandomSetEntry(set);
    String key = entry.getKey();
    String value = getRandomSetEntry(entry.getValue());
    System.out.println("key: " + key + ", value: " + value);
}

Demo here:

Rextester

Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360