How to groupBy object properties and map to another object using Java 8 Streams?
Suppose I have a group of bumper cars, which have a size, a color and an identifier ("car code") on their sides.
class BumperCar {
int size;
String color;
String carCode;
}
Now I need to map the bumper cars to a List
of DistGroup
objects, which each contains the properties size
, color
and a List
of car codes.
class DistGroup {
int size;
Color color;
List<String> carCodes;
void addCarCodes(List<String> carCodes) {
this.carCodes.addAll(carCodes);
}
}
For example,
[
BumperCar(size=3, color=yellow, carCode=Q4M),
BumperCar(size=3, color=yellow, carCode=T5A),
BumperCar(size=3, color=red, carCode=6NR)
]
should result in:
[
DistGroup(size=3, color=yellow, carCodes=[ Q4M, T5A ]),
DistGroup(size=3, color=red, carCodes=[ 6NR ])
]
I tried the following, which actually does what I want it to do. But the problem is that it materializes the immediate result (into a Map
) and I also think that it can be done at once (perhaps using mapping
or collectingAndThen
or reducing
or something), resulting in more elegant code.
List<BumperCar> bumperCars = ...
Map<SizeColorCombination, List<BumperCar>> map = bumperCars.stream()
.collect(groupingBy(t -> new SizeColorCombination(t.getSize(), t.getColor())));
List<DistGroup> distGroups = map.entrySet().stream()
.map(t -> {
DistGroup d = new DistGroup(t.getKey().getSize(), t.getKey().getColor());
d.addCarCodes(t.getValue().stream()
.map(BumperCar::getCarCode)
.collect(toList()));
return d;
})
.collect(toList());
How can I get the desired result without using a variable for an immediate result?
Edit: How can I get the desired result without materializing the immediate result? I am merely looking for a way which does not materialize the immediate result, at least not on the surface. That means that I prefer not to use something like this:
something.stream()
.collect(...) // Materializing
.stream()
.collect(...); // Materializing second time
Of course, if this is possible.
Note that I omitted getters and constructors for brevity. You may also assume that equals
and hashCode
methods are properly implemented. Also note that I'm using the SizeColorCombination
which I use as group-by key. This class obviously contains the properties size
and color
. Classes like Tuple
or Pair
or any other class representing a combination of two arbitrary values may also be used.
Edit: Also note that an ol' skool for loop can be used instead of course, but that is not in the scope of this question.
java java-8 java-stream grouping collectors
add a comment |
Suppose I have a group of bumper cars, which have a size, a color and an identifier ("car code") on their sides.
class BumperCar {
int size;
String color;
String carCode;
}
Now I need to map the bumper cars to a List
of DistGroup
objects, which each contains the properties size
, color
and a List
of car codes.
class DistGroup {
int size;
Color color;
List<String> carCodes;
void addCarCodes(List<String> carCodes) {
this.carCodes.addAll(carCodes);
}
}
For example,
[
BumperCar(size=3, color=yellow, carCode=Q4M),
BumperCar(size=3, color=yellow, carCode=T5A),
BumperCar(size=3, color=red, carCode=6NR)
]
should result in:
[
DistGroup(size=3, color=yellow, carCodes=[ Q4M, T5A ]),
DistGroup(size=3, color=red, carCodes=[ 6NR ])
]
I tried the following, which actually does what I want it to do. But the problem is that it materializes the immediate result (into a Map
) and I also think that it can be done at once (perhaps using mapping
or collectingAndThen
or reducing
or something), resulting in more elegant code.
List<BumperCar> bumperCars = ...
Map<SizeColorCombination, List<BumperCar>> map = bumperCars.stream()
.collect(groupingBy(t -> new SizeColorCombination(t.getSize(), t.getColor())));
List<DistGroup> distGroups = map.entrySet().stream()
.map(t -> {
DistGroup d = new DistGroup(t.getKey().getSize(), t.getKey().getColor());
d.addCarCodes(t.getValue().stream()
.map(BumperCar::getCarCode)
.collect(toList()));
return d;
})
.collect(toList());
How can I get the desired result without using a variable for an immediate result?
Edit: How can I get the desired result without materializing the immediate result? I am merely looking for a way which does not materialize the immediate result, at least not on the surface. That means that I prefer not to use something like this:
something.stream()
.collect(...) // Materializing
.stream()
.collect(...); // Materializing second time
Of course, if this is possible.
Note that I omitted getters and constructors for brevity. You may also assume that equals
and hashCode
methods are properly implemented. Also note that I'm using the SizeColorCombination
which I use as group-by key. This class obviously contains the properties size
and color
. Classes like Tuple
or Pair
or any other class representing a combination of two arbitrary values may also be used.
Edit: Also note that an ol' skool for loop can be used instead of course, but that is not in the scope of this question.
java java-8 java-stream grouping collectors
2
Just as a side note,groupingBy()
does group the values into aList
by default sotoList()
may be omitted
– Lino
Jan 18 at 12:35
1
The idea of using streams is to make code more readable, more self-explanatory (at the cost of performance), or massively parallelizable without boilerplate code. It's not a modern one-solution-fits-all replacement of the old ways. The code you provided is cryptic at best. I suggest using a classic for-loop which is much cleaner in this case.
– Mark Jeronimus
Jan 18 at 12:37
@Lino You're right. I removed it.
– MC Emperor
Jan 18 at 12:53
@MarkJeronimus That's right, that's why I'm not satisfied with my current solution and looking for an elegant way to achieve just the same result—if it exists. Otherwise I will gladly revert to the classic loop.
– MC Emperor
Jan 18 at 12:58
add a comment |
Suppose I have a group of bumper cars, which have a size, a color and an identifier ("car code") on their sides.
class BumperCar {
int size;
String color;
String carCode;
}
Now I need to map the bumper cars to a List
of DistGroup
objects, which each contains the properties size
, color
and a List
of car codes.
class DistGroup {
int size;
Color color;
List<String> carCodes;
void addCarCodes(List<String> carCodes) {
this.carCodes.addAll(carCodes);
}
}
For example,
[
BumperCar(size=3, color=yellow, carCode=Q4M),
BumperCar(size=3, color=yellow, carCode=T5A),
BumperCar(size=3, color=red, carCode=6NR)
]
should result in:
[
DistGroup(size=3, color=yellow, carCodes=[ Q4M, T5A ]),
DistGroup(size=3, color=red, carCodes=[ 6NR ])
]
I tried the following, which actually does what I want it to do. But the problem is that it materializes the immediate result (into a Map
) and I also think that it can be done at once (perhaps using mapping
or collectingAndThen
or reducing
or something), resulting in more elegant code.
List<BumperCar> bumperCars = ...
Map<SizeColorCombination, List<BumperCar>> map = bumperCars.stream()
.collect(groupingBy(t -> new SizeColorCombination(t.getSize(), t.getColor())));
List<DistGroup> distGroups = map.entrySet().stream()
.map(t -> {
DistGroup d = new DistGroup(t.getKey().getSize(), t.getKey().getColor());
d.addCarCodes(t.getValue().stream()
.map(BumperCar::getCarCode)
.collect(toList()));
return d;
})
.collect(toList());
How can I get the desired result without using a variable for an immediate result?
Edit: How can I get the desired result without materializing the immediate result? I am merely looking for a way which does not materialize the immediate result, at least not on the surface. That means that I prefer not to use something like this:
something.stream()
.collect(...) // Materializing
.stream()
.collect(...); // Materializing second time
Of course, if this is possible.
Note that I omitted getters and constructors for brevity. You may also assume that equals
and hashCode
methods are properly implemented. Also note that I'm using the SizeColorCombination
which I use as group-by key. This class obviously contains the properties size
and color
. Classes like Tuple
or Pair
or any other class representing a combination of two arbitrary values may also be used.
Edit: Also note that an ol' skool for loop can be used instead of course, but that is not in the scope of this question.
java java-8 java-stream grouping collectors
Suppose I have a group of bumper cars, which have a size, a color and an identifier ("car code") on their sides.
class BumperCar {
int size;
String color;
String carCode;
}
Now I need to map the bumper cars to a List
of DistGroup
objects, which each contains the properties size
, color
and a List
of car codes.
class DistGroup {
int size;
Color color;
List<String> carCodes;
void addCarCodes(List<String> carCodes) {
this.carCodes.addAll(carCodes);
}
}
For example,
[
BumperCar(size=3, color=yellow, carCode=Q4M),
BumperCar(size=3, color=yellow, carCode=T5A),
BumperCar(size=3, color=red, carCode=6NR)
]
should result in:
[
DistGroup(size=3, color=yellow, carCodes=[ Q4M, T5A ]),
DistGroup(size=3, color=red, carCodes=[ 6NR ])
]
I tried the following, which actually does what I want it to do. But the problem is that it materializes the immediate result (into a Map
) and I also think that it can be done at once (perhaps using mapping
or collectingAndThen
or reducing
or something), resulting in more elegant code.
List<BumperCar> bumperCars = ...
Map<SizeColorCombination, List<BumperCar>> map = bumperCars.stream()
.collect(groupingBy(t -> new SizeColorCombination(t.getSize(), t.getColor())));
List<DistGroup> distGroups = map.entrySet().stream()
.map(t -> {
DistGroup d = new DistGroup(t.getKey().getSize(), t.getKey().getColor());
d.addCarCodes(t.getValue().stream()
.map(BumperCar::getCarCode)
.collect(toList()));
return d;
})
.collect(toList());
How can I get the desired result without using a variable for an immediate result?
Edit: How can I get the desired result without materializing the immediate result? I am merely looking for a way which does not materialize the immediate result, at least not on the surface. That means that I prefer not to use something like this:
something.stream()
.collect(...) // Materializing
.stream()
.collect(...); // Materializing second time
Of course, if this is possible.
Note that I omitted getters and constructors for brevity. You may also assume that equals
and hashCode
methods are properly implemented. Also note that I'm using the SizeColorCombination
which I use as group-by key. This class obviously contains the properties size
and color
. Classes like Tuple
or Pair
or any other class representing a combination of two arbitrary values may also be used.
Edit: Also note that an ol' skool for loop can be used instead of course, but that is not in the scope of this question.
java java-8 java-stream grouping collectors
java java-8 java-stream grouping collectors
edited 22 hours ago
MC Emperor
asked Jan 18 at 12:29
MC EmperorMC Emperor
8,209125388
8,209125388
2
Just as a side note,groupingBy()
does group the values into aList
by default sotoList()
may be omitted
– Lino
Jan 18 at 12:35
1
The idea of using streams is to make code more readable, more self-explanatory (at the cost of performance), or massively parallelizable without boilerplate code. It's not a modern one-solution-fits-all replacement of the old ways. The code you provided is cryptic at best. I suggest using a classic for-loop which is much cleaner in this case.
– Mark Jeronimus
Jan 18 at 12:37
@Lino You're right. I removed it.
– MC Emperor
Jan 18 at 12:53
@MarkJeronimus That's right, that's why I'm not satisfied with my current solution and looking for an elegant way to achieve just the same result—if it exists. Otherwise I will gladly revert to the classic loop.
– MC Emperor
Jan 18 at 12:58
add a comment |
2
Just as a side note,groupingBy()
does group the values into aList
by default sotoList()
may be omitted
– Lino
Jan 18 at 12:35
1
The idea of using streams is to make code more readable, more self-explanatory (at the cost of performance), or massively parallelizable without boilerplate code. It's not a modern one-solution-fits-all replacement of the old ways. The code you provided is cryptic at best. I suggest using a classic for-loop which is much cleaner in this case.
– Mark Jeronimus
Jan 18 at 12:37
@Lino You're right. I removed it.
– MC Emperor
Jan 18 at 12:53
@MarkJeronimus That's right, that's why I'm not satisfied with my current solution and looking for an elegant way to achieve just the same result—if it exists. Otherwise I will gladly revert to the classic loop.
– MC Emperor
Jan 18 at 12:58
2
2
Just as a side note,
groupingBy()
does group the values into a List
by default so toList()
may be omitted– Lino
Jan 18 at 12:35
Just as a side note,
groupingBy()
does group the values into a List
by default so toList()
may be omitted– Lino
Jan 18 at 12:35
1
1
The idea of using streams is to make code more readable, more self-explanatory (at the cost of performance), or massively parallelizable without boilerplate code. It's not a modern one-solution-fits-all replacement of the old ways. The code you provided is cryptic at best. I suggest using a classic for-loop which is much cleaner in this case.
– Mark Jeronimus
Jan 18 at 12:37
The idea of using streams is to make code more readable, more self-explanatory (at the cost of performance), or massively parallelizable without boilerplate code. It's not a modern one-solution-fits-all replacement of the old ways. The code you provided is cryptic at best. I suggest using a classic for-loop which is much cleaner in this case.
– Mark Jeronimus
Jan 18 at 12:37
@Lino You're right. I removed it.
– MC Emperor
Jan 18 at 12:53
@Lino You're right. I removed it.
– MC Emperor
Jan 18 at 12:53
@MarkJeronimus That's right, that's why I'm not satisfied with my current solution and looking for an elegant way to achieve just the same result—if it exists. Otherwise I will gladly revert to the classic loop.
– MC Emperor
Jan 18 at 12:58
@MarkJeronimus That's right, that's why I'm not satisfied with my current solution and looking for an elegant way to achieve just the same result—if it exists. Otherwise I will gladly revert to the classic loop.
– MC Emperor
Jan 18 at 12:58
add a comment |
4 Answers
4
active
oldest
votes
If we assume that DistGroup
has hashCode/equals
based on size
and color
, you could do it like this:
bumperCars
.stream()
.map(x -> {
List<String> list = new ArrayList<>();
list.add(x.getCarCode());
return new SimpleEntry<>(x, list);
})
.map(x -> new DistGroup(x.getKey().getSize(), x.getKey().getColor(), x.getValue()))
.collect(Collectors.toMap(
Function.identity(),
Function.identity(),
(left, right) -> {
left.getCarCodes().addAll(right.getCarCodes());
return left;
}))
.values(); // Collection<DistGroup>
Thanks, this code works for me. Note that I, after using this code, have tweaked the code a little, so the bumper cars are directly mapped to aDistGroup
using.map(t -> new DistGroup(t.getSize(), t.getColor(), new ArrayList<>(Arrays.asList(t.getCarCode()))))
. Then I collect it withtoMap
with the same arguments as your code, but with the first argument beingt -> new SimpleEntry<>(t.getColor(), t.getSize())
instead ofFunction.identity()
.
– MC Emperor
22 hours ago
add a comment |
Solution-1
Just merging the two steps into one:
List<DistGroup> distGroups = bumperCars.stream()
.collect(Collectors.groupingBy(t -> new SizeColorCombination(t.getSize(), t.getColor())))
.entrySet().stream()
.map(t -> {
DistGroup d = new DistGroup(t.getKey().getSize(), t.getKey().getColor());
d.addCarCodes(t.getValue().stream().map(BumperCar::getCarCode).collect(Collectors.toList()));
return d;
})
.collect(Collectors.toList());
Solution-2
Your intermediate variable would be much better if you could use groupingBy
twice using both the attributes and map the values as List
of codes, something like:
Map<Integer, Map<String, List<String>>> sizeGroupedData = bumperCars.stream()
.collect(Collectors.groupingBy(BumperCar::getSize,
Collectors.groupingBy(BumperCar::getColor,
Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))));
and simply use forEach
to add to the final list as:
List<DistGroup> distGroups = new ArrayList<>();
sizeGroupedData.forEach((size, colorGrouped) ->
colorGrouped.forEach((color, carCodes) -> distGroups.add(new DistGroup(size, color, carCodes))));
Note: I've updated your constructor such that it accepts the card codes list.
DistGroup(int size, String color, List<String> carCodes) {
this.size = size;
this.color = color;
addCarCodes(carCodes);
}
Further combining the second solution into one complete statement(though I would myself favor the forEach
honestly):
List<DistGroup> distGroups = bumperCars.stream()
.collect(Collectors.groupingBy(BumperCar::getSize,
Collectors.groupingBy(BumperCar::getColor,
Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))))
.entrySet()
.stream()
.flatMap(a -> a.getValue().entrySet()
.stream().map(b -> new DistGroup(a.getKey(), b.getKey(), b.getValue())))
.collect(Collectors.toList());
add a comment |
You can collect by by using BiConsumer
that take (HashMap<SizeColorCombination, DistGroup> res, BumperCar bc)
as parameters
Collection<DistGroup> values = bumperCars.stream()
.collect(HashMap::new, (HashMap<SizeColorCombination, DistGroup> res, BumperCar bc) -> {
SizeColorCombination dg = new SizeColorCombination(bc.color, bc.size);
DistGroup distGroup = res.get(dg);
if(distGroup != null) {
distGroup.addCarCode(bc.carCode);
}else {
List<String> codes = new ArrayList();
distGroup = new DistGroup(bc.size, bc.color, codes);
res.put(dg, distGroup);
}
},HashMap::putAll).values();
add a comment |
Check out my library AbacusUtil:
StreamEx.of(bumperCars)
.groupBy(c -> Tuple.of(c.getSize(), c.getColor()), BumperCar::getCarCode)
.toList(e -> new DistGroup(e.getKey()._1, e.getKey()._2, e.getValue());
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54254064%2fhow-to-groupby-object-properties-and-map-to-another-object-using-java-8-streams%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
If we assume that DistGroup
has hashCode/equals
based on size
and color
, you could do it like this:
bumperCars
.stream()
.map(x -> {
List<String> list = new ArrayList<>();
list.add(x.getCarCode());
return new SimpleEntry<>(x, list);
})
.map(x -> new DistGroup(x.getKey().getSize(), x.getKey().getColor(), x.getValue()))
.collect(Collectors.toMap(
Function.identity(),
Function.identity(),
(left, right) -> {
left.getCarCodes().addAll(right.getCarCodes());
return left;
}))
.values(); // Collection<DistGroup>
Thanks, this code works for me. Note that I, after using this code, have tweaked the code a little, so the bumper cars are directly mapped to aDistGroup
using.map(t -> new DistGroup(t.getSize(), t.getColor(), new ArrayList<>(Arrays.asList(t.getCarCode()))))
. Then I collect it withtoMap
with the same arguments as your code, but with the first argument beingt -> new SimpleEntry<>(t.getColor(), t.getSize())
instead ofFunction.identity()
.
– MC Emperor
22 hours ago
add a comment |
If we assume that DistGroup
has hashCode/equals
based on size
and color
, you could do it like this:
bumperCars
.stream()
.map(x -> {
List<String> list = new ArrayList<>();
list.add(x.getCarCode());
return new SimpleEntry<>(x, list);
})
.map(x -> new DistGroup(x.getKey().getSize(), x.getKey().getColor(), x.getValue()))
.collect(Collectors.toMap(
Function.identity(),
Function.identity(),
(left, right) -> {
left.getCarCodes().addAll(right.getCarCodes());
return left;
}))
.values(); // Collection<DistGroup>
Thanks, this code works for me. Note that I, after using this code, have tweaked the code a little, so the bumper cars are directly mapped to aDistGroup
using.map(t -> new DistGroup(t.getSize(), t.getColor(), new ArrayList<>(Arrays.asList(t.getCarCode()))))
. Then I collect it withtoMap
with the same arguments as your code, but with the first argument beingt -> new SimpleEntry<>(t.getColor(), t.getSize())
instead ofFunction.identity()
.
– MC Emperor
22 hours ago
add a comment |
If we assume that DistGroup
has hashCode/equals
based on size
and color
, you could do it like this:
bumperCars
.stream()
.map(x -> {
List<String> list = new ArrayList<>();
list.add(x.getCarCode());
return new SimpleEntry<>(x, list);
})
.map(x -> new DistGroup(x.getKey().getSize(), x.getKey().getColor(), x.getValue()))
.collect(Collectors.toMap(
Function.identity(),
Function.identity(),
(left, right) -> {
left.getCarCodes().addAll(right.getCarCodes());
return left;
}))
.values(); // Collection<DistGroup>
If we assume that DistGroup
has hashCode/equals
based on size
and color
, you could do it like this:
bumperCars
.stream()
.map(x -> {
List<String> list = new ArrayList<>();
list.add(x.getCarCode());
return new SimpleEntry<>(x, list);
})
.map(x -> new DistGroup(x.getKey().getSize(), x.getKey().getColor(), x.getValue()))
.collect(Collectors.toMap(
Function.identity(),
Function.identity(),
(left, right) -> {
left.getCarCodes().addAll(right.getCarCodes());
return left;
}))
.values(); // Collection<DistGroup>
edited Jan 18 at 13:04
nullpointer
46.3k1198190
46.3k1198190
answered Jan 18 at 12:56
EugeneEugene
69.4k999164
69.4k999164
Thanks, this code works for me. Note that I, after using this code, have tweaked the code a little, so the bumper cars are directly mapped to aDistGroup
using.map(t -> new DistGroup(t.getSize(), t.getColor(), new ArrayList<>(Arrays.asList(t.getCarCode()))))
. Then I collect it withtoMap
with the same arguments as your code, but with the first argument beingt -> new SimpleEntry<>(t.getColor(), t.getSize())
instead ofFunction.identity()
.
– MC Emperor
22 hours ago
add a comment |
Thanks, this code works for me. Note that I, after using this code, have tweaked the code a little, so the bumper cars are directly mapped to aDistGroup
using.map(t -> new DistGroup(t.getSize(), t.getColor(), new ArrayList<>(Arrays.asList(t.getCarCode()))))
. Then I collect it withtoMap
with the same arguments as your code, but with the first argument beingt -> new SimpleEntry<>(t.getColor(), t.getSize())
instead ofFunction.identity()
.
– MC Emperor
22 hours ago
Thanks, this code works for me. Note that I, after using this code, have tweaked the code a little, so the bumper cars are directly mapped to a
DistGroup
using .map(t -> new DistGroup(t.getSize(), t.getColor(), new ArrayList<>(Arrays.asList(t.getCarCode()))))
. Then I collect it with toMap
with the same arguments as your code, but with the first argument being t -> new SimpleEntry<>(t.getColor(), t.getSize())
instead of Function.identity()
.– MC Emperor
22 hours ago
Thanks, this code works for me. Note that I, after using this code, have tweaked the code a little, so the bumper cars are directly mapped to a
DistGroup
using .map(t -> new DistGroup(t.getSize(), t.getColor(), new ArrayList<>(Arrays.asList(t.getCarCode()))))
. Then I collect it with toMap
with the same arguments as your code, but with the first argument being t -> new SimpleEntry<>(t.getColor(), t.getSize())
instead of Function.identity()
.– MC Emperor
22 hours ago
add a comment |
Solution-1
Just merging the two steps into one:
List<DistGroup> distGroups = bumperCars.stream()
.collect(Collectors.groupingBy(t -> new SizeColorCombination(t.getSize(), t.getColor())))
.entrySet().stream()
.map(t -> {
DistGroup d = new DistGroup(t.getKey().getSize(), t.getKey().getColor());
d.addCarCodes(t.getValue().stream().map(BumperCar::getCarCode).collect(Collectors.toList()));
return d;
})
.collect(Collectors.toList());
Solution-2
Your intermediate variable would be much better if you could use groupingBy
twice using both the attributes and map the values as List
of codes, something like:
Map<Integer, Map<String, List<String>>> sizeGroupedData = bumperCars.stream()
.collect(Collectors.groupingBy(BumperCar::getSize,
Collectors.groupingBy(BumperCar::getColor,
Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))));
and simply use forEach
to add to the final list as:
List<DistGroup> distGroups = new ArrayList<>();
sizeGroupedData.forEach((size, colorGrouped) ->
colorGrouped.forEach((color, carCodes) -> distGroups.add(new DistGroup(size, color, carCodes))));
Note: I've updated your constructor such that it accepts the card codes list.
DistGroup(int size, String color, List<String> carCodes) {
this.size = size;
this.color = color;
addCarCodes(carCodes);
}
Further combining the second solution into one complete statement(though I would myself favor the forEach
honestly):
List<DistGroup> distGroups = bumperCars.stream()
.collect(Collectors.groupingBy(BumperCar::getSize,
Collectors.groupingBy(BumperCar::getColor,
Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))))
.entrySet()
.stream()
.flatMap(a -> a.getValue().entrySet()
.stream().map(b -> new DistGroup(a.getKey(), b.getKey(), b.getValue())))
.collect(Collectors.toList());
add a comment |
Solution-1
Just merging the two steps into one:
List<DistGroup> distGroups = bumperCars.stream()
.collect(Collectors.groupingBy(t -> new SizeColorCombination(t.getSize(), t.getColor())))
.entrySet().stream()
.map(t -> {
DistGroup d = new DistGroup(t.getKey().getSize(), t.getKey().getColor());
d.addCarCodes(t.getValue().stream().map(BumperCar::getCarCode).collect(Collectors.toList()));
return d;
})
.collect(Collectors.toList());
Solution-2
Your intermediate variable would be much better if you could use groupingBy
twice using both the attributes and map the values as List
of codes, something like:
Map<Integer, Map<String, List<String>>> sizeGroupedData = bumperCars.stream()
.collect(Collectors.groupingBy(BumperCar::getSize,
Collectors.groupingBy(BumperCar::getColor,
Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))));
and simply use forEach
to add to the final list as:
List<DistGroup> distGroups = new ArrayList<>();
sizeGroupedData.forEach((size, colorGrouped) ->
colorGrouped.forEach((color, carCodes) -> distGroups.add(new DistGroup(size, color, carCodes))));
Note: I've updated your constructor such that it accepts the card codes list.
DistGroup(int size, String color, List<String> carCodes) {
this.size = size;
this.color = color;
addCarCodes(carCodes);
}
Further combining the second solution into one complete statement(though I would myself favor the forEach
honestly):
List<DistGroup> distGroups = bumperCars.stream()
.collect(Collectors.groupingBy(BumperCar::getSize,
Collectors.groupingBy(BumperCar::getColor,
Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))))
.entrySet()
.stream()
.flatMap(a -> a.getValue().entrySet()
.stream().map(b -> new DistGroup(a.getKey(), b.getKey(), b.getValue())))
.collect(Collectors.toList());
add a comment |
Solution-1
Just merging the two steps into one:
List<DistGroup> distGroups = bumperCars.stream()
.collect(Collectors.groupingBy(t -> new SizeColorCombination(t.getSize(), t.getColor())))
.entrySet().stream()
.map(t -> {
DistGroup d = new DistGroup(t.getKey().getSize(), t.getKey().getColor());
d.addCarCodes(t.getValue().stream().map(BumperCar::getCarCode).collect(Collectors.toList()));
return d;
})
.collect(Collectors.toList());
Solution-2
Your intermediate variable would be much better if you could use groupingBy
twice using both the attributes and map the values as List
of codes, something like:
Map<Integer, Map<String, List<String>>> sizeGroupedData = bumperCars.stream()
.collect(Collectors.groupingBy(BumperCar::getSize,
Collectors.groupingBy(BumperCar::getColor,
Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))));
and simply use forEach
to add to the final list as:
List<DistGroup> distGroups = new ArrayList<>();
sizeGroupedData.forEach((size, colorGrouped) ->
colorGrouped.forEach((color, carCodes) -> distGroups.add(new DistGroup(size, color, carCodes))));
Note: I've updated your constructor such that it accepts the card codes list.
DistGroup(int size, String color, List<String> carCodes) {
this.size = size;
this.color = color;
addCarCodes(carCodes);
}
Further combining the second solution into one complete statement(though I would myself favor the forEach
honestly):
List<DistGroup> distGroups = bumperCars.stream()
.collect(Collectors.groupingBy(BumperCar::getSize,
Collectors.groupingBy(BumperCar::getColor,
Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))))
.entrySet()
.stream()
.flatMap(a -> a.getValue().entrySet()
.stream().map(b -> new DistGroup(a.getKey(), b.getKey(), b.getValue())))
.collect(Collectors.toList());
Solution-1
Just merging the two steps into one:
List<DistGroup> distGroups = bumperCars.stream()
.collect(Collectors.groupingBy(t -> new SizeColorCombination(t.getSize(), t.getColor())))
.entrySet().stream()
.map(t -> {
DistGroup d = new DistGroup(t.getKey().getSize(), t.getKey().getColor());
d.addCarCodes(t.getValue().stream().map(BumperCar::getCarCode).collect(Collectors.toList()));
return d;
})
.collect(Collectors.toList());
Solution-2
Your intermediate variable would be much better if you could use groupingBy
twice using both the attributes and map the values as List
of codes, something like:
Map<Integer, Map<String, List<String>>> sizeGroupedData = bumperCars.stream()
.collect(Collectors.groupingBy(BumperCar::getSize,
Collectors.groupingBy(BumperCar::getColor,
Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))));
and simply use forEach
to add to the final list as:
List<DistGroup> distGroups = new ArrayList<>();
sizeGroupedData.forEach((size, colorGrouped) ->
colorGrouped.forEach((color, carCodes) -> distGroups.add(new DistGroup(size, color, carCodes))));
Note: I've updated your constructor such that it accepts the card codes list.
DistGroup(int size, String color, List<String> carCodes) {
this.size = size;
this.color = color;
addCarCodes(carCodes);
}
Further combining the second solution into one complete statement(though I would myself favor the forEach
honestly):
List<DistGroup> distGroups = bumperCars.stream()
.collect(Collectors.groupingBy(BumperCar::getSize,
Collectors.groupingBy(BumperCar::getColor,
Collectors.mapping(BumperCar::getCarCode, Collectors.toList()))))
.entrySet()
.stream()
.flatMap(a -> a.getValue().entrySet()
.stream().map(b -> new DistGroup(a.getKey(), b.getKey(), b.getValue())))
.collect(Collectors.toList());
edited Jan 18 at 13:18
answered Jan 18 at 12:38
nullpointernullpointer
46.3k1198190
46.3k1198190
add a comment |
add a comment |
You can collect by by using BiConsumer
that take (HashMap<SizeColorCombination, DistGroup> res, BumperCar bc)
as parameters
Collection<DistGroup> values = bumperCars.stream()
.collect(HashMap::new, (HashMap<SizeColorCombination, DistGroup> res, BumperCar bc) -> {
SizeColorCombination dg = new SizeColorCombination(bc.color, bc.size);
DistGroup distGroup = res.get(dg);
if(distGroup != null) {
distGroup.addCarCode(bc.carCode);
}else {
List<String> codes = new ArrayList();
distGroup = new DistGroup(bc.size, bc.color, codes);
res.put(dg, distGroup);
}
},HashMap::putAll).values();
add a comment |
You can collect by by using BiConsumer
that take (HashMap<SizeColorCombination, DistGroup> res, BumperCar bc)
as parameters
Collection<DistGroup> values = bumperCars.stream()
.collect(HashMap::new, (HashMap<SizeColorCombination, DistGroup> res, BumperCar bc) -> {
SizeColorCombination dg = new SizeColorCombination(bc.color, bc.size);
DistGroup distGroup = res.get(dg);
if(distGroup != null) {
distGroup.addCarCode(bc.carCode);
}else {
List<String> codes = new ArrayList();
distGroup = new DistGroup(bc.size, bc.color, codes);
res.put(dg, distGroup);
}
},HashMap::putAll).values();
add a comment |
You can collect by by using BiConsumer
that take (HashMap<SizeColorCombination, DistGroup> res, BumperCar bc)
as parameters
Collection<DistGroup> values = bumperCars.stream()
.collect(HashMap::new, (HashMap<SizeColorCombination, DistGroup> res, BumperCar bc) -> {
SizeColorCombination dg = new SizeColorCombination(bc.color, bc.size);
DistGroup distGroup = res.get(dg);
if(distGroup != null) {
distGroup.addCarCode(bc.carCode);
}else {
List<String> codes = new ArrayList();
distGroup = new DistGroup(bc.size, bc.color, codes);
res.put(dg, distGroup);
}
},HashMap::putAll).values();
You can collect by by using BiConsumer
that take (HashMap<SizeColorCombination, DistGroup> res, BumperCar bc)
as parameters
Collection<DistGroup> values = bumperCars.stream()
.collect(HashMap::new, (HashMap<SizeColorCombination, DistGroup> res, BumperCar bc) -> {
SizeColorCombination dg = new SizeColorCombination(bc.color, bc.size);
DistGroup distGroup = res.get(dg);
if(distGroup != null) {
distGroup.addCarCode(bc.carCode);
}else {
List<String> codes = new ArrayList();
distGroup = new DistGroup(bc.size, bc.color, codes);
res.put(dg, distGroup);
}
},HashMap::putAll).values();
answered Jan 18 at 15:00
SEY_91SEY_91
1,093615
1,093615
add a comment |
add a comment |
Check out my library AbacusUtil:
StreamEx.of(bumperCars)
.groupBy(c -> Tuple.of(c.getSize(), c.getColor()), BumperCar::getCarCode)
.toList(e -> new DistGroup(e.getKey()._1, e.getKey()._2, e.getValue());
add a comment |
Check out my library AbacusUtil:
StreamEx.of(bumperCars)
.groupBy(c -> Tuple.of(c.getSize(), c.getColor()), BumperCar::getCarCode)
.toList(e -> new DistGroup(e.getKey()._1, e.getKey()._2, e.getValue());
add a comment |
Check out my library AbacusUtil:
StreamEx.of(bumperCars)
.groupBy(c -> Tuple.of(c.getSize(), c.getColor()), BumperCar::getCarCode)
.toList(e -> new DistGroup(e.getKey()._1, e.getKey()._2, e.getValue());
Check out my library AbacusUtil:
StreamEx.of(bumperCars)
.groupBy(c -> Tuple.of(c.getSize(), c.getColor()), BumperCar::getCarCode)
.toList(e -> new DistGroup(e.getKey()._1, e.getKey()._2, e.getValue());
answered Jan 19 at 5:31
123-xyz123-xyz
60125
60125
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54254064%2fhow-to-groupby-object-properties-and-map-to-another-object-using-java-8-streams%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
2
Just as a side note,
groupingBy()
does group the values into aList
by default sotoList()
may be omitted– Lino
Jan 18 at 12:35
1
The idea of using streams is to make code more readable, more self-explanatory (at the cost of performance), or massively parallelizable without boilerplate code. It's not a modern one-solution-fits-all replacement of the old ways. The code you provided is cryptic at best. I suggest using a classic for-loop which is much cleaner in this case.
– Mark Jeronimus
Jan 18 at 12:37
@Lino You're right. I removed it.
– MC Emperor
Jan 18 at 12:53
@MarkJeronimus That's right, that's why I'm not satisfied with my current solution and looking for an elegant way to achieve just the same result—if it exists. Otherwise I will gladly revert to the classic loop.
– MC Emperor
Jan 18 at 12:58