Reputation: 334
I just started with learning and implementing collections via the Java 8 stream API. I have one class:
public class Discount {
int amount;
String lastMarketingRegion;
Discount (int amount, String lastMarketingRegion) {
this.amount = amount;
this.lastMarketingRegion= lastMarketingRegion;
}
public int getAmount() { return amount; }
public String getLastMarketingRegion() { return lastMarketingRegion; }
public String toString() {
return String.format("{%s,\"%s\"}", amount, lastMarketingRegion);
}
}
And I am given with the following:
Map<String, Discount> prepaid = new HashMap<String, Discount>();
prepaid.put("HAPPY50", new Discount(100, "M1"));
prepaid.put("LUCKY10", new Discount(10, "M2"));
prepaid.put("FIRSTPAY", new Discount(20, "M3"));
Map<String, Discount> otherBills = new HashMap<String, Discount>();
otherBills.put("HAPPY50", new Discount(60, "M4"));
otherBills.put("LUCKY10", new Discount(7, "M5"));
otherBills.put("GOOD", new Discount(20, "M6"));
List<Map<String, Discount>> discList = new ArrayList<Map<String, Discount>>();
discList.add(prepaid);
discList.add(otherBills);
So, basically I have a list of Discount
maps of all discount codes for different payment types.
Requirement is to create a single map with all the discount codes across all payment types with sum_of_amount
and the last_region
:
Map<String, Discount> totalDiscounts =
{LUCKY10={17, "M5"}, FIRSTPAY={20, "M3"}, HAPPY50={160, "M4"}, GOOD={20, "M6"}}
I am able to get:
Map<String, Integer> totalDiscounts =
{LUCKY10=17, FIRSTPAY=20, HAPPY50=160, GOOD=20}
by using the following code:
Map<String, Integer> afterFormatting = discList.stream()
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.summingInt(map -> map.getValue().amount)));
but I need a Discount
object also with the region.
I need a collection of Discount objects where the amount is the total of the amounts of same key and region is from otherBills.
Any help would be much appreciated. Thank You.
Edit 1 - For the sake of simplicity, please consider lastMarketingRegion to have same value for a discount code. I also tried to explain it via diagram -
Upvotes: 2
Views: 4995
Reputation: 17880
From comments
Why do you expect "LUCKY10" - "M5" when you have "M2" and "M5" entries for LUCKY10?
because otherBills has more priority than prepaid
You can use Collectors.toMap
for this. The last argument to it is the mergeFunction
that merges two Discounts that had same String key in the map.
Map<String, Discount> totalDiscounts = discList.stream()
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(discount1, discount2) -> new Discount(discount1.getAmount() + discount2.getAmount(),
discount2.getLastMarketingRegion())));
Since the stream generated out of a list is ordered, the discount2
Discount will be the one from the otherBills
map and hence I'm picking the region of it.
If you have constructed the list by adding otherBills
followed by prepaid
, then this will have a different output.
Relying on the encounter order makes this a not-a-great-solution. (If you are going to assume we process entries from the second map after processing the first, why merge them in the first place?)
See my other answer that uses Map.merge
Upvotes: 3
Reputation: 17880
If you have just two maps, then rather than going for a stream-based solution (my other answer), you can use Map.merge
for this.
Here, we make a copy of the prepaid
map. Then we iterate through the otherBills
map. For each key
Discount
object whose amount is the sum of amounts of the Discount object already present in the map (the one from prepaid
) and the current Discount object (the one from otherBill
). It takes the region of the Discount object from the otherBill
map.Map<String, Discount> result = new HashMap<>(prepaid);
otherBills.forEach((k, v) -> result.merge(k, v, (discountFromPrepaid, discountFromOtherBill) ->
new Discount(discountFromPrepaid.getAmount() + discountFromOtherBill.getAmount(),
discountFromOtherBill.getLastMarketingRegion())));
Upvotes: 2