How to merge two nested HashMaps in java - java

I have two nested maps and I try to merge two nested maps and produce a output of two merged HashMap. Below is the code that I use:
HashMap<String, Object> map = new HashMap<>();
HashMap<String, Object> map1 = new HashMap<>();
map1.put("location", "A");
HashMap<String, Object> map2 = new HashMap<>();
map2.put("geocoordinates", map1);
HashMap<String, Object> map3 = new HashMap<>();
map3.put("TEST", map2);
map.putAll(map3);
HashMap<String, Object> map11 = new HashMap<>();
map11.put("longitude", "B");
HashMap<String, Object> map12 = new HashMap<>();
map12.put("geocoordinates", map11);
HashMap<String, Object> map13 = new HashMap<>();
map13.put("TEST", map12);
map.putAll(map13);
System.out.println(map);
The output that I get is:
{TEST={geocoordinates={longitude=B}}}
But I expected both longitude and location key to be nested inside geocoordinates key but only longitude B is there. So, How can I get the combined. How achieve this?

Do it as follows:
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<String, Object> map = new HashMap<>();
HashMap<String, Object> map1 = new HashMap<>();
map1.put("location", "A");
HashMap<String, Object> map2 = new HashMap<>();
map2.put("geocoordinates", map1);
HashMap<String, Object> map3 = new HashMap<>();
map3.put("TEST1", map2);
map.putAll(map3);
HashMap<String, Object> map11 = new HashMap<>();
map11.put("longitude", "B");
HashMap<String, Object> map12 = new HashMap<>();
map12.put("geocoordinates", map11);
HashMap<String, Object> map13 = new HashMap<>();
map13.put("TEST2", map12);
map.putAll(map13);
System.out.println(map);
}
}
Output:
{TEST2={geocoordinates={longitude=B}}, TEST1={geocoordinates={location=A}}}
Reason: a Map replaces the old value when you put a new value on the same key (in your case, it is TEST). Note that HashMap.putAll() copies all of the mappings from one map into another. In your code, map.putAll(map3) is equivalent of map.put("TEST",map3). And, map.putAll(map13) is equivalent of map.put("TEST",map13) which replaces the earlier value, map3 because of the same key, TEST.
Update: Adding the following update based on the new requirement mentioned in your comment
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Main {
public static void main(String[] args) {
HashMap<String, Object> map = new HashMap<>();
List<HashMap> list=new ArrayList<HashMap>();
HashMap<String, Object> map1 = new HashMap<>();
map1.put("location", "A");
list.add(map1);
HashMap<String, Object> map11 = new HashMap<>();
map11.put("longitude", "B");
list.add(map11);
HashMap<String, Object> map2 = new HashMap<>();
map2.put("geocoordinates", list);
map.put("TEST",map2);
System.out.println(map);
}
}
Output:
{TEST={geocoordinates=[{location=A}, {longitude=B}]}}
Another way of doing it as follows:
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HashMap<String, Object> map1 = new HashMap<>();
map1.put("location", "A");
map1.put("longitude", "B");
HashMap<String, Object> map2 = new HashMap<>();
map2.put("geocoordinates", map1);
HashMap<String, Object> map = new HashMap<>();
map.put("TEST", map2);
System.out.println(map);
}
}
Output:
{TEST={geocoordinates={location=A, longitude=B}}}

The behavior you're seeing is correct – namely, if you have a map and store a new value for an existing key, the old value will be lost.
Here's a simple example that isolates what you're doing. In this code, the initial value "one" will be overwritten by "two". This is how maps work. In your case, you're using other maps instead of simple strings, but the behavior is the same – you have one value and you're replacing it with another value.
HashMap<String, Object> map = new HashMap<>();
map.put("TEST", "one");
map.put("TEST", "two");
To retain both "one" and "two", you need to either use different keys (ex: "TEST1" and "TEST2"), or alter one of the nested maps stored under "TEST", or introduce an altogether different data structure (such as a java.util.Set).

Related

Java 8 List<Map<String, Object>> to List<Map<String, Object>> group by key and count by value

