Reputation: 101
I have a
Map<String,List<User>>map = new HashMap<>();
map.put("projectA",Arrays.asList(new User(1,"Bob"),new User(2,"John"),new User(3,"Mo")));
map.put("projectB",Arrays.asList(new User(2,"John"),new User(3,"Mo")));
map.put("projectC",Arrays.asList(new User(3,"Mo")));
Can use String instead of User.
String is a project Name but the same users can relate to different projects.
I would like to get sth like Map<User, List<String>>
where
the key will represent a distinct user and a value as a list of projects' names to which he/she relates.
Bob = [projectA]
John = [projectA, projectB]
Mo = [projectA, projectB, projectC]
TQ in advance for any piece of advice.
Upvotes: 0
Views: 570
Reputation: 28988
To reverse this Map, you need to iterate over its entries and for each distinct user create an entry containing a list of projects as a value in the resulting Map.
This logic can be implemented using Java 8 methods Map.computeIfAbsent()
and Map.forEach()
.
Map<String, List<User>> usersByProject = // initilizing the source map
Map<User, List<String>> projectsByUser = new HashMap<>();
usersByProject.forEach((project, users) ->
users.forEach(user -> projectsByUser.computeIfAbsent(user, k -> new ArrayList<>())
.add(project))
);
Stream-based implementation would require a bit more effort.
The core logic remains the same. But there's one important peculiarity: we would need to generate from each entry of the source Map a sequence of new elements, containing references to a particular user and a project.
To carry this data we would need an auxiliary type, and a Java 16 record fits into this role very well. And quick and dirty alternative would be to use Map.Entry
, but it's better to avoid resorting to this option because methods getKey()
/getValue()
are faceless, and it requires more effort to reason about the code. You can also define a regular class
if you're using an earlier version of JDK.
public record UserProject(User user, String project) {}
That's how a stream-based solution might look like:
Map<String, List<User>> usersByProject = Map.of(
"projectA", List.of(new User(1, "Bob"), new User(2, "John"), new User(3, "Mo")),
"projectB", List.of(new User(2, "John"), new User(3, "Mo")),
"projectC", List.of(new User(3, "Mo"))
);
Map<User, List<String>> projectByUsers = usersByProject.entrySet().stream()
.flatMap(entry -> entry.getValue().stream().
map(user -> new UserProject(user, entry.getKey()))
)
.collect(Collectors.groupingBy(
UserProject::user,
Collectors.mapping(UserProject::project,
Collectors.toList())
));
projectsByUser.forEach((k, v) -> System.out.println(k + " -> " + v));
Output:
User[id=1, name=Bob] -> [projectA]
User[id=2, name=John] -> [projectA, projectB]
User[id=3, name=Mo] -> [projectA, projectC, projectB]
Upvotes: 0
Reputation: 11934
Just loop over the map's entries and the List inside of them:
public static void main(String[] args) {
Map<String, List<User>> map = new HashMap<>();
map.put("projectA", Arrays.asList(new User(1,"Bob"),new User(2,"John"),new User(3,"Mo")));
map.put("projectB",Arrays.asList(new User(2,"John"),new User(3,"Mo")));
map.put("projectC",Arrays.asList(new User(3,"Mo")));
Map<User, List<String>> result = new HashMap<>();
for(Map.Entry<String, List<User>> e:map.entrySet()) {
for(User u:e.getValue()) {
result.putIfAbsent(u, new ArrayList<>());
result.get(u).add(e.getKey());
}
}
System.out.println(result);
}
public static record User(int id, String name) {}
prints
{User[id=1, name=Bob]=[projectA], User[id=2, name=John]=[projectB, projectA], User[id=3, name=Mo]=[projectB, projectA, projectC]}
Upvotes: 1