Invert Map | Values ---> Keys - java

I want to be able to inverse a given HashMap that has multiple keys can point to the same value.
HashMap<String, String> cities = new HashMap<String, String>();
cities.put("Manchester", "UK");
cities.put("London", "UK");
static HashMap<String, String> inverseMap(HashMap map) {
// something that has "UK" point to both "Manchester" and "London"
// if it can be done without using any special Java 8 feature, that would be great
}
I am unsure where to start.

Do it like this. Basically, it employs a merge function which concatenates values for a duplicate key.
Create a new map
Use the values of the old map for the keys to the new
If the new map does not have a value for the key, put the value in the new map
Otherwise, concatenate the value to the old value for that key
HashMap<String, String> cities = new HashMap<String, String>();
cities.put("Manchester", "UK");
cities.put("London", "UK");
cities.put("New York", "US");
cities.put("Chicago", "US");
Map<String,String> inverted = new HashMap<>();
for (String key : cities.keySet()) {
String newKey = cities.get(key);
String value = inverted.get(newKey);
if (value == null) {
inverted.put(newKey, key);
} else {
value = value + ", " + key;
inverted.put(newKey, value);
}
}
for (Entry<String,String> e : inverted.entrySet()) {
System.out.println(e.getKey() + " -> " + e.getValue());
}
It prints
UK -> Manchester, London
US -> New York, Chicago
Since you didn't specify how to handle duplicate keys. I could also have stored it in a Map<String,List<String>>

Is this what you're looking for?
Map<String, String> cities = new HashMap<>();
cities.put("Manchester", "UK");
cities.put("London", "UK");
Map<String, List<String>> reverseMap = new HashMap<>();
for (Entry<String, String> entry : cities.entrySet()) {
List<String> list = reverseMap.get(entry.getValue());
if (list == null) {
list = new ArrayList<>();
reverseMap.put(entry.getValue(), list);
}
list.add(entry.getKey());
}
System.out.println(reverseMap);

As multiple keys can contain the same value, you will have to be able to store multiple values per key in the inversed Map. I recommend using a Set as a value for this.
Create a map that can save a Set of Strings as values
Iterate through the original map
If the country is not found in the new map, create an entry there
Add the city to the map
This should also work with Java 7 without dependencies.
static HashMap<String, Set<String>> inverseMap(HashMap<String,String> map) {
HashMap<String,Set<String>> inversed=new HashMap<>();
for(Map.Entry<String,String> entry:map.entrySet()){
if(!inversed.containsKey(entry.getValue())){
inversed.put(entry.getValue(),new HashSet<>());
}
inversed.get(entry.getValue()).add(entry.getKey());
}
return inversed;
}
{Manchester=UK,London=UK} would turn into {UK={Manchester,London}} (order may differ).

You can look at MultiMap. It allows mapping of a single key to multiple values.
This is something has been implemented in both Google Guava
https://guava.dev/releases/19.0/api/docs/com/google/common/collect/Multimap.html
ListMultimap<String, String> multimap = ArrayListMultimap.create();
for (President pres : US_PRESIDENTS_IN_ORDER) {
multimap.put(pres.firstName(), pres.lastName());
}
for (String firstName : multimap.keySet()) {
List<String> lastNames = multimap.get(firstName);
out.println(firstName + ": " + lastNames);
}
... produces output such as:
Zachary: [Taylor]
John: [Adams, Adams, Tyler, Kennedy] // Remember, Quincy!
George: [Washington, Bush, Bush]
Grover: [Cleveland, Cleveland] // Two, non-consecutive terms, rep'ing NJ!
As in Apache Commons Collections
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/MultiValuedMap.html
MultiValuedMap<K, String> map = new MultiValuedHashMap<K, String>();
map.put(key, "A");
map.put(key, "B");
map.put(key, "C");
Collection<String> coll = map.get(key);
coll will be a collection containing "A", "B", "C".

