iterating two maps into a third map on key from both maps - java

I have two maps with key as an integer and value as a double.
I want to create a third map sorted on key and value would be a list of doubles from both the maps.
map1:
1, 90.00
5, 75.45
8, 76.50
map2:
4, 12.00
5, 322.09
8, 11.09
9, 21.00
final map:
1, (90.00,0.00)
5, (75.45,322.09)
8, (76.50,11.09)
9, (0.00, 21.00)
As clear from above, if a key in one of the map is missing in the other map, the value in the final map for the other map should be defaulted to 0.00
Map firstMap = new HashMap<Integer, Double>();
Map secondMap = new HashMap<Integer, Double>();
Map finalMap = new HashMap<Integer, List<Double>>();
firstMap.put(1, 90.00);
firstMap.put(5, 75.45);
firstMap.put(8, 76.50);
secondMap.put(4, 12.00);
secondMap.put(5, 322.09);
secondMap.put(8, 11.09);
secondMap.put(9, 21.00);
I can use putAll method to put all keys into the third map. But how to put the values as I want ?
Thanks for reading!

Simply do
// Create a sorted map
Map<Integer, List<Double>> finalMap = new TreeMap<Integer, List<Double>>();
Set<Integer> keys = new HashSet<Integer>();
keys.addAll(firstMap.keySet());
keys.addAll(secondMap.keySet());
for (Integer key : keys) {
double first = firstMap.containsKey(key) ? firstMap.get(key) : 0.0;
double second = secondMap.containsKey(key) ? secondMap.get(key) : 0.0;
finalMap.put(key, Arrays.asList(first, second));
}
Using Guava it's as simple as
Multimap<Integer, Double> finalMultiMap = ArrayListMultimap.create();
finalMultiMap.putAll(Multimaps.forMap(firstMap));
finalMultiMap.putAll(Multimaps.forMap(secondMap));
Map<Integer, Collection<Double>> finalMap = finalMultiMap.asMap();

The following code would perform the requested operation
List<Map<Integer, Double>> originals = getOriginalLists();
Map<Integer, List<Double>> result = new HashMap<Integer, List<Double>>();
for (Map<Integer, Double> original : originals) {
for (Map.Entry<Integer, Double> entry : original.entrySet()) {
if (!result.containsKey(entry.getKey())) {
result.put(entry.getKey(), new ArrayList<Double>());
}
result.get(entry.getKey()).add(entry.getValue());
}
}
It works one a list of maps, so the solutions is not restricted to two maps.

Related

How can I group values of List in Java using Lambda like we do in python

I want to group values of a map based on the key. Let's say
Map<String,Integer> map1 = new TreeMap<String,Integer>();
map1.put("D", 3);
map1.put("B", 2);
map1.put("C", 1);
Map<String,Integer> map2 = new TreeMap<String,Integer>();
map2.put("A", 13);
map2.put("B", 22);
map2.put("C", 12);
Map<String,Integer> map3 = new TreeMap<String,Integer>();
map3.put("A", 33);
map3.put("B", 32);
map3.put("C", 32);
Map<Integer,Map<String,Integer>> map = new HashMap <Integer,Map<String,Integer>>();
map.put(1,map1);
map.put( 2, map2);
map.put(3, map3);
System.out.println(map);
I want to group values in the map based on the keys: Output should be ["A","B","C"]:[2,3], ["D","B","C"]:[1]
So what I have done:
Map<List<String>, List<Integer>> newMap = new HashMap<List<String>, List<Integer>>();
for (Integer item : map) {
Map<String,Integer> currentValue = map.get(item);
List<String> oldItemKeySet = newMap.get(currentValue.keySet());
newMap.put(currentValue.keySet(), (oldItemKeySet == null) ? 1 : oldItemKeySet.put());
}
But it doesn't work out, can anyone help here.
PS: In Python, these things can be done with itertools.groupby or reduce, but i am still don't knoww how to do it perfectly in Java
If I understand well, you want to group the identical key set of the maps you added in the last map associated with the original key.
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;
...
Map<Set<String>, List<Integer>> newMap =
map.entrySet()
.stream()
.collect(groupingBy(e -> e.getValue().keySet(),
mapping(Map.Entry::getKey, toList())));
From the last map, you get the stream of entries (which is a Stream<Entry<Integer, Map<String, Integer>>). There you group the entries by the key set of their map's values.
Then you map the values of the resulting map using a downstream collector, which collects the keys of the original entries in a List<Integer>.
Output:
{[A, B, C]=[2, 3], [B, C, D]=[1]}

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

Join two maps by key

