How Concatenate 2 Java Map<String, Object> with same keys without override - java

How Concatenate 2 Java Map<String, Object> with same keys without override
Hi, I'm trying to concatenate 2 maps in java, and try with putAll() but this method override values with same key
Example initial maps:
{Foo: "A", Bar: "B"}
{Foo: "C", Bar: "D"}
I want some like this:
{ Foo0: "A", Bar0: "B", Foo1: "C", Bar1: "D" }

It's not entirely clear what your requirements are, as #JohnBollinger pointed out. But if we take your example very simplistically, you just want to append an index to the end of each key, in which case you can do something like this:
static Map<String, Object> merge(Map<String, Object> map1, Map<String, Object> map2) {
Map<String, Object> result = new HashMap<>();
map1.forEach((k, v) -> result.put(k + "0", v));
map2.forEach((k, v) -> result.put(k + "1", v));
return result;
}

I advise you considering the following structure: Map<String, List<String>>.
In that case, you needn't override a key, just put a value to the end of the list by this key. Also, you don't have to think about how to name new keys (Bar0, Bar1). Finally, if a new map appears (third, fourth), its values will be added without issues.
Some pseudo-code for you:
map.put(key,
map.get(key) == null ? newListAddValueReturnList :
getListAddValueReturnList);
Well, the code also is written for you:
String v = map.get(key);
if(v == null)
map.put(key, new ArrayList<String>() {{ add(value); }});
else
v.add(value);

Related

Store all values of Duplicate Key in Map

