I have a List
of Objects
that each one of them contains a X, Y value (Doubles
) and a name String
. As I have multiple entries of the same names that correspond to coordinates, I want to link every name to all the coordinates, which I managed to do by creating a Map
of type <String, List<Object>>
Example of output:
One [[0.1,0.1,0.1],[0.2,0.3,0.4]]
One,Two [[0.1,0.1] ,[0.4,0.5]]
One,Two,Three [[0.1,0.1] ,[0.6,0.7]]
What I want to do now is to split the names where there is a comma ,
and achieve the following result:
One [[0.1,0.1,0.1,0.1,0.1,0.1],[0.2,0.3,0.4,0.5,0.6,0.7]]
Two [[0.01,0.01,0.01,0.01] ,[0.4,0.5,0.6,0.7]]
Three [[0.01,0.01] ,[0.6,0.7]]
My code that WORKS is:
Map<String, List<Coordinates>> newList = coordinateList.stream()
.collect(Collectors.groupingBy(Coordinates::getName));
which creates the first Map
.
In order to do the second part I have tried the following along with various combinations but with no luck:
newList.entrySet().stream()
.flatMap(entry -> Arrays.stream(entry.getKey().split(","))
.map(s -> new AbstractMap.SimpleEntry<>(s, entry.getValue())))
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.flatMapping(entry -> entry.getValue().stream(), Collectors.toList())));
The error that I get is:
The method flatMapping((<no type> entry) -> {}, Collectors.toList()) is undefined for the type Collectors
Note that by changing flatMapping
to mapping
works but does not solve the problem.
Another variation of the code that I tried is:
Map<String, List<Object>> collect1 = map.entrySet().stream()
.flatMap(entry -> Arrays.stream(entry.getKey().split(","))
.map(s -> new AbstractMap.SimpleEntry<>(s, entry.getValue())))
.collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toList())))
.entrySet().stream()
.flatMap(entry -> entry.getValue().stream()
.flatMap(Collection::stream)
.map(o -> new AbstractMap.SimpleEntry<>(entry.getKey(), o)))
.collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toList())));
Still no luck and I regularly get the error Cannot infer type argument(s) for <R> flatMap(Function<? super T,? extends Stream<? extends R>>)
Any ideas?
It seems you are not using java9. If you are using Java9 the former approach with Collectors.flatMapping
would work. But I still don't see any point in creating a new Map.Entry
here. Take a look at this solution.
private static final Pattern COMMA_DELIMITER = Pattern.compile(",\\s*");
Map<String, Set<Coordinate>> nameToCoordinates = coordinates.stream()
.flatMap(
c -> COMMA_DELIMITER.splitAsStream(c.getName())
.map(n -> new Coordinate(n, c.getX(), c.getY())))
.collect(Collectors.groupingBy(Coordinate::getName, Collectors.toSet()));
For each coordinate, grab it's name and split it using the , delimiter and then for each such name token create a new Coordinate with that new name and x, y coordinates. Then merely collect it using the groupingBy collector. Since you need to keep only distinct Coordinate values, you have to override equals and hashCode methods in the Coordinate class. Here's how it looks.
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + Double.hashCode(x);
result = 31 * result + Double.hashCode(y);
return result;
}
@Override
public boolean equals(Object obj) {
return obj instanceof Coordinate && ((Coordinate) obj).getX() == x
&& ((Coordinate) obj).getY() == y
&& ((Coordinate) obj).getName().equals(name);
}
For some reason if you really need to use the intermediary map created at the first step, then your solution would be like this.
newList.values().stream().flatMap(Collection::stream)
.flatMap(
c -> COMMA_DELIMITER.splitAsStream(c.getName())
.map(n -> new Coordinate(n, c.getX(), c.getY())))
.collect(Collectors.groupingBy(Coordinate::getName, Collectors.toSet()));
Here's the output.
{One=[[name=One, x=0.1, y=0.2], [name=One, x=0.1, y=0.4], [name=One, x=0.1, y=0.3], [name=One, x=0.1, y=0.5], [name=One, x=0.1, y=0.6], [name=One, x=0.1, y=0.7]], Two=[[name=Two, x=0.1, y=0.4], [name=Two, x=0.1, y=0.5], [name=Two, x=0.1, y=0.6], [name=Two, x=0.1, y=0.7]], Three=[[name=Three, x=0.1, y=0.6], [name=Three, x=0.1, y=0.7]]}