Get Average Value of Two+ HashMap using Java 8 - java

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));

Related

Leaderboard displaying entire list instead of pagination

I have a list of users (stored in a properties file) that have a level. I sort the users by their level and then send the sorted list back to the guild. I paginate the list, but its still showing all of the users instead of just 10 per page.
Map<String, Integer> unsortedMap = new HashMap<String, Integer>();
for (String key : prop.stringPropertyNames()) {
String value = prop.getProperty(key);
unsortedMap.put(key, Integer.valueOf(value));
}
Map<String, Integer> sortedMap = sortByValue(unsortedMap);
EmbedBuilder eb = new EmbedBuilder();
eb.setTitle("aaaaaaaaaaaa");
ArrayList<Page> pages = new ArrayList<>();
for (Map.Entry<String, Integer> entry : sortedMap.entrySet()) {
String key = entry.getKey();
int value = entry.getValue();
if (key.contains(".level")) {
User users = ctx.getJDA().retrieveUserById(key.replace(".level", ""), true).complete();
eb.addField(users.getName(), String.valueOf(value), false);
}
}
for (int i = 0; i < sortedMap.size(); i++){
pages.add(new InteractPage(eb.build()));
}
channel.sendMessageEmbeds((MessageEmbed) pages.get(0).getContent()).queue(success -> {
Pages.paginate(success, pages, true);
});
}
private static Map<String, Integer> sortByValue(Map<String, Integer> unsortedMap) {
List<Map.Entry<String, Integer>> list = new LinkedList<Map.Entry<String, Integer>>(unsortedMap.entrySet());
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
#Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return (o2.getValue().compareTo(o1.getValue()));
}
});
Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
for (Map.Entry<String, Integer> entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}
return sortedMap;
Partition the for loop into smaller parts using the for loop. Initialize the int outside of the loop and then increment the int for every entry and then divide the int by how many items you want displayed on the list. Ex:
int i = 0;
for (Map.Entry<String, Integer> entry : sortedMap.entrySet()) {
i++;
if (i % 6 == 0) {
//add page here

Hashmap within ArrayList in Java

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}

Using Hashmaps to compare keys and values and add together values based on similar keys

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));
}

Java streams sum values of a List of Maps

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);

Sorting based on all heighest number

I want to get the person having highest values from all the tables. Below is the example which i retrieve from db
Id play(count) listen(count) display(count) comment(count)
a 3 1 4 2
b 2 5 3 7
c 6 3 0 1
d 0 0 5 4
e 6 4 8 9
f 4 2 5 7
in this all counts related to same id but coming from different tables.Here I want e(6,4,8,9) as sorting output. How can i do this sorting?
Try something like this:
Map<String, List<Integer>> map = new LinkedHashMap<>();
// Assuming that you are working with query output resultset
try {
stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
List<Integer> li = new ArrayList<>();
li.add(rs.getInt("PLAY"));
li.add(rs.getInt("LISTEN"));
li.add(rs.getInt("DISPLAY"));
li.add(rs.getInt("COMMENT"));
map.put(rs.getString("ID"), li);
}
} catch (SQLException e ) {
/* SQLException handler */
} finally {
if (stmt != null) { stmt.close(); }
}
map = sortByValues(map);
for (Map.Entry<String, List<Integer>> entry: map.entrySet()) {
System.out.println(entry.getKey() + "," + entry.getValue());
}
}
public static Map<String, List<Integer>> sortByValues(Map<String, List<Integer>> map) {
List<Map.Entry<String, List<Integer>>> list =
new LinkedList<>(map.entrySet());
Collections.sort(list, new Comparator<Map.Entry<String, List<Integer>>>() {
public int compare(Map.Entry<String, List<Integer>> m1, Map.Entry<String, List<Integer>> m2) {
int sum1 = 0;
for(Integer d : m1.getValue())
sum1 += d;
int sum2 = 0;
for(Integer d : m2.getValue())
sum2 += d;
return (new Integer(sum2)).compareTo(new Integer(sum1));
}
}) ;
Map<String, List<Integer>> result = new LinkedHashMap<>();
for (Map.Entry<String, List<Integer>> entry: list) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
Note: I have a separate method sortByValues() to do the comparison. This makes code neat to read and reusable rather than implementing an anonymous block after try-catch.
What's happening here?
Our aim is to sort IDs by different values fetched from Database. I feel Map data structure to be the apt one here. Example Map which is represented in a generic way as Map<String, List<Integers>>.
Map<String, List<Integer>> map = new LinkedHashMap<>();
Creates the linked hash map to store the pair
List<Integer> li = new ArrayList<>();
Creates an Arraylist object inside while loop to store each value fetched from database. It's scope dies within this loop.
map.put(id, li);
Adds each users id and values in the format Map<String, List<Integer>>
map = sortByValues(map);
Accesses the static sortByValues() to fetch the sorted map based on values it has.
sortByValues(Map<String, List<Integer>> map)
Overrides the Comparator's compare() anonymously and performs sorting based upon values. It sums up each ID's value and does the comparison.
Mock Execution:
Map<String, List<Integer>> map = new LinkedHashMap<>();
// Map<String, List<Integer>> map = new HashMap<>();
List<Integer> li = new ArrayList<>();
li.add(1);
li.add(2);
li.add(3);
// MathUtils.sum()
map.put("a", li);
// map.put("a", 5);
List<Integer> li2 = new ArrayList<>();
li2.add(3);
li2.add(-1);
li2.add(1);
map.put("b", li2);
List<Integer> li3 = new ArrayList<>();
li3.add(10);
li3.add(-1);
li3.add(9);
map.put("c", li3);
map = sortByValues(map);
for (Map.Entry<String, List<Integer>> entry: map.entrySet()) {
System.out.println(entry.getKey() + "," + entry.getValue());
}
Result:
c,[10, -1, 9]
a,[1, 2, 3]
b,[3, -1, 1]

Categories

Resources