I have two maps:
Map<Integer, String> mapOne = {(1,"a"), (2, "b")};
Map<Integer, Double> mapTwo = {(1,10.0), (2,20.0)};
and I want to combine this maps into one by Integer value, so the result map is
Map<String, Double> mapResult = {("a",10.0), ("b",20.0)};
Is there any way to do this easier than iterate over entry set?
Assuming that the keys of the two maps match and that the maps have the same number of entries, with Java 8 you can write it in one line with:
Map<String, Double> map = mapOne.entrySet().stream()
.collect(toMap(e -> e.getValue(),
e -> mapTwo.get(e.getKey())));
So you start from the first map and create a new map where the keys are the values of mapOne and the values are the corresponding values in mapTwo.
Technically this is somewhat equivalent to iterating over the entry set of the first map though.
Note: requires import static java.util.stream.Collectors.toMap;
Looks like only iteration:
#Test
public void testCollection() {
Map<Integer, String> mapOne = new HashMap<Integer, String>();
mapOne.put(1, "a");
mapOne.put(2, "b");
Map<Integer, Double> mapTwo = new HashMap<Integer, Double>();
mapTwo.put(1, 10.0);
mapTwo.put(2, 20.0);
Map<String, Double> mapResult = new HashMap<String, Double>();
Set<Integer> keySet = mapOne.keySet();
keySet.retainAll(mapTwo.keySet());
for (Integer value : keySet) {
mapResult.put(mapOne.get(value), mapTwo.get(value));
}
System.out.println(mapResult);
}
If the maps were the same type, you could use a putAll(), but since you are changing the key value pairs, it looks like you are going to have to iterate over each integer, get() from each map, then put(mapOneVal,mapTwoVal)
for(int i=0;i<max;i++){
String key = mapOne.get(i);
Double val = maptwo.get(i);
if(key!=null && val!=null)
map3.put(key,val);
}

Java how to use TreeMap

I have an TreeMap with for key an Integer and for value Coords (int x, int y).
I would like to get all the values (my coords) associated with an specific key.
For exemple :
key = 1, coords = [0, 0]
key = 1, coords = [0, 1]
key = 2, coords = [1, 0]
key = 3, coords = [1, 1]
foreach myMap.getKeys() as key
do
myMap.getValuesFrom(key)
store it in a new list
end do
I don't really know how to use Map, your helps is welcome.
First point - Map cannot have two different values associated with the same key. If you really need to do this, you can create a map of lists, like this:
Map<Integer, List<Integer>> map = new TreeMap<Integer, List<Integer>>();
The following code traverses the keys of the map and puts related values to the list:
Map<Integer, Integer> map = new TreeMap<Integer, Integer>();
List<Integer> list = new ArrayList<Integer>();
for (Integer key : map.keySet()) {
list.add(map.get(key));
}
More correct way to do the same thing:
Map<Integer, Integer> map = new TreeMap<Integer, Integer>();
List<Integer> list = new ArrayList<Integer>(map.values());
If you really need several values mapped to the same key, try this:
Map<Integer, List<Integer>> map = new TreeMap<Integer, List<Integer>>();
for (Map.Entry<Integer, List<Integer>> entry : map.entrySet()) {
System.out.println("Map values for key \"" + entry.getKey() + "\":");
for (Integer value : entry.getValue() == null ? new ArrayList<Integer>() : entry.getValue()) {
System.out.print(value + " ");
}
System.out.println();
}
This code simply writes all map keys and its values into System.out.
For further information, try to read Map Javadoc and List Javadoc.

Java: How to get set of keys having same value in hashmap

