How to merge two ArrayList<Map<String, String>> to one? - java

ArrayList<Map<String, String>> result1
result1 is like
(1, a)
(2, a)
(3, b)
(4, e)
(5, e)
ArrayList<Map<String, String>> result2
result2 is like
(1,android)
(2,ios)
(3,android)
(4,android)
(5,ios)
I want to merge the two maps to build a map like this one
(1, ( a, android))
(2, ( a, ios))
(3, ( b, android))
(4, (e, android))
(5, (e, ios))
How to make this happen?

You can merge two streams with Stream.concat() and group them with Collectors.groupingBy() and Collectors.mapping():
Map<String, String> first = Map.of("1", "a", "2", "a");
Map<String, String> second = Map.of("1", "android", "2", "ios");
Map<String, List<String>> result = Stream.concat(first.entrySet().stream(), second.entrySet().stream())
.collect(groupingBy(Entry::getKey, mapping(Entry::getValue, toList())));
System.out.println(result);
will output:
{1=[a, android], 2=[a, ios]}

For the requirement you have specified here, you can do it like this.
I'm iterating over the keys of the first map. And collecting values for each key from all the maps and putting them in a list. Then put the list to the resulting map.
import java.util.*;
public class MergeMaps
{
public static void main(String[] args)
{
Map<String, String> map1 = new HashMap<>();
map1.put("1", "a");
map1.put("2", "a");
map1.put("3", "b");
map1.put("4", "e");
map1.put("5", "e");
Map<String, String> map2 = new HashMap<>();
map2.put("1", "android");
map2.put("2", "ios");
map2.put("3", "android");
map2.put("4", "android");
map2.put("5", "ios");
Set<String> keys = new HashSet<>();
keys.addAll(map1.keySet());
keys.addAll(map2.keySet());
Map<String, List<String>> mergedMap = new HashMap<>();
for (String key : keys)
{
List<String> list = new ArrayList<>();
list.add(map1.get(key));
list.add(map2.get(key));
mergedMap.put(key, list);
}
System.out.println(mergedMap);
}
}
Output will be:
{1=[a, android], 2=[a, ios], 3=[b, android], 4=[e, android], 5=[e, ios]}

You can try this approach as well:
Map<String, String> result1 = new HashMap<>();
// initialize result1 ...
Map<String, String> result2 = new HashMap<>();
// initialize result2 ...
Map<String, Map<String, String>> mergedResult = new HashMap<>();
Up to Java 8
result1.forEach((k1, v1) ->
mergedResult.put(k1, new HashMap<String, String>() {{
put(v1, result2.get(k1));
}}));
Java 9 or later
result1.forEach((k1, v1) -> mergedResult.put(k1,
Map.of(v1, result2.get(k1))));

This is one way arriving at the result:
Input Data:
// The first list of data
List<Map<String, String>> list1 = new ArrayList<>();
list1.add(getMapData("1", "a"));
list1.add(getMapData("2", "a"));
list1.add(getMapData("3", "b"));
list1.add(getMapData("4", "e"));
list1.add(getMapData("5", "e"));
list1.add(getMapData("999", "x"));
System.out.println(list1);
Data 1: [{1=a}, {2=a}, {3=b}, {4=e}, {5=e}, {999=x}]
// The second list of data
List<Map<String, String>> list2 = new ArrayList<>();
list2.add(getMapData("1", "android"));
list2.add(getMapData("2", "ios"));
list2.add(getMapData("3", "android"));
list2.add(getMapData("4", "android"));
list2.add(getMapData("5", "ios"));
list2.add(getMapData("888", "zzzzz"));
System.out.println(list2);
Data 2: [{1=android}, {2=ios}, {3=android}, {4=android}, {5=ios}, {888=zzzzz}]
// utility method for creating test data
private static Map<String, String> getMapData(String k, String v) {
Map<String, String> m = new HashMap<>();
m.put(k, v);
return m;
}
The Result Process:
The output is stored to a Map<String, List<String>>:
Map<String, List<String>> result = new HashMap<>();
// process the first list
for (Map<String, String> m : list1) {
for (Map.Entry<String, String> entry : m.entrySet()) {
List<String> valueList = new ArrayList<>();
valueList.add(entry.getValue());
result.put(entry.getKey(), valueList);
}
}
// process the second list; merge with the first
for (Map<String, String> m : list2) {
for (Map.Entry<String, String> entry : m.entrySet()) {
String k = entry.getKey();
List<String> valueList = result.get(k);
if (valueList == null) {
valueList = new ArrayList<>();
}
valueList.add(entry.getValue());
result.put(k, valueList);
}
}
System.out.println(result);
The Result:
{1=[a, android], 2=[a, ios], 3=[b, android], 4=[e, android], 5=[e, ios], 888=[zzzzz], 999=[x]}

