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]}
Related
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).
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.
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.
I want to be able to convert a List to a HashMap where the key is the elementName and the values is a list of something random (in this case its the Element Name). So in short I want (A->List(A), B->List(B), C-> List(C)). I tried using toMap() and passing it the keyMapper and ValueMapper but I get a compilation error. I would really appreciate if someone can help me out.
Thanks!
public static void main(String[] args) {
// TODO Auto-generated method stub
List<String> list = Arrays.asList("A","B","C","D");
Map<String, List<String>> map = list.stream().map((element)->{
Map<String, List<String>> map = new HashMap<>();
map.put(element, Arrays.asList(element));
return map;
}).collect(??);
}
Function<Map<String, String>, String> key = (map) -> {
return map.keySet().stream().findFirst().get();
};
Function<Map<String, String>, String> value = (map) -> {
return map.values().stream().findFirst().get();
};
=== This worked for me
Thanks for all the help guys! #izstas "they should operate on the elements" helped a lot :). Actually this is what I was looking for to be exact
public static void test2 (){
Function<Entry<String, List<String>>, String> key = (entry) -> {
return entry.getKey();
};
Function<Entry<String, List<String>>, List<String>> value = (entry) -> {
return new ArrayList<String>(entry.getValue());
};
BinaryOperator<List<String>> merge = (old, latest)->{
old.addAll(latest);
return old;
};
Map<String, List<String>> map1 = new HashMap<>();
map1.put("A", Arrays.asList("A1", "A2"));
map1.put("B", Arrays.asList("B1"));
map1.put("D", Arrays.asList("D1"));
Map<String, List<String>> map2 = new HashMap<>();
map2.put("C", Arrays.asList("C1","C2"));
map2.put("D", Arrays.asList("D2"));
Stream<Map<String, List<String>>> stream =Stream.of(map1, map2);
System.out.println(stream.flatMap((map)->{
return map.entrySet().stream();
}).collect(Collectors.toMap(key, value, merge)));
}
You can use the groupingBy method to manage aggregation, for example:
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B", "C", "D", "A");
Map<String, List<String>> map = list.stream().collect(Collectors.groupingBy(Function.identity()));
}
If you want more flexibility (for example to map the value and return a Set instead of a List) you can always use the groupingBy method with more parameters as specified in javadoc:
Map<City, Set<String>> namesByCity = people.stream().collect(Collectors.groupingBy(Person::getCity, mapping(Person::getLastName, toSet())));
Functions key and value you have defined in your code are not correct because they should operate on the elements of your list, and your elements are not Maps.
The following code works for me:
List<String> list = Arrays.asList("A", "B", "C", "D");
Map<String, List<String>> map = list.stream()
.collect(Collectors.toMap(Function.identity(), Arrays::asList));
First argument to Collectors.toMap defines how to make a key from the list element (leaving it as is), second argument defines how to make a value (making an ArrayList with a single element).
Thanks for all the help guys! #izstas "they should operate on the elements" helped a lot :). Actually this is what I was looking for to be exact
public static void test2 (){
Function<Entry<String, List<String>>, String> key = (entry) -> {
return entry.getKey();
};
Function<Entry<String, List<String>>, List<String>> value = (entry) -> {
return new ArrayList<String>(entry.getValue());
};
BinaryOperator<List<String>> merge = (old, latest)->{
old.addAll(latest);
return old;
};
Map<String, List<String>> map1 = new HashMap<>();
map1.put("A", Arrays.asList("A1", "A2"));
map1.put("B", Arrays.asList("B1"));
map1.put("D", Arrays.asList("D1"));
Map<String, List<String>> map2 = new HashMap<>();
map2.put("C", Arrays.asList("C1","C2"));
map2.put("D", Arrays.asList("D2"));
Stream<Map<String, List<String>>> stream =Stream.of(map1, map2);
System.out.println(stream.flatMap((map)->{
return map.entrySet().stream();
}).collect(Collectors.toMap(key, value, merge)));
}
I have the following maps.
Map<String,String> map1= new HashMap<String, String>(){{
put("no1","123"); put("no2","5434"); put("no5","234");}};
Map<String,String> map1 = new HashMap<String, String>(){{
put("no1","523"); put("no2","234"); put("no3","234");}};
sum(map1, map2);
I want to join them to one, summing up similar keyed values together. What;s the best way I could do it using java 7 or guava libraries ?
expected output
Map<String, String> output = { { "no1" ,"646"}, { "no2", "5668"}, {"no5","234"}, {"no3","234" } }
private static Map<String, String> sum(Map<String, String> map1, Map<String, String> map2) {
Map<String, String> result = new HashMap<String, String>();
result.putAll(map1);
for (String key : map2.keySet()) {
String value = result.get(key);
if (value != null) {
Integer newValue = Integer.valueOf(value) + Integer.valueOf(map2.get(key));
result.put(key, newValue.toString());
} else {
result.put(key, map2.get(key));
}
}
return result;
}
try this
Map<String, List<String>> map3 = new HashMap<String, List<String>>();
for (Entry<String, String> e : map1.entrySet()) {
List<String> list = new ArrayList<String>();
list.add(e.getValue());
String v2 = map2.remove(e.getKey());
if (v2 != null) {
list.add(v2);
}
map3.put(e.getKey(), list);
}
for (Entry<String, String> e : map2.entrySet()) {
map3.put(e.getKey(), new ArrayList<String>(Arrays.asList(e.getValue())));
}
Java 8 introduces Map.merge(K, V, BiFunction), which makes this easy if not particularly concise:
Map<String, String> result = new HashMap<>(map1);
//or just merge into map1 if mutating it is okay
map2.forEach((k, v) -> result.merge(k, v, (a, b) ->
Integer.toString(Integer.parseInt(a) + Integer.parseInt(b))));
If you're doing this repeatedly, you're going to be parsing and creating a lot of strings. If you're generating the maps one at a time, you're best off maintaining a list of strings and only parsing and summing once.
Map<String, List<String>> deferredSum = new HashMap<>();
//for each map
mapN.forEach((k, v) ->
deferredSum.computeIfAbsent(k, x -> new ArrayList<String>()).add(v));
//when you're done
Map<String, String> result = new HashMap<>();
deferredSum.forEach((k, v) -> result.put(k,
Long.toString(v.stream().mapToInt(Integer::parseInt).sum())));
If this summing is a common operation, consider whether using Integer as your value type makes more sense; you can use Integer::sum as the merge function in that case, and maintaining lists of deferred sums would no longer be necessary.
Try this
Map<String,String> map1= new HashMap<String, String>(){{
put("no1","123"); put("no2","5434"); put("no5","234");}};
Map<String,String> map2 = new HashMap<String, String>(){{
put("no1","523"); put("no2","234"); put("no3","234");}};
Map<String,String> newMap=map1;
for(String a:map2.keySet()){
if(newMap.keySet().contains(a)){
newMap.put(a,""+(Integer.parseInt(newMap.get(a))+Integer.parseInt(map2.get(a))));
}
else{
newMap.put(a,map2.get(a));
}
}
for(String k : newMap.keySet()){
System.out.println("key : "+ k + " value : " + newMap.get(k));
}