You can iterate over the entry set of your original map and use the values (country code) as key and add each key (cities) to a list:
static HashMap<String, List<String>> inverseMap(HashMap<String, String> map) {
HashMap<String, List<String>> countryToCity = new HashMap<>();
for(Map.Entry<String,String> entry: map.entrySet()){
countryToCity.computeIfAbsent(entry.getValue(), k -> new ArrayList<>()).add(entry.getKey());
}
return countryToCity;
}

Related

how to iterate and compare over Hashtable<String, Map<String, Set<String>>> in java

I have a structure like below ,
Key: active-csr-HA-profile & Value: {sapd-outside=[outside], sapd-extra4=[extra4], sapd-extra3=[extra3], sapd-inside=[inside]}
Key = sapd-outside, Value = [outside]
Key = sapd-extra4, Value = [extra4]
Key = sapd-extra3, Value = [extra3]
Key = sapd-inside, Value = [inside]
Key: standby-csr-HA-profile & Value: {sapd-outside=[outside], sapd-extra4=[extra4], sapd-extra3=[extra3], sapd-inside=[inside]}
Key = sapd-outside, Value = [outside]
Key = sapd-extra4, Value = [extra4]
Key = sapd-extra3, Value = [extra3]
Key = sapd-inside, Value = [inside]
the above if of format Hashtable<String, Map<String, Set<String>>>
I want to compare if sapd-outside of active-csr-HA-profile is same as one of the keys of standby-csr-HA-profile. So compare each key of active-csr-HA-profile to each key of standby-csr-HA-profile.
I looked some similar questions but what i am working out is not solving the purpose.
As already mentioned in the comments, the Hashtable is considered obsolete. Its replacement is HashMap. If you wish make HashMap synchronized the same way the Hashtable does, use the Collections::synchronizedMap decorator on it.
The structure of your Hashtable looks a bit unclear. I guess the following structure matches your one the best and I base my solution on it.
Hashtable<String, Map<String, Set<String>>> map = new Hashtable<>();
Map<String, Set<String>> activeCsrHAProfile = new HashMap<>();
activeCsrHAProfile.put("sapd-outside", new HashSet<>(Arrays.asList("outside")));
activeCsrHAProfile.put("sapd-extra4", new HashSet<>(Arrays.asList("extra4")));
activeCsrHAProfile.put("sapd-extra3", new HashSet<>(Arrays.asList("extra3")));
activeCsrHAProfile.put("sapd-inside", new HashSet<>(Arrays.asList("inside")));
Map<String, Set<String>> standbyCsrHAProfile = new HashMap<>();
standbyCsrHAProfile.put("sapd-outside", new HashSet<>(Arrays.asList("outside")));
standbyCsrHAProfile.put("sapd-extra4", new HashSet<>(Arrays.asList("extra4")));
standbyCsrHAProfile.put("sapd-extra3", new HashSet<>(Arrays.asList("extra3")));
standbyCsrHAProfile.put("sapd-inside", new HashSet<>(Arrays.asList("inside")));
map.put("active-csr-HA-profile", activeCsrHAProfile);
map.put("standby-csr-HA-profile", standbyCsrHAProfile);
In case my structure differs a bit from yours, there would be no problem to amend the solution in order to match your structure - the principle is the same.
Set<String> sapdOutsideOfActiveCsrHAProfile = map.get("active-csr-HA-profile")
.get("sapd-outside");
map.get("standby-csr-HA-profile").entrySet()
.stream()
.filter(i -> i.getValue().containsAll(sapdOutsideOfActiveCsrHAProfile))
.forEach(e -> System.out.println("Found at: " +
"key=" + e.getKey() + ", value=" + e.getValue()));
.filter(i -> i.getValue().containsAll(..) filters those entris which values Set<String> contains all of the required Strings.
.forEach(..) gives a consumer performing an action over all the matching results.
In case you need the boolean representing whether the match has occurred or not, do:
boolean matches = map.get(..).entrySet().stream().filter(..).findFirst().isPresent();
As mentioned in the comments, HashTable is a debatable choice. Regardless of the implementation you choose, you could create your own class to manage the messy stuff:
public class CustomMap extends Hashtable<String, Map<String, Set<String>>> {
public CustomMap() {
super();
}
public boolean compareEntries(String key1, String key2) {
if (!this.containsKey(key1) || !this.containsKey(key2) || this.get(key1).size() != this.get(key2).size())
return false;
for (String innerKey : this.get(key1).keySet()) {
if (!this.get(key2).containsKey(innerKey)) {
return false;
}
final Set<String> setA = this.get(key1).get(innerKey);
final Set<String> setB = this.get(key2).get(innerKey);
if (!setA.containsAll(setB) || !setB.containsAll(setA)) {
return false;
}
}
return true;
}
}
I took the assumption there could be more entries in your table and you'd want to compare specific entries.
You can iterate through a map with its entry set:
Hashtable<String, Map<String, Set<String>>> table = new Hashtable();
for (Map.Entry<String, Map<String, Set<String>>> entry : table.entrySet()) {
String key = entry.getKey();
Map<String, Set<String>> map = entry.getValue();
for (Map.Entry<String, Set<String>> mapEntry : map.entrySet()) {
String mapKey = mapEntry.getKey();
Set<String> set = mapEntry.getValue();
for (String text : set) {
// ...
}
}
}
Nesting sets inside maps inside maps makes the code hard to read though, you might want to use specialized objects instead.
As others have said, in most cases HashMap is preferrable compared to an Hashtable.

Best way to find element in List Java [duplicate]

It's a simple question,
I have a simple HashMap of which i want to reverse the keys and values.
HashMap<Character, String> myHashMap = new HashMap<Character, String>();
myHashMap.put('a', "test one");
myHashMap.put('b', "test two");
and I want to create a new HashMap in which i put the opposites.
HashMap<String, Character> reversedHashMap = new HashMap<String, Character>();
e.g. Keys "test one" & "test two" and values 'a' & 'b'.
They all are unique, yes
If you're sure that your values are unique you can iterate over the entries of your old map .
Map<String, Character> myNewHashMap = new HashMap<>();
for(Map.Entry<Character, String> entry : myHashMap.entrySet()){
myNewHashMap.put(entry.getValue(), entry.getKey());
}
Alternatively, you can use a Bi-Directional map like Guava provides and use the inverse() method :
BiMap<Character, String> myBiMap = HashBiMap.create();
myBiMap.put('a', "test one");
myBiMap.put('b', "test two");
BiMap<String, Character> myBiMapInversed = myBiMap.inverse();
As java-8 is out, you can also do it this way :
Map<String, Integer> map = new HashMap<>();
map.put("a",1);
map.put("b",2);
Map<Integer, String> mapInversed =
map.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey))
Finally, I added my contribution to the proton pack library, which contains utility methods for the Stream API. With that you could do it like this:
Map<Character, String> mapInversed = MapStream.of(map).inverseMapping().collect();
Apache commons collections library provides a utility method for inversing the map. You can use this if you are sure that the values of myHashMap are unique
org.apache.commons.collections.MapUtils.invertMap(java.util.Map map)
Sample code
HashMap<String, Character> reversedHashMap = MapUtils.invertMap(myHashMap)
If the values are not unique, the safe way to inverse the map is by using java 8's groupingBy function
Map<String, Integer> map = new HashMap<>();
map.put("a",1);
map.put("b",2);
Map<Integer, List<String>> mapInversed =
map.entrySet()
.stream()
.collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())))
I wrote a simpler loop that works too (note that all my values are unique):
HashMap<Character, String> myHashMap = new HashMap<Character, String>();
HashMap<String, Character> reversedHashMap = new HashMap<String, Character>();
for (char i : myHashMap.keySet()) {
reversedHashMap.put(myHashMap.get(i), i);
}
To answer your question on how you can do it, you could get the entrySet from your map and then just put into the new map by using getValue as key and getKey as value.
But remember that keys in a Map are unique, which means if you have one value with two different key in your original map, only the second key (in iteration order) will be kep as value in the new map.
Iterate through the list of keys and values, then add them.
HashMap<String, Character> reversedHashMap = new HashMap<String, Character>();
for (String key : myHashMap.keySet()){
reversedHashMap.put(myHashMap.get(key), key);
}
private <A, B> Map<B, A> invertMap(Map<A, B> map) {
Map<B, A> reverseMap = new HashMap<>();
for (Map.Entry<A, B> entry : map.entrySet()) {
reverseMap.put(entry.getValue(), entry.getKey());
}
return reverseMap;
}
It's important to remember that put replaces the value when called with the same key. So if you map has two keys with the same value only one of them will exist in the inverted map.
Tested with below sample snippet, tried with MapUtils, and Java8 Stream feature. It worked with both cases.
public static void main(String[] args) {
Map<String, String> test = new HashMap<String, String>();
test.put("a", "1");
test.put("d", "1");
test.put("b", "2");
test.put("c", "3");
test.put("d", "4");
test.put("d", "41");
System.out.println(test);
Map<String, String> test1 = MapUtils.invertMap(test);
System.out.println(test1);
Map<String, String> mapInversed =
test.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
System.out.println(mapInversed);
}
Output:
{a=1, b=2, c=3, d=41}
{1=a, 2=b, 3=c, 41=d}
{1=a, 2=b, 3=c, 41=d}
Use forEach introduced in Java 8
Map<Short, String> regularMap = new HashMap<>();
Map<String, Short> inversedMap = new HashMap<>();
regularMap.forEach((key, value) -> inversedMap.put(value, key));
for reverting the map, in your case:
private void reverseMap(Map<Character, String> map) {
Map<String, Character> newList = new HashMap<>();
map.forEach((key, value) -> newList.put(value, key));
System.out.println(newList);
}
or you can traverse the old hashmap
HashMap<String, Character> newList = new HashMap<String, Character>();
for (String key : list.keySet()){
newList.put(list.get(key), key);
}
For Reversing the Array of Dictionary. (If values are Unique)
private void reverseArrayMap(List<Map<String, String>> list) {
// reversing the array of dictionary
List<Map<String, String>> newList = new ArrayList<>();
Map<String, String> resDic = new HashMap<>();
for (Map<String, String> map : list) {
map.forEach((key, value) -> resDic.put(value, key));
newList.add(resDic);
}
System.out.println("Original Array of Dictionary" + list);
System.out.println("Reversed Array of Dictionary" + newList);
}
Java :
Simple approach, No need for java 8
Map<String,String> map=new HashMap<>();
Map<String,String> mapInv=new HashMap<>();
for (String key : map.keySet())
mapInv.put(map.get(key), key);
Java 8:
forEach() is a new method to iterate the elements. It is defined in Iterable and Stream interface.
Map<String,String> map=new HashMap<>();
Map<String,String> mapInv=new HashMap<>();
map.forEach((key, value) -> mapInv.put(value, key));
Kotlin :
val map: Map<String, String> = HashMap()
val mapInv: MutableMap<String?, String> = HashMap()
for (key in map.keys) mapInv[map[key]] = key

