Merge Map in java restoring insertion order - java

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.

Related

Compare maps stored in a list in java

Is possible to compare many maps stored in an arrayList?
I mean:
public static void main(String[] args) {
List<Map<String, Object>> mapList = new ArrayList<Map<String, Object>>();
Map<String, Object> entry1 = new HashMap<>();
Map<String, Object> entry2 = new HashMap<>();
Map<String, Object> entry3 = new HashMap<>();
Map<String, Object> entry4 = new HashMap<>();
entry1.put("entity1", "19820271");
entry2.put("entity1", "19820271");
entry3.put("entity2", "19820272");
entry4.put("entity2", "19820272");
mapList.add(entry1);
mapList.add(entry2);
mapList.add(entry3);
mapList.add(entry4);
groupMaps(mapList);
}
private static List<Map<String, Object>> groupMaps(List<Map<String, Object>> mapList) {
List<Map<String, Object>> resultante = new ArrayList<>();
for (int i = 0; i < mapList.size(); i++) {
Map<String, Object> map1= mapList.get(0);
for (Map.Entry<String, Object> entry : mapList.get(i).entrySet()) {
if (entry.getValue().equals(map1.get("entity"))) {
resultante.add(mapList.get(i));
}
}
}
return mapList;
}
In the "groupMaps" method I need to compare each map so I can group by match, for example:
map1 matches map2
map3 matches map7
map4 matches map5
and so on...
Is there any way to do it?
The most performant way to do that is to create a histogram of frequencies. I.e. a map that will use your maps from the source list as keys and values will represent the number of occurrences of each map in the list.
That is how it could be implemented using Stream API.
private static List<Map<String, Object>> groupMaps(List<Map<String, Object>> mapList) {
return mapList.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet().stream()
.filter(entry -> entry.getValue() > 1)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
The same result can be achieved using an iterative approach. The main logic remains the same:
create a map of frequencies;
then extract duplicates based on it.
private static List<Map<String, Object>> groupMaps(List<Map<String, Object>> mapList) {
Map<Map<String, Object>, Integer> histogram = getHist(mapList);
List<Map<String, Object>> duplicates = new ArrayList<>();
for (Map.Entry<Map<String, Object>, Integer> entry: histogram.entrySet()) {
if (entry.getValue() > 1) {
duplicates.add(entry.getKey());
}
}
return duplicates;
}
private static Map<Map<String, Object>, Integer> getHist(List<Map<String, Object>> mapList) {
Map<Map<String, Object>, Integer> histogram = new HashMap<>();
for (Map<String, Object> next: mapList) {
histogram.merge(next, 1, Integer::sum);
}
return histogram;
}
Output for your example (both versions)
[{entity2=19820272}, {entity1=19820271}]
Note, if you are working on this problem just for exercise it's OK, but you should be aware that using Object as a generic type is as good as not using generics at all. If your map is intended to store string values then it has to be Map<String, String>.

How to merge two nested HashMaps in 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).

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

Get Map key and value from two different Map with key and vlue in java

There are two Map, first one contains student id and name. second one contains student id and status of 'Pass' or 'Fail'. There is a method which takes two parameters of Map type and returns Map.
//1st Map
Map<Integer, String> map1 = new HashMap<>();
map1.put(1, "x");
map1.put(2, "y");
map1.put(3, "z");
map1.put(4, "a");
map1.put(5, "b");`
and
//2nd Map
Map<Integer, String> map2 = new HashMap<>();
map2.put(1, "fail");
map2.put(2, "fail");
map2.put(3, "fail");
map2.put(4, "pass");
map2.put(5, "pass");`
//the method
public Map<Integer, String>findFaildStudent(Map<Integer, String>map1,Map<Integer, String>map2){
returns Map<Integer, String>;
}
So, my question is how to find the record of failed students. I did try it myself but didn't succeed. Any help is appreciated.
What I tried so far
public Map<Integer, String>findFaildStudent(Map<Integer, String>map1,Map<Integer, String>map2){
Integer key = null;
String value = null;
Iterator<Entry<Integer, String>> iterator = map1.entrySet().iterator();
while(iterator.hasNext()){
Entry<Integer, String> next = iterator.next();
key = next.getKey();
}
Iterator<Entry<Integer, String>> iterator2 = map2.entrySet().iterator();
while(iterator2.hasNext()){
Entry<Integer, String> next = iterator2.next();
value=next.getValue();
}
Map<Integer,String> hashMap = new HashMap<>();
hashMap.put(key, value);
return hashMap;
}
public Map<String, String> findFaildStudent(Map<Integer, String>map1,Map<Integer, String>map2){
Map<String, String> failed = new HashMap<>();
for (Map.Entry<Integer, String> k : map2.entrySet()) {
if(k.getValue().equals("fail")){
failed.put(map1.get(k.getKey()), map2.get(k.getValue()));
}
}
return failed;
}
//returns... ("x","failed"), ("y", "failed"), ("z", "failed")
So basically you just need to iterate through the 2nd map since it contain failed student, then you get the key and use that key to get the value of map1.
You cannot concate the keys because the key is unique so if you want combine both map together it is better to create another map using map1(value) as key and map2(value) as value. Hope I understand your question but if you only want map2 of failed student then try below.
public Map<Integer, String> findFaildStudent(Map<Integer, String>map1,Map<Integer, String>map2){
Map<Integer, String> failed = new HashMap<>();
for (Map.Entry<Integer, String> k : map2.entrySet()) {
if(k.getValue().equals("fail")){
failed.put(k.getKey(), map1.get(k.getKey()));
}
}
return failed;
//returns... (1,"x"), (2, "y"), (3, "z")
map1.entrySet().stream()
.filter(entry -> "fail".equals(map2.get(entry.getKey())))
.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));

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