I have the following data stored as lists:
List<ArrayList<String>> dummy = new ArrayList<>();
dummy.add(new ArrayList<>(List.of("1", "SP1", "SW1")));
dummy.add(new ArrayList<>(List.of("2", "SP1", "SW2")));
dummy.add(new ArrayList<>(List.of("3", "SP2", "SW1")));
dummy.add(new ArrayList<>(List.of("4", "SP2", "SW2")));
dummy.add(new ArrayList<>(List.of("5", "SP2", "SW1")));
dummy.add(new ArrayList<>(List.of("6", "SP1", "SW1")));
dummy.add(new ArrayList<>(List.of("7", "SP3", "SW2")));
I need to group it as follows
SW1-SP1-list{1,6}
SW1-SP2-list{3,5}
SW2-SP1-list{2}
SW2-SP2-list{4}
SW2-SP3-list{7}
What I have tried is the following:
var test = dummy.stream()
.collect(Collectors.groupingBy(
s -> s.get(2),
TreeMap::new,
Collectors.groupingBy(
s -> s.get(1),
Collectors.toList()
)
));
But this doesn't give the desired result.
CodePudding user response:
Here's how you can apply nested groupingBy
Map<String, Map<String, List<String>>> result = dummy.stream()
.collect(Collectors.groupingBy(l -> l.get(2),
Collectors.groupingBy(l -> l.get(1),
Collectors.mapping(l -> l.get(0), Collectors.toList()))));
To collect it in the order encountered, you can use a LinkedHashMap
,
Map<String, Map<String, List<String>>> result = dummy.stream()
.collect(Collectors.groupingBy(l -> l.get(2), LinkedHashMap::new,
Collectors.groupingBy(l -> l.get(1), LinkedHashMap::new,
Collectors.mapping(l -> l.get(0), Collectors.toList()))));
System.out.println(result);
Result:
{SW1={SP1=[1, 6], SP2=[3, 5]}, SW2={SP1=[2], SP2=[4], SP3=[7]}}
CodePudding user response:
Here's a solution:
var test = dummy.stream()
.collect(Collectors.groupingBy(z -> z.get(2) "-" z.get(1),
Collectors.mapping(z -> z.get(0), Collectors.toList())));
Output:
SW2-SP1=[2]
SW2-SP3=[7]
SW1-SP2=[3, 5]
SW2-SP2=[4]
SW1-SP1=[1, 6]
CodePudding user response:
Nested Maps are impractical because they are not very convenient.
And since you basically need to associate every distinct combination of SW
and SP
with a corresponding list of values, you can define an auxiliary object that would server a key.
That would allow to achieve the desired result without a need to resort to nested maps.
A Java 16 record would fit into this role perfectly well (if you're using a lower version of JDK, you can implement it as a plain class):
record SwSp(String sw, String sp) {}
In order to use it as a key in a TreeMap
it needs either implement Comparable
interface, or you can provide a Comparator
while creating a TreeMap
as shown below.
To generate comparator you can use Java 8 static methods comparing()
and thenComparing()
.
With it, the stream might look like this:
public static void main(String[] args) {
List<List<String>> dummy = List.of(
new ArrayList<>(List.of("1", "SP1", "SW1")),
new ArrayList<>(List.of("2", "SP1", "SW2")),
new ArrayList<>(List.of("3", "SP2", "SW1")),
new ArrayList<>(List.of("4", "SP2", "SW2")),
new ArrayList<>(List.of("5", "SP2", "SW1")),
new ArrayList<>(List.of("6", "SP1", "SW1")),
new ArrayList<>(List.of("7", "SP3", "SW2"))
);
NavigableMap<SwSp, List<String>> test = dummy.stream()
.collect(Collectors.groupingBy(
list -> new SwSp(list.get(2), list.get(1)),
() -> new TreeMap<>(
Comparator.comparing(SwSp::sw).thenComparing(SwSp::sp)
),
Collectors.mapping(list -> list.get(0),
Collectors.toList())
));
test.forEach((k, v) -> System.out.println(k " -> " v));
}
Output:
SwSp[sw=SW1, sp=SP1] -> [1, 6]
SwSp[sw=SW1, sp=SP2] -> [3, 5]
SwSp[sw=SW2, sp=SP1] -> [2]
SwSp[sw=SW2, sp=SP2] -> [4]
SwSp[sw=SW2, sp=SP3] -> [7]
Note
Based on the way how these lists are structured (they all have exactly 3
elements which are ordered in the same way) I can make a conclusion that you're misusing collections because your List
of List
s should be a List
of objects in the first place.