Related

How to print keys with duplicate values in a hashmap?

I have a hashmap with some keys pointing to same values. I want to find all the values that are equal and print the corresponding keys.
This is the current code that I have:
Map<String, String> map = new HashMap<>();
map.put("hello", "0123");
map.put("hola", "0123");
map.put("kosta", "0123");
map.put("da", "03");
map.put("notda", "013");
map.put("twins2", "01");
map.put("twins22", "01");
List<String> myList = new ArrayList<>();
for (Map.Entry<String, String> entry : map.entrySet()) {
for (Map.Entry<String, String> entry2 : map.entrySet()){
if (entry.getValue().equals(entry2.getValue()))
{
myList.add(entry.getKey());
}
}
}
The current code adds the duplicates two times into the list, however it also adds every key one time.
Thanks.
You can use streams to retrive duplicates in this way:
List<String> myList = map.stream()
.filter(n -> Collections.frequency(map.values(), n) > 1)
.collect(Collectors.toList());
And then, you can print this out with:
myList.foreach(System.out::println);
Build a Map<VALUE, List<KEY>>, i.e. a Map<String, List<String>>.
Example
Map<String, String> map = new HashMap<>();
map.put("hello", "0123");
map.put("hola", "0123");
map.put("kosta", "0123");
map.put("da", "03");
map.put("notda", "013");
map.put("twins2", "01");
map.put("twins22", "01");
map.entrySet().stream()
.collect(Collectors.groupingBy(Entry::getValue,
Collectors.mapping(Entry::getKey, Collectors.toList())))
.entrySet().stream()
.filter(e -> e.getValue().size() > 1)
.forEach(System.out::println);
Output
01=[twins22, twins2]
0123=[kosta, hello, hola]
Without the filter(), the result would be:
01=[twins22, twins2]
013=[notda]
03=[da]
0123=[kosta, hello, hola]
If you want a solution beside to Stream API;
public static void duplicatedValuesMap() {
Map<String, String> map = new HashMap<>();
map.put("hello", "0123");
map.put("hola", "0123");
map.put("kosta", "0123 test");
map.put("da", "03");
map.put("notda", "013");
map.put("twins2", "01");
map.put("twins22", "01");
HashMap<String, List<String>> valueToKeyMapCounter = new HashMap<>();
for (Map.Entry<String, String> entry : map.entrySet()) {
if (valueToKeyMapCounter.containsKey(entry.getValue())) {
valueToKeyMapCounter.get(entry.getValue()).add(entry.getKey());
} else {
List<String> keys = new ArrayList<>();
keys.add(entry.getKey());
valueToKeyMapCounter.put(entry.getValue(), keys);
}
}
for (Map.Entry<String, List<String>> counterEntry : valueToKeyMapCounter.entrySet()) {
if (counterEntry.getValue().size() > 1) {
System.out.println("Duplicated Value:" + counterEntry.getKey() + " for Keys:" + counterEntry.getValue());
}
}
}
I think other answers already good to solve the question, i support another method to do just for extended thinking.This method need use Guava's MutliMap interface:
// init the input map
Map<String, String> map = new HashMap<>();
map.put("hello", "0123");
map.put("hola", "0123");
map.put("kosta", "0123");
map.put("da", "03");
map.put("notda", "013");
map.put("twins2", "01");
map.put("twins22", "01");
// swap key and value of the input map,since different key has same value
// so we need Multimap
Multimap<String, String> container = ArrayListMultimap.create();
map.entrySet().forEach(entry -> container.put(entry.getValue(), entry.getKey()));
container.keySet().stream()
.filter(s -> container.get(s).size() > 1).
forEach(System.out::println);
output:
01
0123

Convert a map to list of map

