Issue
I have a List
of objects A
, where the object A
has this form:
class A {
private String a1;
private String a2;
private String a3;
private String a4;
private String a5;
private String a6;
}
I need to group this List first by a1
and a2
, then by a3
and a4
, resulting into List<B>
Where the object B
has this form
class B {
private String a1;
private String a2;
private List<C> list;
}
And where the object C
has this form
class C {
private String a3;
private String a4;
private List<D> list;
}
And where the object D
has this form
class D {
private String a5;
private String a6;
}
Example. Given the list:
[{a1="100", a2="bbb", a3="100100", a4="ddd", a5="1", a6="10"},
{a1="100", a2="bbb", a3="100100", a4="ddd", a5="2", a6="20"},
{a1="100", a2="bbb", a3="100200", a4="eee", a5="3", a6="30"},
{a1="200", a2="ccc", a3="200100", a4="fff", a5="4", a6="40"},
{a1="200", a2="ccc", a3="200200", a4="ggg", a5="5", a6="50"},
{a1="200", a2="ccc", a3="200300", a4="hhh", a5="6", a6="60"}]
I need this structure as a result:
{"B": [
{
"a1": "100",
"a2": "bbb",
"C": [
{
"a3": "100100",
"a4": "ddd",
"D": [
{"a5": "1", "a6": "10"},
{"a5": "2", "a6": "20"}
]
},
{
"a3": "100200",
"a4": "eee",
"D": [
{"a5": "3", "a6": "30"}
]
}
]
},
{
"a1": "200",
"a2": "ccc",
"C": [
{
"a3": "200100",
"a4": "fff",
"D": [
{"a5": "4", "a6": "40"}
]
},
{
"a3": "200200",
"a4": "ggg",
"D": [
{"a5": "5", "a6": "50"}
]
},
{
"a3": "200300",
"a4": "hhh",
"D": [
{"a5": "6", "a6": "60"}
]
}
]
}
]
}
Is this possible with streams in Java 11? Any other ways to achieve this goal are welcome as well.
Solution
The code provided below does what required.
There's a problem though which derived from the way of how your data is structured:
- If chunks of data like pairs of strings
a1
anda2
,a3
anda4
, etc. constitute self-contained units of information that have a particular meaning in your domain model, you shouldn't create such beasts likeA
with huge a number of string fields (objectA
must contain a collection of objectsB
and that it). It's a faulty design, you should refine you classes. - Some strings in your example contain numeric data. If you are parsing it somewhere later on, that isn't good as well. They must be replaced with numeric data types.
The solution below makes use of built-in collectors Collector.collectionAndThen()
and Collectors.groupingBy()
. Map.Entry
is utilized as an intermediate container of data in both groupingBy()
collectors (Map.Entry
objects are keys in both maps).
Functions are responsible for transforming these maps into list List<C>
and List<B>
.
public static void main(String[] args) {
List<A> aList = List.of(new A("100", "bbb", "100100", "ddd", "1", "10"),
new A("100", "bbb", "100100", "ddd", "2", "20"),
new A("100", "bbb", "100200", "eee", "3", "30"),
new A("200", "ccc", "200100", "fff", "4", "40"),
new A("200", "ccc", "200200", "ggg", "5", "50"),
new A("200", "ccc", "200300", "hhh", "6", "60"));
Function<Map<Map.Entry<String, String>, List<D>>, List<C>> mapToListC =
mapC -> mapC.entrySet().stream()
.map(entry -> new C(entry.getKey().getKey(),
entry.getKey().getValue(),
entry.getValue()))
.collect(Collectors.toList());
Function<Map<Map.Entry<String, String>, Map<Map.Entry<String, String>, List<D>>>, List<B>> mapToListB =
mapB -> mapB.entrySet().stream()
.map(entry -> new B(entry.getKey().getKey(),
entry.getKey().getValue(),
mapToListC.apply(entry.getValue())))
.collect(Collectors.toList());
List<B> bList = aList.stream()
.collect(Collectors.collectingAndThen(
Collectors.groupingBy((A a) -> Map.entry(a.getA1(), a.getA2()),
Collectors.groupingBy((A a) -> Map.entry(a.getA3(), a.getA4()),
Collectors.mapping((A a) -> new D(a.getA5(), a.getA6()),
Collectors.toList()))),
mapToListB));
bList.forEach(System.out::println);
}
Output
B{a1='200', a2='ccc'list=
C{a3='200300', a4='hhh'list=
D{a5='6', a6='60'}}
C{a3='200100', a4='fff'list=
D{a5='4', a6='40'}}
C{a3='200200', a4='ggg'list=
D{a5='5', a6='50'}}}
B{a1='100', a2='bbb'list=
C{a3='100200', a4='eee'list=
D{a5='3', a6='30'}}
C{a3='100100', a4='ddd'list=
D{a5='1', a6='10'}
D{a5='2', a6='20'}}}
Answered By - Alexander Ivanchenko
Answer Checked By - Clifford M. (JavaFixing Volunteer)