HashMap Key Comparison and return values in JAVA

I want to compare keys of two different hash maps say
Map<String, Float> map1 = new HashMap<>();
Map<String, Float> map2 = new HashMap<>();
map1:
<org.openjdk.jmh.samples.JMHSortBenchmark.collectionsSort,6691.679>
<org.openjdk.jmh.samples.JMHSortBenchmark.abcdSort,5151.45>
<org.openjdk.jmh.samples.JMHSortBenchmark.saasSort,5454.54>
<org.openjdk.jmh.samples.JMHSortBenchmark.xyzSort,888.22>
map2:
<org.openjdk.jmh.samples.JMHSortBenchmark.xyzSort,7448.362>
<org.openjdk.jmh.samples.JMHSortBenchmark.abcdSort,951.5>
<org.openjdk.jmh.samples.JMHSortBenchmark.lmnSort,4454.54>
And if they match eg., "org.openjdk.jmh.samples.JMHSortBenchmark.xyzSort" so I want to return the <Key,Value> pair of both map1 and map2 i.e., it must return
org.openjdk.jmh.samples.JMHSortBenchmark.xyzSort,888.22
org.openjdk.jmh.samples.JMHSortBenchmark.xyzSort,7448.362
org.openjdk.jmh.samples.JMHSortBenchmark.abcdSort,5151.45
org.openjdk.jmh.samples.JMHSortBenchmark.abcdSort,951.5
because I want to process the difference between their values and compare them i.e., 888.2 in map1 and 7448.362 in map2 thereby logging the difference to a csv file.
I used the following code:
for (Entry<String, Float> entry: map1.entrySet()) {
if (map2.containsKey(entry.getKey())) {
System.out.println("The matched value is" + entry.getValue() +"and Key is"+ entry.getKey());
}
}
but this could return only the values of map1 and not map2.
I have made a working solution for you.
static void test11()
{
HashMap<String, Float> map1 = new HashMap<>();
HashMap<String, Float> map2 = new HashMap<>();
map1.put("org.openjdk.jmh.samples.JMHSortBenchmark.collectionsSort",(float) 6691.679);
map1.put("org.openjdk.jmh.samples.JMHSortBenchmark.abcdSort1",(float) 5151.45);
map1.put("org.openjdk.jmh.samples.JMHSortBenchmark.saasSort",(float) 5454.54);
map1.put("org.openjdk.jmh.samples.JMHSortBenchmark.xyzSort",(float) 888.22);
map2.put("org.openjdk.jmh.samples.JMHSortBenchmark.xyzSort", (float) 7448.362);
map2.put("org.openjdk.jmh.samples.JMHSortBenchmark.abcdSort", (float) 951.5);
map2.put("org.openjdk.jmh.samples.JMHSortBenchmark.lmnSort", (float) 4454.54);
for(String key: map1.keySet())
{
// use key to search 2nd list, will be null if no matching key found
Float map2data = map2.get(key);
if (null == map2data)
{
// Current key not found
}
else
{
Float map1data = map1.get(key);
// You can do you operations here with matching keys data here
}
}
}
Hope this will help. :-)
I would do it like this:
map1.keySet().retainAll(map2.keySet());
The keySet() method will give you a set view (!) on the keys of the map. retainAll() will only keep the elements in that set that are keys in map2, too. If you want to keep all values of map1 you may want to make a copy first.