Map<String,Integer> map=new HashMap<String,Integer>();
map.put("A",1);
map.put("A",2);
map.put("A",3);
map.put("B",4);
Here My key is A and it will override previous value of A and give value of key A is 3. But I want to store all the values of this key like i want to store 1 ,2 and 3.Then please tell me how all these value of particular key is stored in arraylist.
That doesn’t work in this way. Map keys are unique by definition.
You will need a
Map<String, List<Integer>>
Of course before you add a key you need to lookup if an entry already exists. If not, add a new Arraylist using the key, and add the value to the new list.
Or a much mature alternative could be Guava's multiMap.
You can find the reference to it's usage here
Hope it helps!
Try this and hope it helps.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MapwithDupKeys {
public static void main(String[] args) {
Map<String, List<Integer>> myMultiMap = new HashMap<>();
add(myMultiMap, "A", 1);
add(myMultiMap, "A", 2);
add(myMultiMap, "A", 3);
add(myMultiMap, "B", 4);
System.out.println(myMultiMap);
}
static void add(Map<String, List<Integer>> map, String key, Integer value) {
if (map.get(key) == null) {
List valueList = new ArrayList();
valueList.add(value);
map.put(key, valueList);
} else
((ArrayList) map.get(key)).add(value);
}
}
Lets analyze the requirement
You have a key of type String which is needed to map with a collection(unique) of values of type Integer. (unique is my assumption). I mean ("xyz", 1) and ("xyz,1) in case of these two entries in the map it has to be seen as only one entry.
From point 1 we can define a structure for an entry : [ Key- String , Value- Set ]
A map is needed to hold entries of type as mentioned in point 2.
We can have a map like below.
HashMap <String, Set<Integer>>
Lets translate it to easiest implementation, although there may be other options too.
private Map<String, Set<Integer>> map = new HashMap<>();
public void putPair( String key, Integer value){
Set<Integer> values = map.get(key);
if(values == null){
values = new HashSet<Integer>();
map.put(key, values);
}
values.add(value);
}
In case multiple same values also you want you can use simple ArrayList instead of Set. But this case better way is to encapsulate the Integer in another wrapper class and keep a count. increment the count in case of same entry.
As per your requirements, you don't need a Map<String, Integer>, but a Map<String, List<Integer>> instead. In other words, you're after a multimap.
One way to achieve such data structure in Java 8+, is by using the Map.computeIfAbsent and Map.computeIfPresent methods for insertions and removals, respectively:
Map<String, List<Integer>> map = new HashMap<>(); // use diamond operator
// INSERT
map.computeIfAbsent("A", k -> new ArrayList<>()).add(1);
map.computeIfAbsent("A", k -> new ArrayList<>()).add(2);
map.computeIfAbsent("A", k -> new ArrayList<>()).add(3);
map.computeIfAbsent("B", k -> new ArrayList<>()).add(4);
// REMOVE
map.computeIfPresent("A", (k, v) -> {
v.remove(1);
return v.isEmpty() ? null : v;
});
map.computeIfPresent("A", (k, v) -> {
v.remove(2);
return v.isEmpty() ? null : v;
});
map.computeIfPresent("A", (k, v) -> {
v.remove(3);
return v.isEmpty() ? null : v;
});
map.computeIfPresent("B", (k, v) -> {
v.remove(4);
return v.isEmpty() ? null : v;
});
EDIT:
The remapping function argument for the removals could be extarcted out to the following utility method:
static <K, V> BiFunction<K, List<V>> removing(V elem) {
return (k, v) -> { v.remove(elem); return v.isEmpty() ? null : v; };
}
Which could then be used as follows:
map.computeIfPresent("A", removing(1));
map.computeIfPresent("A", removing(2));
map.computeIfPresent("A", removing(3));
map.computeIfPresent("B", removing(4));

get key and value from HashMap within ArrayList

I have a file that i get all the data and separate it into a HashMap.
The file looks something like this below.
Before the : is the key and after is the value
key1: 1
key2: 2
key3: 3
this is the code that puts the file data into the map ArrayList:
protected List<Map<String, String>> yaml_parse(BufferedReader filename) throws IOException{
String result;
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
while ((result = filename.readLine()) != null) {
Map<String, String> map = new HashMap<String, String>();
String key = result.substring(0, result.indexOf(":"));
String value = result.substring(result.lastIndexOf(":") + 2);
map.put(key, value);
list.add(map);
}
return list;
}
in another class where i call the function and println, this is the output
[{key1=1}, {key2=2}, {key3=3}]
So my Main question is, how do i get key1 and have it return its value?
I don't understand why you are creating a List of maps. A Map will let you put several key value pairs. Here is a way that would work:
protected Map<String, String> yaml_parse(BufferedReader filename) throws IOException{
String result;
Map<String, String> map = new HashMap<String, String>();
while ((result = filename.readLine()) != null) {
//keyValue[0] = key, keyValue[1] = value
String[] keyValue = result.split(": ");
map.put(keyValue[0], keyValue[1]);
}
return map;
}
And you would use it like this:
Map<String, String> map = yaml_parse("myFile.yaml");
String key1Value = map.get("key1"); //Stores key1's value into key1Value
I think you might be using the wrong data structure. From your question, it seems like you want a Map only, not a List of Maps.
You should look at changing your List<Map> to a Map. You can do this using:
Map<String, String> map = list.stream()
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
If you want to work with your current data structure, you can get the required value like this:
private Optional<String> getValue(List<Map<String, String>> list, String key) {
return list.stream()
.filter(m -> m.containsKey(key))
.map(m -> m.get(key))
.findFirst();
}
and use it as follows:-
Optional<String> value = getValue(list, "key2");
System.out.println(value.orElse(null));
So if you are interested in using java-8, if list of map contains any of entry with key as key1 will return the first entry value else it will return the default value
list.stream().flatMap(map->map.entrySet().stream().filter(entry->entry.getKey().equals("key1"))).findFirst()
.orElse(new AbstractMap.SimpleEntry("key1", "default value")).getValue();
Just by using normal for loop
for(Map<String, String> map : list) {
if(map.containsKey("key1")) {
result = map.get("key1");
break;
}
}
Are you sure this is the data structure you want?
A map can contain more than 1 key/value pair. Why not have a single hashmap here, containing all 3 key/value pairs, at which point, you can just do:
map.get("key1")
and it'll still be fast even if you have millions of these.
If you are making single-size maps and putting them into an arraylist because you want to preserve order, use LinkedHashMap. If you need to be capable of dealing with repeated keys, use guava's Multimap, or make a Map<String, List<String>>.

Compare two Maps and remove all the elements that either have the same key or the same value

I have two Maps that I need to compare and merge them into a result map. I need to remove all the elements that either have the same key or the same value.
Basically, say I have two Maps:
Map<String, String> map1 = new HashMap<>();
map1.put("1", "A");
map1.put("2", "A");
map1.put("3", "B");
map1.put("4", "C");
map1.put("5", "D");
map1.put("6", "E");
Map<String, String> map2 = new HashMap<>();
map2.put("1", "B");
map2.put("2", "A");
map2.put("4", "F");
map2.put("6", "C");
map2.put("7", "G");
map2.put("8", "H");
I need to remove all the entries that have either the same keys or the same values and need to retain back only bidirectional unique entries. So after merge, I need to have the following result map in which every key maps to a unique value and every value has a unique key:
("5", "D"), ("7", "G"), ("8", "H")
What's the best way to do this in Java?
I would create another map that contains all the values and keys from map1 and map2, and then I would go through a loop deleting the duplicates keys and values
Map<String, String> map3 = new HashMap<>();
map3.putAll(map1);
map3.putAll(map2);
for(String a: map1.keySet()){
if(map2.containsKey(a) || map2.containsValue(map1.get(a))){
map3.remove(a);
}
}
Hope this is useful!
Below code will do this
Map map3 = new HashMap<>(map1);
map3.keySet().removeAll(map2.keySet());
map3.values().removeAll(map2.values());
map2.keySet().removeAll(map1.keySet());
map2.values().removeAll(map1.values());
map3.putAll(map2);
System.out.println(map3);
This will result {7=G, 5=D, 8=H}
Interesting problem. I can't think of a particularly neat way of doing it but here's a potential solution using Java 8 - I'm pretty sure it could be simplified somewhat. I don't like these sorts of stateful operations mid stream but the only way I can see to avoid it is to split it into two operations.
Set<Map.Entry<String, String>> values = new HashSet<>();
Map<String,String> mergedMap =
Stream.concat(map1.entrySet().stream(), map2.entrySet().stream)
.filter(e -> !values.keySet().contains(e.getKey()))
.filter(e -> !values.valueSet().contains(e.getValue()))
.peek(e -> values.add(e))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Using Java 8 Optional for safe Map traversal

I have Map<String, Map<String, String>> myMap in my Java 8 class. I need to navigate to a leaf String like myMap['keyA']['keyB'], returning null if either 'keyA' or 'keyB' does not exist in the correlating Map.
In groovy I would use myMap?.keyA?.keyB and be done with it. I understand that Java 8's Optional<T> brings similar behavior into java. Is there a way to use this new behavior to concisely mimic the groovy functionality? If not, is there another concise way to get this behavior in Java 8, or am I still stuck with elaborate procedural code?
String valueOrNull = Optional.ofNullable(myMap.get("keyA"))
.map(x -> x.get("keyB"))
.orElse(null);
First, it wraps the results of the first lookup in an Optional, which acts as a monad. If you add a third layer (myMap.?keyA.?keyB.?keyC), it would look like this:
String valueOrNull = Optional.ofNullable(myMap.get("keyA"))
.map(x -> x.get("keyB"))
.map(x -> x.get("keyC"))
.orElse(null);
You can use Optional's ofNullable method to create an Optional that may or may not represent a null value. Then you can use the map method that will, with a Function, map the result to a new value if the value wasn't already null.
Here, I supply a Function as a lambda expression to get the value from the second Map using the second key.
Optional<String> result = Optional.ofNullable(myMap.get("keyA")).map(m -> m.get("keyB"));
From there you can see if the Optional has a value with isPresent(), and if so, get it with get().
Testing:
public static Optional<String> method(Map<String, Map<String, String>> map,
String key1, String key2)
{
return Optional.ofNullable(map.get(key1)).map(m -> m.get(key2));
}
Calling Code:
Map<String, Map<String, String>> myMap = new HashMap<>();
Map<String, String> inner = new HashMap<>();
inner.put("one", "two");
myMap.put("three", inner);
System.out.println(method(myMap, "three", "one"));
System.out.println(method(myMap, "three", "dne"));
System.out.println(method(myMap, "dne", "dne"));
Output:
Optional[two]
Optional.empty
Optional.empty
Interesting question.
You can consider using recursion.
/**
* Finds the value of a node in nested maps.
* #return leaf value or null if none
*/
public <K, V> V getValueFromKeys(Map<K, V> map, K... keys) {
V value = map.getOrDefault(keys[0], null);
if (keys.length == 1) return value;
if (value instanceof Map) {
K[] remainingKeys = Arrays.copyOfRange(keys, 1, keys.length);
return getValueFromKeys((Map<K, V>) value, remainingKeys);
}
return null;
}
This will work with Java >= 8 (you can easily adapt it to previous versions).
Bonus (needs Guava):
#Test
public void getValueFromKeys_level1() {
Map<String, String> mapLevel1 = ImmutableMap.of("key1", "value1");
assertEquals("value1", getValueFromKeys(mapLevel1, "key1"));
assertNull(getValueFromKeys(mapLevel1, null));
assertNull(getValueFromKeys(mapLevel1, ""));
assertNull(getValueFromKeys(mapLevel1, "wrong"));
assertNull(getValueFromKeys(mapLevel1, "key1", "wrong"));
}
#Test
public void getValueFromKeys_level2() {
Map<String, Map<String, String>> mapLevel2 = ImmutableMap.of("key1", ImmutableMap.of("subkey1", "value1"));
assertEquals("value1", getValueFromKeys(mapLevel2, "key1", "subkey1"));
assertNull(getValueFromKeys(mapLevel2, null));
assertNull(getValueFromKeys(mapLevel2, ""));
assertNull(getValueFromKeys(mapLevel2, "wrong"));
assertNull(getValueFromKeys(mapLevel2, "key1", "wrong"));
assertNull(getValueFromKeys(mapLevel2, "key1", "subkey1", "wrong"));
assertTrue(getValueFromKeys(mapLevel2, "key1") instanceof Map);
}
#Test
public void getValueFromKeys_level3() {
Map<String, Map<String, Map<String, String>>> mapLevel3 = ImmutableMap.of("key1", ImmutableMap.of("subkey1", ImmutableMap.of("subsubkey1", "value1")));
assertEquals("value1", getValueFromKeys(mapLevel3, "key1", "subkey1", "subsubkey1"));
assertNull(getValueFromKeys(mapLevel3, null));
assertNull(getValueFromKeys(mapLevel3, ""));
assertNull(getValueFromKeys(mapLevel3, "wrong"));
assertNull(getValueFromKeys(mapLevel3, "key1", "wrong"));
assertNull(getValueFromKeys(mapLevel3, "key1", "subkey1", "wrong"));
assertNull(getValueFromKeys(mapLevel3, "key1", "subkey1", "subsubkey1", "wrong"));
}

How to putAll on Java hashMap contents of one to another, but not replace existing keys and values?

I need to copy all keys and values from one A HashMap onto another one B, but not to replace existing keys and values.
Whats the best way to do that?
I was thinking instead iterating the keySet and checkig if it exist or not, I would
Map temp = new HashMap(); // generic later
temp.putAll(Amap);
A.clear();
A.putAll(Bmap);
A.putAll(temp);
It looks like you are willing to create a temporary Map, so I'd do it like this:
Map tmp = new HashMap(patch);
tmp.keySet().removeAll(target.keySet());
target.putAll(tmp);
Here, patch is the map that you are adding to the target map.
Thanks to Louis Wasserman, here's a version that takes advantage of the new methods in Java 8:
patch.forEach(target::putIfAbsent);
Using Guava's Maps class' utility methods to compute the difference of 2 maps you can do it in a single line, with a method signature which makes it more clear what you are trying to accomplish:
public static void main(final String[] args) {
// Create some maps
final Map<Integer, String> map1 = new HashMap<Integer, String>();
map1.put(1, "Hello");
map1.put(2, "There");
final Map<Integer, String> map2 = new HashMap<Integer, String>();
map2.put(2, "There");
map2.put(3, "is");
map2.put(4, "a");
map2.put(5, "bird");
// Add everything in map1 not in map2 to map2
map2.putAll(Maps.difference(map1, map2).entriesOnlyOnLeft());
}
Just iterate and add:
for(Map.Entry e : a.entrySet())
if(!b.containsKey(e.getKey())
b.put(e.getKey(), e.getValue());
Edit to add:
If you can make changes to a, you can also do:
a.putAll(b)
and a will have exactly what you need. (all the entries in b and all the entries in a that aren't in b)
You can make it in just 1 line if you change maps order in #erickson's solution:
mapWithNotSoImportantValues.putAll( mapWithImportantValues );
In this case you replace values in mapWithNotSoImportantValues with value from mapWithImportantValues with the same keys.
Java 8 solution using Map#merge
As of java-8 you can use Map#merge(K key, V value, BiFunction remappingFunction) which merges a value into the Map using remappingFunction in case the key is already found in the Map you want to put the pair into.
// using lambda
newMap.forEach((key, value) -> map.merge(key, value, (oldValue, newValue) -> oldValue));
// using for-loop
for (Map.Entry<Integer, String> entry: newMap.entrySet()) {
map.merge(entry.getKey(), entry.getValue(), (oldValue, newValue) -> oldValue);
}
The code iterates the newMap entries (key and value) and each one is merged into map through the method merge. The remappingFunction is triggered in case of duplicated key and in that case it says that the former (original) oldValue value will be used and not rewritten.
With this solution, you don't need a temporary Map.
Let's have an example of merging newMap entries into map and keeping the original values in case of the duplicated antry.
Map<Integer, String> newMap = new HashMap<>();
newMap.put(2, "EVIL VALUE"); // this will NOT be merged into
newMap.put(4, "four"); // this WILL be merged into
newMap.put(5, "five"); // this WILL be merged into
Map<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
newMap.forEach((k, v) -> map.merge(k, v, (oldValue, newValue) -> oldValue));
map.forEach((k, v) -> System.out.println(k + " " + v));
1 one
2 two
3 three
4 four
5 five
public class MyMap {
public static void main(String[] args) {
Map<String, String> map1 = new HashMap<String, String>();
map1.put("key1", "value1");
map1.put("key2", "value2");
map1.put("key3", "value3");
map1.put(null, null);
Map<String, String> map2 = new HashMap<String, String>();
map2.put("key4", "value4");
map2.put("key5", "value5");
map2.put("key6", "value6");
map2.put("key3", "replaced-value-of-key3-in-map2");
// used only if map1 can be changes/updates with the same keys present in map2.
map1.putAll(map2);
// use below if you are not supposed to modify the map1.
for (Map.Entry e : map2.entrySet())
if (!map1.containsKey(e.getKey()))
map1.put(e.getKey().toString(), e.getValue().toString());
System.out.println(map1);
}}
With Java 8 there is this API method to accomplish your requirement.
map.putIfAbsent(key, value)
If the specified key is not already associated with a value (or is mapped to null) associates it with the given value and returns null, else returns the current value.
As others have said, you can use putIfAbsent. Iterate over each entry in the map that you want to insert, and invoke this method on the original map:
mapToInsert.forEach(originalMap::putIfAbsent);

Categories