I have a hashmap as below:
1->x
2->y
3->x
4->z
Now i want to know all keys whose value is x (ans: [1,3] ). what is best way to do?
Brute force way is to just iterate over map and store all keys in array whose value is x.
Is there any efficient way for this.
Thanks
A hashmap is a structure that is optimized for associative access of the values using the keys, but is in no way better in doing the reverse then an array for instance. I don't think you can do any better then just iterate. Only way to improve efficiency is if you have a reverse hash map as well(i.e. hash map where you hold an array of keys pointing to a given value for all values).
You can use a MultiMap to easily get all those duplicate values.
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "x");
map.put(2, "y");
map.put(2, "z");
map.put(3, "x");
map.put(4, "y");
map.put(5, "z");
map.put(6, "x");
map.put(7, "y");
System.out.println("Original map: " + map);
Multimap<String, Integer> multiMap = HashMultimap.create();
for (Entry<Integer, String> entry : map.entrySet()) {
multiMap.put(entry.getValue(), entry.getKey());
}
System.out.println();
for (Entry<String, Collection<Integer>> entry : multiMap.asMap().entrySet()) {
System.out.println("Original value: " + entry.getKey() + " was mapped to keys: "
+ entry.getValue());
}
Prints out:
Original map: {1=x, 2=z, 3=x, 4=y, 5=z, 6=x, 7=y}
Original value: z was mapped to keys: [2, 5]
Original value: y was mapped to keys: [4, 7]
Original value: x was mapped to keys: [1, 3, 6]
Per #noahz's suggestion, forMap and invertFrom takes fewer lines, but is arguably more complex to read:
HashMultimap<String, Integer> multiMap =
Multimaps.invertFrom(Multimaps.forMap(map),
HashMultimap.<String, Integer> create());
in place of:
Multimap<String, Integer> multiMap = HashMultimap.create();
for (Entry<Integer, String> entry : map.entrySet()) {
multiMap.put(entry.getValue(), entry.getKey());
}
If Java 8 is an option, you could try a streaming approach:
Map<Integer, String> map = new HashMap<>();
map.put(1, "x");
map.put(2, "y");
map.put(3, "x");
map.put(4, "z");
Map<String, ArrayList<Integer>> reverseMap = new HashMap<>(
map.entrySet().stream()
.collect(Collectors.groupingBy(Map.Entry::getValue)).values().stream()
.collect(Collectors.toMap(
item -> item.get(0).getValue(),
item -> new ArrayList<>(
item.stream()
.map(Map.Entry::getKey)
.collect(Collectors.toList())
))
));
System.out.println(reverseMap);
Which results in:
{x=[1, 3], y=[2], z=[4]}
If Java 7 is preferred:
Map<String, ArrayList<Integer>> reverseMap = new HashMap<>();
for (Map.Entry<Integer,String> entry : map.entrySet()) {
if (!reverseMap.containsKey(entry.getValue())) {
reverseMap.put(entry.getValue(), new ArrayList<>());
}
ArrayList<Integer> keys = reverseMap.get(entry.getValue());
keys.add(entry.getKey());
reverseMap.put(entry.getValue(), keys);
}
As an interesting aside, I experimented with the time required for each algorithm when executing large maps of (index,random('a'-'z') pairs.
10,000,000 20,000,000
Java 7: 615 ms 11624 ms
Java 8: 1579 ms 2176 ms
If you are open to using a library, use Google Guava's Multimaps utilities, specifically forMap() combined with invertFrom()
Yup, just brute force. You can make it fast by also storing a Multimap from Value -> Collection of Key, at the expense of memory and runtime cost for updates.
HashMap computes the hashcode() of the key, not of the values. Unless you store some kind of additional information, or consider using a different data structure, I think the only way you can get this is brute force.
If you need to perform efficient operation on the values, you should think whether you're using the appropriate data structure.
If you are using a hashmap there is no efficient way doing it but iterating the values
If you already have a map, you should consider using Google's Guava library to filter the entries you're interested in. You can do something along the lines of:
final Map<Integer, Character> filtered = Maps.filterValues(unfiltered, new Predicate<Character>() {
#Override
public boolean apply(Character ch) {
return ch == 'x';
}
});
I agree with George Campbell but for java 8 I would do it a bit easier:
Map<String, List<Integer>> reverseMap = map.entrySet()
.stream()
.collect(Collectors.groupingBy(Map.Entry::getValue,
Collectors.mapping(
Map.Entry::getKey,
Collectors.toList())));
Try This.....
public static void main(String[] args) {
HashMap<String, String> hashMap = new HashMap<String, String>();
hashMap.put("cust_tenure", "3_sigma");
hashMap.put("cust_age", "3_sigma");
hashMap.put("cust_amb_6m_sav", "3_sigma");
hashMap.put("cust_amb_6m_chq", "3_sigma");
hashMap.put("cust_total_prod_6m", "3_sigma");
HashMap<String, ArrayList<String>> result = new LinkedHashMap<String, ArrayList<String>>();
for (String key : hashMap.keySet()) {
ArrayList<String> colName = null;
if (!result.containsKey(hashMap.get(key))) {
colName = new ArrayList<String>();
colName.add(key);
result.put(hashMap.get(key), colName);
} else {
colName = result.get(hashMap.get(key));
colName.add(key);
result.put(hashMap.get(key), colName);
}
System.out.println(key + "\t" + hashMap.get(key));
}
for (String key : result.keySet()) {
System.out.println(key + "\t" + result.get(key));
}
System.out.println(hashMap.size());
}

Categories