I have the following list of maps
List<Map<String, Object>> listBeforeGroup = new ArrayList<Map<String, Object>>();
Map<String, Object> m1 = new HashMap<String, Object>();
m1.put("company", "LG");
m1.put("billType", "A");
m1.put("billPeriod", "09-2018");
Map<String, Object> m2 = new HashMap<String, Object>();
m2.put("company", "LG");
m2.put("billType", "A");
m2.put("billPeriod", "09-2018");
Map<String, Object> m3 = new HashMap<String, Object>();
m3.put("company", "LG");
m3.put("billType", "A");
m3.put("billPeriod", "09-2018");
Map<String, Object> m4 = new HashMap<String, Object>();
m4.put("company", "LG");
m4.put("billType", "B");
m4.put("billPeriod", "01-2019");
Map<String, Object> m5 = new HashMap<String, Object>();
m5.put("company", "LG");
m5.put("billType", "B");
m5.put("billPeriod", "01-2019");
Map<String, Object> m6 = new HashMap<String, Object>();
m6.put("company", "Samsung");
m6.put("billType", "A");
m6.put("billPeriod", "10-2018");
Map<String, Object> m7 = new HashMap<String, Object>();
m7.put("company", "Samsung");
m7.put("billType", "A");
m7.put("billPeriod", "10-2018");
Map<String, Object> m8 = new HashMap<String, Object>();
m8.put("company", "Samsung");
m8.put("billType", "B");
m8.put("billPeriod", "11-2018");
listBeforeGroup.add(m1);listBeforeGroup.add(m2);
listBeforeGroup.add(m3);listBeforeGroup.add(m4);
listBeforeGroup.add(m5);listBeforeGroup.add(m6);
How do I get this output?
//Desired Output - List<Map<String, Object>>
//{company=LG, billType=A, billPeriod=09-2018, count=3}
//{company=LG, billType=B, billPeriod=01-2019, count=2}
//{company=Samsung, billType=A, billPeriod=10-2018, count=2}
//{company=Samsung, billType=B, billPeriod=11-2018, count=1}
I tried this, using java 8 streams, but I can't get the desired output
List<Map<String, Object>> listAfterGroup = listBeforeGroup.stream().map(m -> m.entrySet().stream().collect(Collectors.toMap(p -> p.getKey(), p - > p.getValue()))).collect(Collectors.toList());
And tried this, btw this solution gives a map but I don't want this
Map<Object, Long> listAfterGroup = listBeforeGroup.stream().flatMap(m -> m.entrySet().stream()).collect(Collectors.groupingBy(Map.Entry::getKey,Collectors.counting()));
I want to group the maps by the key "billPeriod" for example, and count items by the values, then generate a new list of maps.
You can create a class Company and then subsequent operations become much simpler.
class Company {
String company;
String billType;
String billPeriod;
public Company(String company, String billType, String billPeriod) {
this.company = company;
this.billType = billType;
this.billPeriod = billPeriod;
}
// getters, setters, toString, etc
}
Initialize the list :
List<Company> list = new ArrayList<>();
list.add(new Company("LG", "A", "09-2018"));
list.add(new Company("LG", "A", "09-2018"));
list.add(new Company("LG", "A", "09-2018"));
list.add(new Company("LG", "B", "01-2019"));
list.add(new Company("LG", "B", "01-2019"));
list.add(new Company("Samsung", "A", "10-2018"));
list.add(new Company("Samsung", "A", "10-2018"));
list.add(new Company("Samsung", "B", "11-2018"));
Now for an example, you can group by company name :
Map<String, Long> map = list.stream().collect(
Collectors.groupingBy(Company::getCompany,
Collectors.mapping((Company c) -> c, Collectors.counting())));
Now it becomes much easier to perform other operations as you desire. Also, here instead of creating 8 maps you end up dealing with just 1 list.
It's really difficult to grouping and counting a map because your map data will be changed after you increase your counter value. Therefore, you must save the original data of the map, and save your counter value to the another map. Join 2 maps after your counting process is complete.
Map<Map<String, Object>, Long> counterData = listBeforeGroup.stream().collect(Collectors.groupingBy(m -> m, Collectors.counting()));
List<Map<String, Object>> listAfterGroup = new ArrayList<>();
for (Map<String, Object> m : counterData.keySet()) {
Map<String, Object> newMap = new HashMap<>(m);
newMap.put("count", counterData.get(m));
listAfterGroup.add(newMap);
}
Update Java 8 approach, not easy to debug
List<Map<String, Object>> listAfterGroup = listBeforeGroup.stream().collect(Collectors.groupingBy(m -> m, Collectors.counting())).entrySet().stream().map(e -> {
Map<String, Object> newMap = e.getKey();
newMap.put("count", e.getValue());
return newMap;
}).collect(Collectors.toList());
My approach with java 8:
Function<Map<String, Object>, String> createHashKey = map ->
map.values().stream()
.map(val -> String.valueOf(val))
.collect(Collectors.joining());
BinaryOperator<Map<String, Object>> mergeDuplicate = (map1, map2) -> {
int incrementedCount = (int)map1.get("count") + 1;
map1.put("count", incrementedCount);
return map1;
};
List<Map<String, Object>> listAfterGroup = listBeforeGroup.stream()
.collect(Collectors.toMap(createHashKey, el -> {
el.put("count", 1);
return el;
},mergeDuplicate))
.values().stream()
.collect(toList());
Maybe not the most succinct solution but I think quite readable and easy to follow the logic of the code.

