There is a hash map within the ArrayList. the output is like below
[{A=2},{A=3},{B=1},{B=4},{A=3}]
Below I have mentioned my code sample
ArrayList<Map<String, Short>> deviceInfo = new ArrayList<>();
Map<String, Integer> rssiMapper = new HashMap<>();
rssiMapper.put(device.getName(), rssi);
deviceInfo.add(rssiMapper);
I want to take mean value of A and B separately. How can I achieve that
Try it like this.
List<Map<String,Integer>> list = List.of(
Map.of("A", 2),
Map.of("A", 3),
Map.of("B", 1),
Map.of("B", 4),
Map.of("A", 3));
Map<String, Double> avgs = list.stream()
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.groupingBy(
Entry::getKey,
Collectors.averagingInt(Entry::getValue)));
System.out.println(avgs);
Prints
{A=2.6666666666666665, B=2.5}
As was suggested, if you are unfamiliar with streams, here is an iterative approach.
Map<String,Double> avgs = new HashMap<>();
Map<String,Integer> count = new HashMap<>();
for (Map<String,Integer> map : list) {
for (Entry<String,Integer> e : map.entrySet()) {
String key = e.getKey();
int value = e.getValue();
// These just either initialize or update the appropriate
// values.
avgs.compute(key, (k,v)-> v == null ? value : v + value);
count.compute(key, (k,v)->v == null ? 1 : v + 1);
}
}
// now find the averages.
for(Entry<String,Double> e : avgs.entrySet()) {
avgs.computeIfPresent(e.getKey(), (k,v)->v/count.get(e.getKey()));
}
System.out.println(avgs);
You can create maps to track the sum, count and the mean (i.e. sum / count) of the entries as shown below:
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class Main {
public static void main(String[] args) {
List<Map<String, Integer>> list = List.of(Map.of("A", 2), Map.of("A", 3), Map.of("B", 1), Map.of("B", 4),
Map.of("A", 3));
Map<String, Integer> sumMap = new HashMap<>();
Map<String, Integer> countMap = new HashMap<>();
Map<String, Double> meanMap = new HashMap<>();
for (Map<String, Integer> map : list) {
for (Entry<String, Integer> entry : map.entrySet()) {
sumMap.put(entry.getKey(), sumMap.getOrDefault(entry.getKey(), 0) + entry.getValue());
countMap.put(entry.getKey(), countMap.getOrDefault(entry.getKey(), 0) + 1);
meanMap.put(entry.getKey(),
(double) sumMap.getOrDefault(entry.getKey(), 0) / countMap.getOrDefault(entry.getKey(), 1));
}
}
// Display
System.out.println(meanMap);
}
}
Output:
{A=2.6666666666666665, B=2.5}
Related
Merge below 3 maps if values exist then replace and if the value is new then add that value, for example :-
I have a Map1 as :-
{
BMW = {
SIZE=1,
SPEED=60
},
AUDI = {
SIZE=5,
SPEED=21
},
SEAT= {
SPEED=15
}
}
And a Map2 as :-
{
Suzuki = {
WHEELS_SIZE=2,
DOORS=3
},
AUDI = {
WHEELS_SIZE=5,
SIZE=4,
DOORS=5
},
SEAT= {
DOORS=4
}
}
And Map3 as :-
{
TOYOTA = {
WHEELS_SIZE=5,
DOORS=5
},
BMW= {
SIZE=10
}
}
I have a requirement to merge all 3 above so, that final map looks like
{
BMW = {
SIZE=10,
SPEED=60
},
AUDI = {
SIZE=4,
SPEED=21,
WHEELS_SIZE=5,
DOORS=5
},
SEAT= {
SPEED=15,
DOORS=4
},
Suzuki = {
WHEELS_SIZE=2,
DOORS=3
},
TOYOTA = {
WHEELS_SIZE=5,
DOORS=5
},
}
Could someone please suggest an optimized way to achieve this ? Thanks in advance!
Here is a naïve implementation:
public static void main(String[] args){
Map<String, Integer> BMW1 = new HashMap<>();
BMW1.put("SIZE", 1);
BMW1.put("SPEED", 60);
Map<String, Integer> AUDI1 = new HashMap<>();
AUDI1.put("SIZE", 5);
AUDI1.put("SPEED", 21);
Map<String, Integer> SEAT1 = new HashMap<>();
SEAT1.put("SPEED", 15);
Map<String, Map<String, Integer>> one =
Map.of("BMW", BMW1, "AUDI", AUDI1, "SEAT", SEAT1);
Map<String, Integer> SUZUKI2 = new HashMap<>();
SUZUKI2.put("WHEEL_SIZE", 2);
SUZUKI2.put("DOORS", 3);
Map<String, Integer> AUDI2 = new HashMap<>();
AUDI2.put("WHEELS_SIZE", 5);
AUDI2.put("SIZE", 4);
AUDI2.put("DOORS", 5);
Map<String, Integer> SEAT2 = new HashMap<>();
SEAT2.put("DOORS", 4);
Map<String, Map<String, Integer>> two =
Map.of("SUZUKI", SUZUKI2, "AUDI", AUDI2, "SEAT", SEAT2);
Map<String, Integer> TOYOTA3 = new HashMap<>();
TOYOTA3.put("WHEEL_SIZE", 5);
TOYOTA3.put("DOORS", 5);
Map<String, Integer> BMW3 = new HashMap<>();
BMW3.put("SIZE", 10);
Map<String, Map<String, Integer>> three =
Map.of("TOYOTA", TOYOTA3, "BMW", BMW3);
Map<String, Map<String, Integer>>[] maps = new Map[]{ one, two, three };
Map<String, Map<String, Integer>> mergedMaps = mergeMaps(maps);
printMap(mergedMaps);
}
public static Map<String, Map<String, Integer>> mergeMaps(Map<String, Map<String, Integer>>[] maps) {
Map<String, Map<String, Integer>> mergedMap = new HashMap<>();
for(Map<String, Map<String, Integer>> map : maps) {
for(Map.Entry<String, Map<String, Integer>> entry : map.entrySet()) {
if(mergedMap.containsKey(entry.getKey())){
Map<String, Integer> mergedCarStats = mergedMap.get(entry.getKey());
Map<String, Integer> carStats = entry.getValue();
mergedCarStats.putAll(carStats);
} else {
mergedMap.put(entry.getKey(), entry.getValue());
}
}
}
return mergedMap;
}
public static void printMap(Map<String, Map<String, Integer>> car) {
System.out.println("{");
for(Map.Entry<String, Map<String, Integer>> entry: car.entrySet()) {
System.out.println(String.format(" %s = {", entry.getKey()));
printStats(entry.getValue());
System.out.println(" },");
}
System.out.println("}");
}
public static void printStats(Map<String, Integer> stats) {
for(Map.Entry<String, Integer> entry : stats.entrySet()) {
System.out.println(String.format(" '%s': %d,", entry.getKey(), entry.getValue()));
}
}
If you run it you will get this output:
{
SUZUKI = {
'WHEEL_SIZE': 2,
'DOORS': 3,
},
SEAT = {
'SPEED': 15,
'DOORS': 4,
},
AUDI = {
'SPEED': 21,
'DOORS': 5,
'SIZE': 4,
'WHEELS_SIZE': 5,
},
TOYOTA = {
'WHEEL_SIZE': 5,
'DOORS': 5,
},
BMW = {
'SPEED': 60,
'SIZE': 10,
},
}
Which is what you asked for. There are a few things to consider though. Using loops this way is inefficient because the time complexity is going to be O(n^2) as there are 2 for loops. There are built in functions for merging maps since Java 8 which could be worth exploring.
Merge the three maps using java8
Map<String, Map<String, Integer>> resultMap = Stream.of(map1, map2, map3)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> new HashMap<String,Integer>(e.getValue()),
(key, value) -> {key.putAll(value); return key;}
));
Output:
{SEAT={SPEED=15, DOORS=4}, TOYOTA={WHEELS_SIZE=5, DOORS=5}, Suzuki={WHEELS_SIZE=2, DOORS=3}, AUDI={SPEED=21, WHEELS_SIZE=5, DOORS=5, SIZE=4}, BMW={SPEED=60, SIZE=10}}
Suppose I have two hash maps:
HashMap <String, Integer> h1;
h1.put("hi", 30);
h1.put("hi2",20);
h1.put("hi3",10);
h1.put("hi4",20);
HashMap <String, Integer> h2;
h2.put("hi", 20);
h2.put("hi2", 20);
h2.put("hi3", 20);
h2.put("hi4", 20);
My question is, if I do the following
h2.putAll(h1);
How could I update the values of h2 to be the sum, instead of just overwriting it? That is I want
[{"hi"=50}]
[{"hi2"=40}]
[{"hi3"=30}]
[{"hi4"=40}]
Instead of this
[{"hi"=30}]
[{"hi2"=20}]
[{"hi3"=10}]
[{"hi4"=20}]
Note: no functional constructs (including lambdas) and external libraries are allowed
You can use merge method for that:
h1.forEach((key, value) -> h2.merge( key, value, Integer::sum));
System.out.println(h2);
The old fashion way:
for(String key : h1.keySet()){
Integer v1 = h1.get(key);
Integer v2 = h2.get(key);
h2.put(key, (v2 == null) ? v1 : v1 + v2);
}
System.out.println(h2);
In traditional Java (before functional constructs), you would just iterate over the map and get the values from the other map
Map<String, Integer> h1 = new HashMap<>();
h1.put("hi", 30);
Map<String, Integer> h2 = new HashMap<>();
h2.put("hi", 20);
for (Map.Entry<String, Integer> entry : h2.entrySet()) {
String key = entry.getKey();
Integer toAdd = h1.get(key);
if (toAdd != null) {
entry.setValue(entry.getValue() + toAdd);
}
}
System.out.println("h1 = " + h1);
System.out.println("h2 = " + h2);
Which prints
h1 = {hi=30}
h2 = {hi=50}
To go further, if the expected result should be that h2 should also contain every non-matching key from h1, then you can use the following
Map<String, Integer> h1 = new HashMap<>();
h1.put("hi", 30);
h1.put("hii", 40);
Map<String, Integer> h2 = new HashMap<>();
h2.put("hi", 20);
for (Map.Entry<String, Integer> entry : h1.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
Integer toPossiblyMerge = h2.get(key);
if (toPossiblyMerge == null) {
h2.put(key, value);
} else {
h2.put(key, value + toPossiblyMerge);
}
}
System.out.println("h1 = " + h1);
System.out.println("h2 = " + h2);
Which prints
h1 = {hi=30, hii=40}
h2 = {hi=50, hii=40}
You can merge the two maps as shown below:
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, Integer> h1 = new HashMap<>();
h1.put("hi", 30);
Map<String, Integer> h2 = new HashMap<>();
h2.put("hi", 20);
Map<String, Integer> h3 = new HashMap<>(h1);
h2.forEach(
(key, value) -> h3.merge(key, value, (v1, v2) -> v1 + v2)
);
System.out.println(h3);
}
}
Output:
{hi=50}
Non-Lambda solution:
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class Main {
public static void main(String[] args) {
Map<String, Integer> h1 = new HashMap<>();
h1.put("hi", 30);
Map<String, Integer> h2 = new HashMap<>();
h2.put("hi", 20);
Map<String, Integer> h3 = new HashMap<>(h1);
for (Entry<String, Integer> entry : h2.entrySet()) {
String key = entry.getKey();
h3.put(key, entry.getValue() + h1.getOrDefault(key, 0));
}
System.out.println(h3);
}
}
Output:
{hi=50}
So i was wondering how and if it was possible using Hashmaps, one containing only strings and the other containing a similar string key but a float value, to compare them and then from that comparison print out the amount of similar values in the first hashmap, and then the float from the second hashmap added together when their keys/values line up. Example below that should clarify what i mean and to do this dynamically.
CODE
HashMap<String, String> hmap = new HashMap<>();
HashMap<String, Float> h2map = new HashMap<>();
hmap.put("order1", "pending");
hmap.put("order2", "cancelled");
hmap.put("order3", "pending");
h2map.put("order1", (float) 19.95);
h2map.put("order2", (float) 19.95);
h2map.put("order3", (float) 39.9);
Set <String> singles = new HashSet<>(h2map.values());
if(h2map.keySet().equals(hmap.keySet())) {
// below prints out the states and amount of the states but how can i get the float values from hmap to be added together for the similar states and printed with their respective state?
for(String element : singles) {
System.out.println(element + ": " + Collections.frequency(hmap.values(), element));
}
}
Current Output
pending: 2
cancelled: 1
Desired Output
pending: 2 $59.85
cancelled 1 $19.95
Is this what you want?
public static void main(String[] args) {
HashMap<String, String> hmap = new HashMap<>();
HashMap<String, Float> h2map = new HashMap<>();
hmap.put("order1", "pending");
hmap.put("order2", "cancelled");
hmap.put("order3", "pending");
h2map.put("order1", 19.95f);
h2map.put("order2", 19.95f);
h2map.put("order3", 39.9f);
Map<String, DoubleSummaryStatistics> grouping = hmap
.entrySet()
.stream()
.collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.summarizingDouble(e -> h2map.get(e.getKey()))));
grouping.forEach((key, value) -> System.out.println(key + ": " + value.getCount() + " " + value.getSum()));
}
Note that there is no summarizing statistics collector for BigDecimal and this code works only with Float or Double. But for money calculations better use BigDecimal. It's possible to implement the custom collector if needed )
I have replaced the use of Float with BigDecimal for better accuracy. Also I used two maps, one for holding the summed value and the other for count:
public static void main(String[] args) {
HashMap<String, String> hmap = new HashMap<>();
HashMap<String, BigDecimal> h2map = new HashMap<>();
hmap.put("order1", "pending");
hmap.put("order2", "cancelled");
hmap.put("order3", "pending");
h2map.put("order1", new BigDecimal("19.95"));
h2map.put("order2", new BigDecimal("19.95"));
h2map.put("order3", new BigDecimal("39.9"));
//Map for holding sum
HashMap<String, BigDecimal> sum = new HashMap<>();
for(String key : h2map.keySet()){
if(hmap.get(key) != null){
String value = hmap.get(key);
if(sum.get(value) == null){
sum.put(value, h2map.get(key));
}else{
sum.put(value, (sum.get(value).add(h2map.get(key))));
}
}
}
//Map for holding count
HashMap<String, BigDecimal> countMap = new HashMap<>();
for(Iterator<Map.Entry<String, BigDecimal>> itr = sum.entrySet().iterator(); itr.hasNext(); ){
Map.Entry<String, BigDecimal> entry = itr.next();
String key = entry.getKey();
int count = Collections.frequency(hmap.values(), key);
countMap.put((key + count), sum.get(key));
itr.remove();
}
//For GC
sum = null;
countMap.forEach((k, v) -> System.out.println(k + " " + v));
}
i want to determine the of the "columns" in "rows" or or better: Build sum of a list of maps like List> rows
Is it somehow possible to sum all values of each distinct column? The function shall return a Map with the column as key and the sum of all values as value.
summMap.get("columname")
Let's assume i have the following list of maps:
List<Map<String, Long>> mapList = new ArrayList();
Map<String, Object> map1 = new HashMap<>();
Map<String, Object> map2 = new HashMap<>();
Map<String, Object> map3 = new HashMap<>();
map1.put("col1", 90);
map1.put("col2", 50);
map1.put("col3", 10);
map2.put("col1", 90);
map2.put("col2", 50);
map2.put("col3", 10);
map3.put("col1", 90);
map3.put("col2", 50);
map3.put("col3", 10);
mapList.add(map1);
mapList.add(map2);
mapList.add(map3);
Map<String, Long> sum = mapList.stream().distinct().sum() // Example
// result i'm awaiting/expecting
Long sumVal1 = sum.get("col1"); // 270
Long sumVal2 = sum.get("col2"); // 150
Long sumVal3 = sum.get("col3"); // 30
Long sumVal = sum.get("col1");
It’s as simple as
Map<String, Long> sum = mapList.stream()
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, Long::sum));
Holger has already provided a clean solution, but I think you can also try flatMap and groupingBy as:
Map<String, Long> sum = mapList.stream().flatMap(map -> map.entrySet().stream())
.collect(groupingBy(Map.Entry::getKey, summingLong(Map.Entry::getValue)));
The whole solution to your question:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.util.stream.Collectors.*;
public class ListMapSum {
public static void main(String... args) {
List<Map<String, Long>> mapList = new ArrayList();
Map<String, Long> map1 = new HashMap<>();
Map<String, Long> map2 = new HashMap<>();
Map<String, Long> map3 = new HashMap<>();
map1.put("col1", 90L);
map1.put("col2", 50L);
map1.put("col3", 10L);
map2.put("col1", 90L);
map2.put("col2", 50L);
map2.put("col3", 10L);
map3.put("col1", 90L);
map3.put("col2", 50L);
map3.put("col3", 10L);
mapList.add(map1);
mapList.add(map2);
mapList.add(map3);
Map<String, Long> sum = mapList.stream().flatMap(map -> map.entrySet().stream())
.collect(groupingBy(Map.Entry::getKey, summingLong(Map.Entry::getValue)));
Long sumVal1 = sum.get("col1"); // 270
Long sumVal2 = sum.get("col2"); // 150
Long sumVal3 = sum.get("col3"); // 30
}
}
This doesn't support parallel execution, but could do by modifying the last argument in reduce:
private static Map<String, Long> reduceLongs(List<Map<String, Long>> maps) {
return maps.stream()
.flatMap(map -> map.entrySet().stream())
.reduce(new HashMap<>(), (map, e) -> {
map.compute(e.getKey(), (k ,v) -> v == null ? e.getValue() : e.getValue() + v);
return map;
}, (m1, m2) -> { throw new UnsupportedOperationException(); });
}
And a passing test:
final List<Map<String, Long>> maps = new ArrayList<>();
Map<String, Long> map1 = new HashMap<>();
Map<String, Long> map2 = new HashMap<>();
map1.put("col1", 90L);
map1.put("col2", 50L);
map2.put("col1", 90L);
map2.put("col2", 50L);
map2.put("col3", 100L);
maps.add(map1);
maps.add(map2);
final Map<String, Long> sums = reduceLongs(maps);
assertEquals(180L, sums.get("col1").longValue());
assertEquals(100L, sums.get("col2").longValue());
assertEquals(100L, sums.get("col3").longValue());
This gives the same answer even after changing the values
Here is the simple solution, it will give the result as per your requirement:
List<Map<String, Long>> mapList = new ArrayList();
Map<String, Long> map1 = new HashMap<>();
Map<String, Long> map2 = new HashMap<>();
Map<String, Long> map3 = new HashMap<>();
map1.put("col1", 90L);
map1.put("col2", 50L);
map1.put("col3", 10L);
map2.put("col1", 90L);
map2.put("col2", 50L);
map2.put("col3", 10L);
map3.put("col1", 90L);
map3.put("col2", 50L);
map3.put("col3", 10L);
mapList.add(map1);
mapList.add(map2);
mapList.add(map3);
Map<String, Long> sum = new HashMap<>();
mapList.forEach(map -> map.keySet().forEach(
s -> {
mapList.stream()
.collect(Collectors.groupingBy(foo -> s,
Collectors.summingLong(foo -> map.get(s)))).forEach(
(id, sumTargetCost) ->
sum.put(s, sumTargetCost)
);
}
));
Long sumVal1 = sum.get("col1"); // 270
Long sumVal2 = sum.get("col2"); // 150
Long sumVal3 = sum.get("col3"); // 30
System.out.println("SumVal1: " + sumVal1 + ", SumVal2: " + sumVal2 + ", SumVal3: " + sumVal3);
I have two HashMap<String, Integer> How can I get the average of the values?
HashMap<String, Integer> map1 = ...
map1.put("str1", 7);
map1.put("str2", 4);
HashMap<String, Integer> map2 = ...
map2.put("str1", 3);
map2.put("str2", 2);
The expected output is:
("str1") = 5;
("str2") = 3;
I am able to retrieve the sum of two maps as follows:
map2.forEach((k, v) -> map1.merge(k, v, Integer::sum));
But how can I retrieve the average of two maps using Java 8?
Update:
At the request of # I am posting a larger portion of my code:
HashMap<String, HashMap<String, Double>> map;
HashMap<String, Double> map2 = new HashMap<String, Double>();
map = func1();
map = func2();
map = func3();
for (Entry<String, HashMap<String, Double>> entry : map.entrySet()) {
String key = entry.getKey();
HashMap<String, Double> mp = map.get(key);
mp.forEach((k, v) -> map2.merge(k, v, (t, u) -> (t + u) / 2));
for (Entry<String, Double> entry1 : mp.entrySet()) {
StringfieldName = entry1.getKey();
Double score= entry1.getValue();
System.out.println(fieldName.toString() + " = " + score);
}
}
return map2;
}
Did you tried to do this :
map1.forEach((k, v) -> map1.merge(k, v, (t, u) -> (t + u) / 2));
Why not take advantage of Java 8 features altogether?
double avg = Stream.of(map1.values(), map2.values())
.map(set -> set.stream().collect(Collectors.summingInt(Integer::intValue)))
.collect(Collectors.averagingDouble(Integer::doubleValue));