I have a requirement where i want to convert a map into list of map.
I tried the following code,
List<Map<String, Object>> response = new ArrayList<>();
Set<Long> categories = new HashSet<>();
List<CCP> ccpList = ccpRepository.findByPIdIn(ids);
Map<Integer, List<Integer>> categoriesByProduct = ccpList.stream()
.collect(Collectors.groupingBy(ccp -> ccp.getPId(), Collectors.mapping(ccp-> ccp.getCId(), Collectors.toList())));
for(Entry<Integer, List<Integer>> en : categoriesByProduct.entrySet()) {
Map<String,Object> responseObject = new HashMap<>();
responseObject.put("pid", en.getKey());
responseObject.put("categories",en.getValue());
response.add(responseObject);
}
Is there any way in which i can modify the following code in java 8 style:
for(Entry<Integer, List<Integer>> en : categoriesByProduct.entrySet()) {
Map<String,Object> responseObject = new HashMap<>();
responseObject.put("pid", en.getKey());
responseObject.put("categories",en.getValue());
response.add(responseObject);
}
Put the for-loop-content in a function and use it with streams:
response = categoriesByProduct.entrySet()
.stream()
.map(e -> toMap(e))
.collect(Collectors.toList());
public static Map<String, Object> toMap(Map.Entry<Integer, List<Integer>> en) {
Map<String,Object> responseObject = new HashMap<>();
responseObject.put("pid", en.getKey());
responseObject.put("categories",en.getValue());
return responseObject;
}
You may do it like so,
List<Map<String, Object>> result = categoriesByProduct.entrySet().stream()
.map(e -> Stream
.of(new AbstractMap.SimpleEntry<String, Object>("pid", e.getKey()),
new AbstractMap.SimpleEntry<String, Object>("categories", e.getValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))
.collect(Collectors.toList());

How to Compare 2 LinkedHashMap and get difference in keys & values?

There are 2 LinkedHashMaps. I have to check if both LinkedHashMaps have same keys & values. If one LinkedHashMap contains extra keys + values then it should get printed or stored in 3rd LinkedHashMap. If any of the key or value is different in 2nd LinkedHashMap then it should be notified (print in console)
Eg
Map1 contains {A-a, B-b, C-c}
Map2 contains {A-a, B-r, C-c, Z-z}
So here 3rd LinkedHashMap should contain {Z-z} as it contains extra key and
{B-r} should get printed as it is modified
you can use this class :)
public class MapCompare {
private LinkedHashMap<String, String> differencesMap;
private LinkedHashMap<String, String> extraMap;
private void main(LinkedHashMap<String, String> map1, LinkedHashMap<String, String> map2) {
this.differencesMap = new LinkedHashMap<>();
this.extraMap = new LinkedHashMap<>();
Set<Map.Entry<String, String>> set1 = ((LinkedHashMap<String, String>) map1.clone()).entrySet();
Set<Map.Entry<String, String>> set2 = ((LinkedHashMap<String, String>) map2.clone()).entrySet();
set1.removeAll(set2);
compare(set1, map2);
set1 = map1.entrySet();
set2.removeAll(set1);
compare(set2, map1);
}
private LinkedHashMap<String, String> getDifferencesMap() {
return differencesMap;
}
private LinkedHashMap<String, String> getExtraMap() {
return extraMap;
}
private void compare(Set<Map.Entry<String, String>> set, LinkedHashMap<String, String> map) {
for (Map.Entry<String, String> entry : set) {
String key = entry.getKey();
String value = entry.getValue();
if (map.containsKey(key) || map.containsValue(value)) {
differencesMap.put(key, value);
} else {
extraMap.put(key, value);
}
}
}
public static void main(String[] args) {
LinkedHashMap<String, String> map1 = new LinkedHashMap<>();
map1.put("A", "a");
map1.put("B", "b");
map1.put("C", "c");
LinkedHashMap<String, String> map2 = new LinkedHashMap<>();
map2.put("A", "a");
map2.put("C", "c");
map2.put("B", "r");
map2.put("Z", "z");
MapCompare mapCompare = new MapCompare();
mapCompare.main(map1,map2);
System.out.println("diff: " + mapCompare.getDifferencesMap());
System.out.println("extra: " + mapCompare.getExtraMap());
}
}

Filter object from List if filter condition is multiple and coming dynamically