Updating a certain value of an hashmap containing an hashmap in Java

I have an outerMap which contains an innerMap for each key it got. At first, every innerMap is the same (here, they contain {1=1}.
I want to change the value of one certain innermap, for a certain key.
Here is my code:
public class HelloWorld
{
public static void main(String args[]){
HashMap<String, HashMap<String, Integer>> outerMap = new HashMap<String, HashMap<String, Integer>>();
HashMap<String, Integer> innerMap = new HashMap<String, Integer>();
outerMap.put("1001",innerMap);
outerMap.put("1002",innerMap);
outerMap.put("1003",innerMap);
innerMap.put("1", 1);
//My attempt to change only one innermap;
Map<String, Integer> map_to_change = outerMap.get("1001");
map_to_change.put("1", 0);
//And then I print them to see if it's working;
for(Map.Entry map : outerMap.entrySet() )
{
System.out.println(map.getKey()+" "+map.getValue());
}
}
}
However, the output here is
1003 {1=0}
1002 {1=0}
1001 {1=0}
Which shows that my code changes all innermaps, and not only the one linked with the key "1001".
What can I do?
You are pointing the same innerMap object in the outerMap,
outerMap.put("1001",new HashMap<String, Integer>());//create separate maps
outerMap.put("1002",new HashMap<String, Integer>());
outerMap.put("1003",new HashMap<String, Integer>());
HashMap<String, Integer> innerMap =outerMap.get("1001");//get the map you want to put value
innerMap.put("1", 1);//assign the value
Update:
If you want to retain a copy of Map which you have already created, you can copy and create a new Map from it using putAll method,
outerMap.put("1001",copyMap(innerMap));
outerMap.put("1002",copyMap(innerMap));
outerMap.put("1003",copyMap(innerMap));
copyMap method looks like,
private static HashMap<String, Integer> copyMap(HashMap<String, Integer> innerMap){
HashMap<String, Integer> copiedInnerMap = new HashMap<String, Integer>();
copiedInnerMap.putAll(innerMap);
return copiedInnerMap;
}

Keep the value of the main map after clearing map

I have mainHolder map which contains another map holder as value I am facing problem when clearing the holder map that I am loosing the vlue of the apple key in the mainHolder map.
How can I Keep the value of the mainHolder map after Clearing the holder map?
Code
import java.util.HashMap;
import java.util.LinkedHashMap;
public class Generator {
public static void main(String[] args) {
LinkedHashMap<String, Object> holder = new LinkedHashMap<String, Object>();
final HashMap<String, LinkedHashMap<String, Object>> mainHolder = new LinkedHashMap<String, LinkedHashMap<String, Object>>();
holder.put("firstName", "Alex");
holder.put("lastName", "Cruz");
mainHolder.put("apple", holder);
holder.clear(); //After Clearing the map I am loosing the value in the mainHolder for 'apple'
holder.put("quantity", 13);
mainHolder.put("apple", holder);
System.out.println("Test");
}
}
mainHolder
should contain at the end the following:
[apple:[firstName: Fadi, lastName: Cruz, quantity:13]]
The reason this is happening is because you are using the same key.
If the map previously contains a mapping for the key, the old value
is replaced.
Update
<>.add(map) will put a reference to map in the list, so it's not a
copy. When you then do map.clear() afterwards, it erases the content
of the map in the list too, because it is the very same object. Do
<>.add(map.clone()) instead or (preferably) do map = new HashMap<>();
solution
LinkedHashMap<String, Object> holder = new LinkedHashMap<String, Object>();
final HashMap<String, LinkedHashMap<String, Object>> mainHolder = new LinkedHashMap<String, LinkedHashMap<String, Object>>();
holder.put("firstName", "Alex");
holder.put("lastName", "Cruz");
mainHolder.put("apple", holder);
holder = new LinkedHashMap<>();
LinkedHashMap<String, Object> temp = mainHolder.get("apple");
temp.put("quantity",13);
mainHolder.put("apple",temp);
System.out.println(mainHolder);

Merge Map in java restoring insertion order

I have 3 map of same type. map1 being the highest priority map, map2 a lesser priority, and map3 being the least priority.
Map<Integer, String> map1 = {2="two", 4="four"};
Map<Integer, String> map2 = {2="twotwo", 1="one",3="three"};
Map<Integer, String> map3 = {1="oneone", 5="five"};
Finally i want merged map to be like
Map<Integer, String> mergedmap = {2="two", 4="four",1="one",3="three",5="five"};
Basic algorithm to be followed
Add all entries of map1 to merged map.
Add entries of map2 to merged map, skip entry if its key is already present.
Add entries of map3 to merged map, skip entry if its key is already present.
I have tried
Map<Integer, String> mergeMap = new HashMap<Integer,String>(map3);
mergeMap.putAll(map2);
mergeMap.putAll(map1);
Outcome of this is
Map<Integer, String> mergedmap = {1="one", 2="two", 3="three", 4="four", 5="five"};
Data is correct but not in the sequence i need. How this can be done ? Below is the test class
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args){
Map<Integer, String> map1 = new HashMap<Integer, String>();
map1.put(2, "two");
map1.put(4, "four");
Map<Integer, String> map2 = new HashMap<Integer, String>();
map2.put(2, "twotwo");
map2.put(1, "one");
map2.put(3, "three");
Map<Integer, String> map3 = new HashMap<Integer, String>();
map3.put(1, "oneone");
map3.put(5, "five");
Map<Integer, String> mergeMap = new HashMap<Integer,String>(map3);
mergeMap.putAll(map2);
mergeMap.putAll(map1);
System.out.println(mergeMap);
}
}
You could use putIfAbsent method to ensure the right entries are in the map.
Map<Integer, String> mergedMap = new HashMap<>(map1);
map2.forEach((k, v) -> mergedMap.putIfAbsent(k, v);
map3.forEach((k, v) -> mergedMap.putIfAbsent(k, v);
Sorting is best done when you use the entries rather than when you are creating them:
mergedMap.entrySet().stream()
.sorted(Map.Entry.comparingByValue)
.forEach(System.out::println);
I wonder why would you want to maintain a sequence in a map but if you indeed do then you should use linkedHashMap. See this -
public static void main(String[] args) {
Map<Integer, String> map1 = new LinkedHashMap<>();
map1.put(2, "two");
map1.put(4, "four");
Map<Integer, String> map2 = new LinkedHashMap<>();
map2.put(2, "twotwo");
map2.put(1, "one");
map2.put(3, "three");
Map<Integer, String> map3 = new LinkedHashMap<>();
map3.put(1, "oneone");
map3.put(5, "five");
Iterator<Entry<Integer, String>> iterator2 = map2.entrySet().iterator();
while (iterator2.hasNext()) {
Entry<Integer, String> entry = iterator2.next();
if (!map1.containsKey(entry.getKey())) {
map1.put(entry.getKey(), entry.getValue());
}
}
System.out.println(map1);
}
OUTPUT -
{2=two, 4=four, 1=one, 3=three}
Try for map3 by yourself.

Reverse a Map with results in a Set

I am attempting to get a Map to reverse the keys and values, then combine the new values into a Set.
For example,
Map<String, String> favoriteColors = new HashMap<String, String>();
favoriteColors.put("Jacob", "Red");
favoriteColors.put("Mason", "Blue");
favoriteColors.put("Ethan", "Green");
favoriteColors.put("William", "Red"); reverseMapping(favoriteColors).entrySet()`
Would return
Red=(Jacob, William), Blue=(Mason), Green=(Ethan)
I'm not even really sure where to start with this.
I've thought about iterating through the original Map, but I can't think of a way to create new, unique Sets for every different color.
As Bohemian mentionned, I'd recommend you to use Guava's Multimap.
For example:
final Map<String, String> favoriteColors = new HashMap<>();
favoriteColors.put("Jacob", "Red");
favoriteColors.put("Mason", "Blue");
favoriteColors.put("Ethan", "Green");
favoriteColors.put("William", "Red");
final Multimap<String, String> map = Multimaps.forMap(favoriteColors);
final Multimap<String, String> inverted = ArrayListMultimap.create();
Multimaps.invertFrom(map, inverted);
System.out.println(inverted);
Output:
{Blue=[Mason], Red=[William, Jacob], Green=[Ethan]}
Just in plain Java:
private Map<String, Set<String>> reverseMap(Map<String, String> map) {
Collection<String> values = map.values();
Iterator<String> it = values.iterator();
Map<String, Set<String>> reverseMap = new HashMap<String, Set<String>>();
while(it.hasNext()) {
String val = it.next();
Set<String> set = new HashSet<String>();
reverseMap.put(val, set);
}
Set<String> keys = map.keySet();
for (String key:keys) {
String val = map.get(key);
Set<String> set = reverseMap.get(val);
set.add(key);
}
return reverseMap;
}
The following code:
Map<String, String> map = new HashMap<String, String>();
map.put("John", "Red");
map.put("Pepe", "Blue");
map.put("Myself", "Red");
Map<String, Set<String>> out = reverseMap(map);
System.out.println(out);
Produces:
{Blue=[Pepe], Red=[John, Myself]}

Categories

Resources