Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Switch direction of association between objects

Say I have a class foo:

class foo {
    String someString;
    List<String> someList;
}

If I have a list of foo's, is there a way for me to create a new list/array/whatever of foo's with essentially the someString and someList values remapped? For example:

arr1: [
    foo {
        someString: 'test1',
        someList: ['a', 'b']
    },
    foo {
        someString: 'test2',
        someList: ['b', 'c']
    }
]

Would become

arr2: [
    foo {
        someString: 'a',
        someList: ['test1']
    },
    foo {
        someString: 'b',
        someList: ['test1', 'test2']
    },
    foo {
        someString: 'c',
        someList: ['test2']
    },
]

Right now, I have a nested loop that iterates over each element of someList for each foo and compiles them into a map where the key is the someList value and the value is a set of the someString values that have come from foos where the someList value was present. Then do a map of the entry set to convert it to new foos with the key and value passed as constructor params.

Map<String, Set<String>> myMap;

for(foo f : arr1) {
    for(String s : f.someList) {
        Set<String> mySet = myMap.get(s);
        if(mySet == null) {
            myMap.put(s, new HashSet<String>(Arrays.asList(f.someString)));
        } else {
            mySet.add(f.someString);
        }
    }
}

List<String> myNewList = myMap.entrySet()
                              .stream()
                              .map(e -> 
                                    new foo(e.key, new ArrayList<String>(e.value)))
                              .collect(Collectors.toList());

It seems to work fine, but it's not the prettiest in the world, so I was wondering if there's another approach to doing this.

like image 259
Andrew Robie Avatar asked Oct 17 '22 11:10

Andrew Robie


1 Answers

First flip your objects by using a map:

Map<String, Set<String>> map = new LinkedHashMap<>();
arr1.forEach(foo -> 
        foo.someList.forEach(s -> 
                map.computeIfAbsent(
                    s, 
                    k -> new LinkedHashSet<>())
                .add(foo.someString)));

Then create the foo objects from the map:

List<foo> result = new ArrayList<>();
map.forEach((k, v) -> list.add(new foo(k, new ArrayList<>(v))));

This assumes you have the appropriate constructor.

like image 154
fps Avatar answered Nov 15 '22 05:11

fps