I have a requirement where I have to filter object from list based on multiple dynamic filter condition.
I have already written code by looping over objects and then all filter and returning false if any condition doesn't match. The code that I have written is as
Map<String, String> obj1 = new HashMap<>();
obj1.put("id", "1");
obj1.put("name", "name1");
obj1.put("dept", "IT");
obj1.put("sex", "M");
Map<String, String> obj2 = new HashMap<>();
obj2.put("id", "2");
obj2.put("name", "name2");
obj2.put("dept", "IT");
obj2.put("sex", "M");
Map<String, String> obj3 = new HashMap<>();
obj3.put("id", "3");
obj3.put("name", "name3");
obj3.put("dept", "DEV");
obj3.put("sex", "F");
ArrayList<Map<String, String>> employees = new ArrayList<>(Arrays.asList(obj1,obj2,obj3));
Map<String, String> filterCondition = new HashMap<>();
filterCondition.put("dept", "IT");
filterCondition.put("sex", "M");
List<Map<String, String>> filteredEmployee = new ArrayList<>();
for(Map<String,String> employee:employees){
if(isValid(filterCondition, employee)){
filteredEmployee.add(employee);
}
}
System.out.println(filteredEmployee);
isValid method is as
private static boolean isValid(Map<String, String> filterCondition, Map<String, String> employee) {
for(Entry<String, String> filterEntry:filterCondition.entrySet()){
if(!employee.get(filterEntry.getKey()).equals(filterEntry.getValue())){
return false;
}
}
return true;
}
Is there any better way to achieve it if filters that I am getting is coming dynamically.
I have already seen some answer in stackoverflow as here ,but with no help
Combine all filters as a single Predicate (using stream, reduce, and predicate composition):
Predicate<Map<String, String>> allConditions = filterCondition
.entrySet()
.stream()
.map(ThisClass::getAsPredicate)
.reduce((employee) -> true, Predicate::and);
Then just use Stream.filter()
List<Map<String, String>> filteredEmployees = employees
.stream()
.filter(allConditions)
.collect(Collectors.toList());
Helper function:
private static Predicate<Map<String, String>> getAsPredicate(Map.Entry<String, String> filter) {
return (Map<String, String> employee) -> employee.get(filter.getKey()).equals(filter.getValue());
}
Maybe you can use for-loop with Stream:
Stream<Map<String, String>> employeeStream = employees.stream();
for (Map.Entry<String, String> entry : filterCondition.entrySet()) {
employeeStream = employeeStream.filter(map -> entry.getValue()
.equals(map.get(entry.getKey())));
}
List<Map<String, String>> filteredEmployee = employeeStream.collect(Collectors.toList());

compareing and merging two TreeMaps

I have produced two multimaps in Java like this:
TreeMap1 = {key1=[a,b,c], key2=[d,e,f]}
TreeMap2 = {key1=[j,k], key2=[z,p]}
How can I add the values of TreeMap2, with similar key as in TreeMap1, to the values of TreeMap1, and produce a third TreeMap Like:
TreeMap3 = {key1=[a,b,c,j,k], key2=[d,e,f,z,p]}
This Method gives me nothing.
public TreeMap<String, List<String>> make_TreeMap3(){
for(Entry<String,List<String>> entry_t1 :this.map_FuncType.entrySet()){
for(Entry<String,List<String>> entry_t2 : this.map_AufOrgUserField.entrySet()){
if(entry_t2.getKey().contains(entry_t1.getKey())){
entry_t2.getValue().addAll(entry_t1.getValue());
}
}
}
return map_mergOrgandType;
}
Something like this?
TreeMap<String, List<String>> treeMap3 = new TreeMap<>(treeMap1);
for (String k : treeMap3.keySet()) {
if (treeMap2.containsKey(k)) {
treeMap3.get(k).addAll(treeMap2.get(k));
}
}
return treeMap3;
It's very easy to do in Guava if you're open to using a 3rd party library.
public class MultimapMerge {
public static void main(String[] args) {
Multimap<String, String> map1 = TreeMultimap.create();
map1.put("key1", "a");
map1.put("key1", "b");
map1.put("key1", "c");
map1.put("key2", "d");
map1.put("key2", "e");
map1.put("key2", "f");
Multimap<String, String> map2 = TreeMultimap.create();
map2.put("key1", "j");
map2.put("key1", "k");
map2.put("key2", "z");
map2.put("key2", "p");
Multimap<String, String> map3 = TreeMultimap.create();
map3.putAll(map1);
map3.putAll(map2);
System.out.println(map3);
}
}
http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Multimap.html

Categories

Resources