Group the map values to avoid duplicate [duplicate]

This question already has answers here:
Reverse HashMap keys and values in Java
(12 answers)
Closed 5 years ago.
I have a Map(map1) whose key is another map(map2) and value is string.
The value of map1 has several duplicate, so I must group them and set as key in another map3 whose value has to be map2.
eg:
map1 { [[a,b],xyz], [[c,d],wrt] , [[e,f],xyz] , [[r,m],xyz] }
output should be :
map3 { [ xyz,[ [a,b],[e,f],[r,m] ] ] , [ wrt,[ [c,d] ]
can i obtain like this ?
try MultiValueMap from (commons-collections) library
Map map1 = new HashMap<String[], String>();
map1.put(new String[]{"a", "b"}, "xyz");
map1.put(new String[]{"c", "d"}, "wrt");
map1.put(new String[]{"e", "f"}, "xyz");
map1.put(new String[]{"c", "d"}, "xyz");
MultiValueMap map2 = new MultiValueMap();
for(String[] key: map1.keySet()) {
String value = map1.get(key);
map2.add(value, key);
}
// now you got map2 as you want
NO, Though you can declare it but while using it, it may allow same keys(human readable). Because Map do not override Object's equals method your key comparison will be on JVM's object level (it may be different for objects with same values in them).
You can use the stream API to solve it:
Map<String, List<Map<String, String>>> map3 = map.entrySet().stream()
.collect(Collectors.groupingBy(Entry::getValue,
Collectors.mapping(Entry::getKey,
Collectors.toList())));
Explanation:
The entries will be grouped by its values (groupingBy(Entry::getValue).
But the values should not be a list of entries so the downstream is necessary. This is the list of keys of the original map.
It is not clear from your post what do you mean by map2, but let's assume you would like to use each Map.Entry<String, String> entry from map2 for each key for map1.
The following code is Java 7, it's a bit verbose. It could be done shorter with Java 8 streams, I guess.
public class MapReverser {
private Map<Map.Entry<String, String>, String> map1 = new HashMap<>();
private Map<String, String> map2 = new LinkedHashMap<>();
private void prepareMaps() {
map2.put("a", "b");
map2.put("c", "d");
map2.put("e", "f");
map2.put("r", "m");
String[] valueArray = { "xyz", "wrt", "xyz", "xyz" };
int i = 0;
for (Map.Entry<String, String> entry : map2.entrySet()) {
map1.put(entry, valueArray[i]);
i++;
}
}
public Map<String, List<Map.Entry<String, String>>> reverse() {
Map<String, List<Map.Entry<String, String>>> reversedMap = new HashMap<>();
for (Map.Entry<Map.Entry<String, String>, String> entry : map1.entrySet()) {
List<Map.Entry<String, String>> reversedMapValue = reversedMap.get(entry.getValue());
if (reversedMapValue == null) {
reversedMapValue = new ArrayList<>();
}
reversedMapValue.add(entry.getKey());
reversedMap.put(entry.getValue(), reversedMapValue);
}
return reversedMap;
}
private void printResult(Map<String, List<Map.Entry<String, String>>> reversedMap) {
for (Map.Entry<String, List<Map.Entry<String, String>>> entry : reversedMap.entrySet()) {
System.out.println("Key: \n" + entry.getKey());
System.out.println("Values:");
List<Map.Entry<String, String>> valuesList = entry.getValue();
for (Map.Entry<String, String> value : valuesList) {
System.out.println(value );
}
System.out.println("\n");
}
}
public static void main(String[] args) {
MapReverser mapReverser = new MapReverser();
mapReverser.prepareMaps();
Map<String, List<Map.Entry<String, String>>> reversedMap = mapReverser.reverse();
mapReverser.printResult(reversedMap);
}
}

What is the best practices to merge two maps

How can I add a new map to existing map. The maps have the same type Map<String, Integer>. If the key from new map exists in the old map the values should be added.
Map<String, Integer> oldMap = new TreeMap<>();
Map<String, Integer> newMap = new TreeMap<>();
//Data added
//Now what is the best way to iterate these maps to add the values from both?
By add, I assume you want to add the integer values, not create a Map<String, List<Integer>>.
Before java 7, you'll have to iterate as #laune showed (+1 to him). Otherwise with java 8, there is a merge method on Map. So you could do it like this:
Map<String, Integer> oldMap = new TreeMap<>();
Map<String, Integer> newMap = new TreeMap<>();
oldMap.put("1", 10);
oldMap.put("2", 5);
newMap.put("1", 7);
oldMap.forEach((k, v) -> newMap.merge(k, v, (a, b) -> a + b));
System.out.println(newMap); //{1=17, 2=5}
What it does is that for each key-value pair, it merges the key (if it's not yet in newMap, it simply creates a new key-value pair, otherwise it updates the previous value hold by the existing key by adding the two Integers)
Also maybe you should consider using a Map<String, Long> to avoid overflow when adding two integers.
for( Map.Entry<String,Integer> entry: newMap.entrySet() ) {
// get key and value from newMap and insert/add to oldMap
Integer oldVal = oldMap.get( entry.getKey() );
if( oldVal == null ){
oldVal = entry.getValue();
} else {
oldVal += entry.getValue();
}
newMap.put( entry.getKey(), oldVal );
}
Hope that this is what you meant

Categories