I need to convert a String to List<Map<String, String>>> for pass JUnit Test. I have this:
String userAttributes = "[{name=test, cp=458999, lastname=test2}]";
That i want is in the tests (Mockito) change a call to a server with this values, something like this:
Mockito.when(template.search(Mockito.anyString, new AttributesMapper()).thenReturn(attributes);
I need List<Map<String, String>>> for do this:
user.setUserName(attributes.get("name"));
Try regex or split by special char. First remove parentheses from beginning and end. After that you can split it by , and = to collect strings to map.
String userAttributes = "[{name=test, cp=458999, lastname=test2}]";
List<String> strings = Arrays.asList(userAttributes
.replace("[{","").replace("}]","")
.split(", "));
Map<String, String> collect = strings.stream()
.map(s -> s.split("="))
.collect(Collectors.toMap(s -> s[0], s -> s[1]));
System.out.println(collect.get("name"));
Other approach with Pattern
Map<String, String> collect = Pattern.compile(",")
.splitAsStream(userAttributes
.replace("[{","").replace("}]",""))
.map(s -> s.split("="))
.collect(Collectors.toMap(s -> s[0], s -> s[1]));
Or if you really wants use List<Map<String, String>>>. But after that you can not do this user.setUserName(attributes.get("name"));
List<Map<String, String>> maps = strings.stream()
.map(s -> s.split("="))
.map(s -> Map.of(s[0], s[1]))
.collect(Collectors.toList());
System.out.println(maps);
String userAttributes = "[{name=test, cp=458999, lastname=test2}]";
StringTokenizer stringTokenizer = new StringTokenizer(userAttributes,",");
List<Map<String,String>> list = new ArrayList<>();
while(stringTokenizer.hasMoreElements()){
StringTokenizer stringTokenizer2 = new StringTokenizer((String)stringTokenizer.nextElement(),"=");
while(stringTokenizer2.hasMoreElements()){
Map<String, String> map = new HashMap<>();
map.put( ((String)stringTokenizer2.nextElement()),((String)stringTokenizer2.nextElement()) );
list.add(map);
}
}
System.err.println(list.toString());
Related
I have a List of String like:
List<String> locations = Arrays.asList("US:5423","US:6321","CA:1326","AU:5631");
And I want to convert in Map<String, List<String>> as like:
AU = [5631]
CA = [1326]
US = [5423, 6321]
I have tried this code and it works but in this case, I have to create a new class GeoLocation.java.
List<String> locations=Arrays.asList("US:5423", "US:6321", "CA:1326", "AU:5631");
Map<String, List<String>> locationMap = locations
.stream()
.map(s -> new GeoLocation(s.split(":")[0], s.split(":")[1]))
.collect(
Collectors.groupingBy(GeoLocation::getCountry,
Collectors.mapping(GeoLocation::getLocation, Collectors.toList()))
);
locationMap.forEach((key, value) -> System.out.println(key + " = " + value));
GeoLocation.java
private class GeoLocation {
private String country;
private String location;
public GeoLocation(String country, String location) {
this.country = country;
this.location = location;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}
But I want to know, Is there any way to convert List<String> to Map<String, List<String>> without introducing new class.
You may do it like so:
Map<String, List<String>> locationMap = locations.stream()
.map(s -> s.split(":"))
.collect(Collectors.groupingBy(a -> a[0],
Collectors.mapping(a -> a[1], Collectors.toList())));
A much more better approach would be,
private static final Pattern DELIMITER = Pattern.compile(":");
Map<String, List<String>> locationMap = locations.stream()
.map(s -> DELIMITER.splitAsStream(s).toArray(String[]::new))
.collect(Collectors.groupingBy(a -> a[0],
Collectors.mapping(a -> a[1], Collectors.toList())));
Update
As per the following comment, this can be further simplified to,
Map<String, List<String>> locationMap = locations.stream().map(DELIMITER::split)
.collect(Collectors.groupingBy(a -> a[0],
Collectors.mapping(a -> a[1], Collectors.toList())));
Try this
Map<String, List<String>> locationMap = locations.stream()
.map(s -> new AbstractMap.SimpleEntry<String,String>(s.split(":")[0], s.split(":")[1]))
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
You can just put the code in grouping by part where you put first group as key and second as value instead of mapping it first
Map<String, List<String>> locationMap = locations
.stream()
.map(s -> s.split(":"))
.collect( Collectors.groupingBy( s -> s[0], Collectors.mapping( s-> s[1], Collectors.toList() ) ) );
What about POJO. It looks not complicated comparing with streams.
public static Map<String, Set<String>> groupByCountry(List<String> locations) {
Map<String, Set<String>> map = new HashMap<>();
locations.forEach(location -> {
String[] parts = location.split(":");
map.compute(parts[0], (country, codes) -> {
codes = codes == null ? new HashSet<>() : codes;
codes.add(parts[1]);
return codes;
});
});
return map;
}
Seems like your location map needs to be sorted based on keys, you can try the following
List<String> locations = Arrays.asList("US:5423", "US:6321", "CA:1326", "AU:5631");
Map<String, List<String>> locationMap = locations.stream().map(str -> str.split(":"))
.collect(() -> new TreeMap<String, List<String>>(), (map, parts) -> {
if (map.get(parts[0]) == null) {
List<String> list = new ArrayList<>();
list.add(parts[1]);
map.put(parts[0], list);
} else {
map.get(parts[0]).add(parts[1]);
}
}, (map1, map2) -> {
map1.putAll(map2);
});
System.out.println(locationMap); // this outputs {AU=[5631], CA=[1326], US=[5423, 6321]}
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
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]}
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());
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());