I have a map with "task(s)": ["epochdate1", "epochdate2"] how can I reverse this map?
For example
task_1 => [date1, date2, date3, date5]
task_2 => [date4, date5]
task_3 => [date2, date3, date5]
task_4 => [date4, date5]
when reversed it will be
date1 => [task_1]
date2 => [task_1, task_3]
date3 => [task_1, task_3]
date4 => [task_2, task_4]
date5 => [task_1, task_2, task_3, task_4]
Code
public static void main(String[] args) {
// TODO Auto-generated method stub
Map<String, ArrayList<String>> myMap = new HashMap<String, ArrayList<String>>();
ArrayList<String> t1List = new ArrayList<String>();
t1List.add("date1");
t1List.add("date2");
t1List.add("date3");
t1List.add("date5");
ArrayList<String> t2List = new ArrayList<String>();
t2List.add("date4");
t2List.add("date5");
ArrayList<String> t3List = new ArrayList<String>();
t3List.add("date2");
t3List.add("date3");
t3List.add("date5");
ArrayList<String> t4List = new ArrayList<String>();
t4List.add("date4");
t4List.add("date5");
myMap.put("task_1", t1List);
myMap.put("task_2", t2List);
myMap.put("task_3", t3List);
myMap.put("task_4", t4List);
Map<String, ArrayList<String>> reversedMap = Test.getReversedMap(myMap);
}
Just implement it:
Map<String, ArrayList<String>> getReversedMap(Map<String, ArrayList<String>> myMap){
Map<String, ArrayList<String>> result = new HashMap<>();
for(String key : myMap.keySet()){
for(String val : myMap.get(key)){
if(!result.containsKey(val)){
result.put(val, new ArrayList());
}
result.get(val).add(key);
}
}
return result;
}
There are no short cuts. You have to iterate over the entrySet of the original map, and for each date value you find, put a new entry in the new map with the matching task as the first value, or add the task to the list of an existing entry if the date is already in the new map.
I would use Guava for this. Try something like:
Map<K, V> map = ...;
ListMultimap<V, K> inverse = Multimaps.invertFrom(Multimaps.forMap(map),
ArrayListMultimap.<V,K>create());
Related
I have a list of Maps as below:
List<Map<String,Object>> someObjectsList = new ArrayList<Map<String,Object>>();
I am storing the following data in each HashMap
key value
2017-07-21 2017-07-21-07.33.28.429340
2017-07-24 2017-07-24-01.23.33.591340
2017-07-24 2017-07-24-01.23.33.492340
2017-07-21 2017-07-21-07.33.28.429540
I want to iterate through the list of HashMaps and check if the key matches with the first 10 characters of any of the HashMap value, then I want to store those keys and values in the following format. i.e. by using the telemeter 'comma'. The ultimate aim is to group the unique keys of the HashMaps and their relative values (if the key matches with the first 10 characters of any of the HashMap value) in a new HashMap.
key value
2017-07-21 2017-07-21-07.33.28.429340,2017-07-21-07.33.28.429540
2017-07-24 2017-07-24-01.23.33.591340,2017-07-24-01.23.33.492340
I am trying with following java code using StringJoiner, but not getting the results as expected. Any clue on how to frame the logic here?
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
public class SampleOne {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Map<String, Object>> someObjectsList = new ArrayList<Map<String, Object>>();
Map<String, Object> mapOne = new HashMap<String, Object>();
mapOne.put("2017-07-21", "2017-07-21-07.33.28.429340");
Map<String, Object> mapTwo = new HashMap<String, Object>();
mapTwo.put("2017-07-24", "2017-07-24-01.23.33.591340");
Map<String, Object> mapThree = new HashMap<String, Object>();
mapThree.put("2017-07-24", "2017-07-24-01.23.33.492340");
Map<String, Object> mapFour = new HashMap<String, Object>();
mapFour.put("2017-07-21", "2017-07-21-07.33.28.429540");
someObjectsList.add(mapOne);
someObjectsList.add(mapTwo);
someObjectsList.add(mapThree);
someObjectsList.add(mapFour);
for (Map map : someObjectsList) {
StringJoiner sj = new StringJoiner(",");
for (Object key : map.keySet()) {
String value = ((String) map.get(key));
String date = value.substring(0, Math.min(value.length(), 10));
//System.out.println(str);
//System.out.println(value);
if(key.equals(date)) {
sj.add(value);
System.out.println(sj.toString());
}
}
}
}
}
output:
2017-07-21-07.33.28.429340
2017-07-24-01.23.33.591340
2017-07-24-01.23.33.492340
2017-07-21-07.33.28.429540
Make use of the .merge function:
Map<String, Object> finalMap = new HashMap<String, Object>();
for (Map map : someObjectsList) {
for (Object key : map.keySet()) {
String value = ((String) map.get(key));
finalMap.merge((String) key, value, (k, v) -> k + "," + v);
}
}
which outputs:
{2017-07-21=2017-07-21-07.33.28.429340,2017-07-21-07.33.28.429540,
2017-07-24=2017-07-24-01.23.33.591340,2017-07-24-01.23.33.492340}
The same can be achieved by the following one-liner:
someObjectsList.stream()
.flatMap(i -> i.entrySet().stream())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue,
(k, v) -> k + "," + v));
On your code, you are using different StringJoiner on each map. So, it's creating a new instance of it.
You can save your keys on a map. An example code:
(Edit: I did not remove your StringJoiner part.)
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Map<String, Object>> someObjectsList = new ArrayList<Map<String, Object>>();
Map<String, Object> mapOne = new HashMap<String, Object>();
mapOne.put("2017-07-21", "2017-07-21-07.33.28.429340");
Map<String, Object> mapTwo = new HashMap<String, Object>();
mapTwo.put("2017-07-24", "2017-07-24-01.23.33.591340");
Map<String, Object> mapThree = new HashMap<String, Object>();
mapThree.put("2017-07-24", "2017-07-24-01.23.33.492340");
Map<String, Object> mapFour = new HashMap<String, Object>();
mapFour.put("2017-07-21", "2017-07-21-07.33.28.429540");
someObjectsList.add(mapOne);
someObjectsList.add(mapTwo);
someObjectsList.add(mapThree);
someObjectsList.add(mapFour);
Map<String, Object> outputMap = new HashMap<String, Object>();
for (Map map : someObjectsList) {
StringJoiner sj = new StringJoiner(",");
for (Object key : map.keySet()) {
String value = ((String) map.get(key));
String date = value.substring(0, Math.min(value.length(), 10));
//System.out.println(str);
//System.out.println(value);
if(key.equals(date)) {
sj.add(value);
System.out.println(sj.toString());
if(outputMap.containsKey(key)) {
String str = (String) map.get(key);
str = str + "," + value;
outputMap.put((String)key, str);
} else {
outputMap.put((String)key, value);
}
}
}
}
for (String map : outputMap.keySet()) {
System.out.println(map + " " + outputMap.get(map));
}
}
You are looking for the grouping behavior of processing a List. You can use the advantage of java-stream since java-8. In any case, you need a new Map to store the values in order to print them. :
someObjectsList.stream()
.flatMap(i -> i.entrySet().stream()) // flatmapping to entries
.collect(Collectors.groupingBy(Entry::getKey)) // grouping them using the key
In case you want to use for-loops. In this case it is harder since the more entries might appear in each List item:
final Map<String, List<Object>> map = new HashMap<>();
for (Map<String, Object> m: someObjectsList) { // iterate List<Map>
for (Entry<String, Object> entry: m.entrySet()) { // iterate entries of each Map
List<Object> list;
final String key = entry.getKey(); // key of the entry
final Object value = entry.getValue(); // value of the entry
if (map.containsKey(key)) { // if the key exists
list = map.get(key); // ... use it
} else {
list = new ArrayList<>(); // ... or else create a new one
}
list.add(value); // add the new value
map.put(key, list); // and add/update the entry
}
}
Printing out of Map<String, List<Object>> map in both cased will produce the following output:
2017-07-21=[2017-07-21-07.33.28.429340, 2017-07-21-07.33.28.429540],
2017-07-24=[2017-07-24-01.23.33.591340, 2017-07-24-01.23.33.492340]
Any reason you're using Object over String and avoiding safety checks? That said, it's not "the first 10 characters", you want to see if value starts with key full-stop (all your keys are 10 characters). So in that case you can just do if (value.startsWith(key)) { ... }. Don't forget your newlines if the stringjoiner wasn't full. Lastly, you don't need a List, a Map can hold multiple keys at once. An alternative way of doing it:
//LinkedHashMap will preserve our insertion order
Map<String, String> map = new LinkedHashMap<>();
map.put("2017-07-21", "2017-07-21-07.33.28.429340");
map.put("2017-07-24", "2017-07-24-01.23.33.591340");
//note duplicates are overwritten, but no value change here
map.put("2017-07-24", "2017-07-24-01.23.33.492340");
map.put("2017-07-21", "2017-07-21-07.33.28.429540");
// You can also use Java 8 streams for the concatenation
// but I left it simple
List<String> matches = map.entrySet()
.filter(e -> e.getValue().startsWith(e.getKey())
.collect(Collectors.toList());
String concatenated = String.join("\n", matches);
If you wanted to generate that string without streams, it would look like this (again, not using #entrySet for simplicity, but it would be more efficient here):
List<String> matches = new ArrayList<>();
StringJoiner joiner = new StringJoiner("\n");
for (String key : map.keySet()) {
String value = map.get(key);
if (value.startsWith(key)) {
joiner.add(value);
}
}
//joiner#toString will give the expected result
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 am trying to set a hashmap to have key as string and a list array as value. Is it possible? and how do I set the list into the value?
HashMap<String, List<String>> foo = new HashMap<String, List<String>>();
foo.put("key1",{"key1_value1","key1_value2"});
You can do the following
Map<String, List<String>> foo = new HashMap<String, List<String>>();
List<String> list = new ArrayList<String>();
list.add("key1_value1");
list.add("key1_value2");
foo.put("key1",list);
foo.put("key", Arrays.asList("key1_val1", "key1_val2"));
You have to use a data structure like ArrayList or just an array maybe to represent the list of strings as value.
You can use the following with a List;
foo.put("key", Arrays.asList("key1_val1", "key1_val2"));
where foo is of type Map<String, List<String>>
Or you the following with an array;
foo.put("key", new String[]{"key1_val1", "key1_val2"});
where foo is of type Map<String, String[]>
Map<String, List<String>> foo = new HashMap<String, List<String>>();
List<String> list = new ArrayList<String>();
list.add("value1");
list.add("value2");
foo.put("key1", list);
for (Entry<String, List<String>> entry : foo.entrySet()) {
String key = entry.getKey();
List<String> valueList = entry.getValue();
System.out.println("key = " + key);
for (String value : valueList) {
System.out.println("value = " + value);
}
}
Output
key = key1
value = value1
value = value2
I have below list, i want to convert list to map.
SurveyAllocationUsers user1 = new SurveyAllocationUsers();
user1.setSurveyorId("1");
user1.setSurveyorTypeCode("LSR");
SurveyAllocationUsers user2 = new SurveyAllocationUsers();
user2.setSurveyorId("1");
user2.setSurveyorTypeCode("SR");
SurveyAllocationUsers user3 = new SurveyAllocationUsers();
user3.setSurveyorId("2");
user3.setSurveyorTypeCode("LSR");
SurveyAllocationUsers user4 = new SurveyAllocationUsers();
user4.setSurveyorId("2");
user4.setSurveyorTypeCode("SR");
SurveyAllocationUsers user5 = new SurveyAllocationUsers();
user5.setSurveyorId("2");
user5.setSurveyorTypeCode("BG");
List<SurveyAllocationUsers> list = new ArrayList<SurveyAllocationUsers>();
list.add(user1);list.add(user2);list.add(user3);list.add(user4);list.add(user5);
want to convert list to map like below.
Map<String,List<String>> usersMap = new HashMap<String, List<String>>();
map key will be SurveyorId and values will be correspondiing List of SurveyorTypeCode.
Thanks for the help in advance!!!
It should be something like below
userMap.put(user1.getSurveyorId(), new ArrayList<>().add(user1.getSurveyorTypeCode));
userMap.put(user2.getSurveyorId(), new ArrayList<>().add(user2.getSurveyorTypeCode));
It should be something like this:
List<SurveyAllocationUsers> list = new ArrayList<SurveyAllocationUsers>();
list.add(user1);list.add(user2);list.add(user3);list.add(user4);list.add(user5);
Map<String,List<String>> usersMap = new HashMap<String, List<String>>();
for(SurveyAllocationUsers sau: list){
if(!userMap.contains(sau.getSurveyorId())){
userMap.put(sau.getSurveyorId(), new ArrayList<>().add(sau.getSurveyorTypeCode));
}else{
userMap.get(sau.getSurveyorId()).add(sau.getSurveyorTypeCode);
}
}
Note: Code in not complied.
A little bit ugly but solves your problem:
Map<String, List<String>> map = list.stream().collect(toMap(SurveyAllocationUsers::getSurveyorId, p -> new ArrayList(singletonList(p.getSurveyorTypeCode())), (s, a) -> {
s.addAll(a);
return s;
}));
You can try this:
Map<String, List<String>> usersMap = new HashMap<String, List<String>>();
for (SurveyAllocationUsers user : list) {
List<String> typeCodes = new ArrayList<>();
typeCodes.add(user.getSurveyorTypeCode());
usersMap.put(user.getSurveyorId(), typeCodes);
}
Thank you everyone for your reply, I got the solution as below.
List <String>newUserList = null;
Map<String,List<String>> usersMap = new HashMap<String, List<String>>();
for(SurveyAllocationUsers sau: list){
if(usersMap.containsKey(sau.getSurveyorId())){
List <String>myList = usersMap.get(sau.getSurveyorId());
myList.add(sau.getSurveyorTypeCode());
usersMap.put(sau.getSurveyorId(), myList);
}else{
if(sau.getSurveyorId() != null){
newUserList = new ArrayList<String>();
newUserList.add(sau.getSurveyorTypeCode());
usersMap.put(sau.getSurveyorId(), newUserList);
}
}
}
Answer as expected
1 [LSR, SR]
2 [LSR, SR, BG]
It should be like this using Java8
resMap = list.stream().collect(Collectors.groupingBy(SurveyAllocationUsers::getSurveyorId,
Collectors.mapping(SurveyAllocationUsers::getSurveyorTypeCode,Collectors.toList())));
Output: {1=[LSR, SR], 2=[LSR, SR, BG]}
I am wondering if there was a way to merge keys together based on same values in the Hashmap.
Ex.
A->2
B->1
C->2
The result I am looking for is:
2 - A, C
1 - B
One of the ways is to have your map defined like this:
Map<String,List<String>> multiValueMap = new HashMap();
Or if you don't want to reinvent the wheel, then use Apache MultiMap
You will have to change the data structure to store data in this way. Use a HashMap of ArrayList or Set (as appropriate for you) to do this.
Example
HashMap<Character, Integer> map = new HashMap<>();
map.put('A', 2);
map.put('B', 1);
map.put('c', 2);
// SORTED
HashMap<Integer, List<Character>> sortedMap = new HashMap<>();
ArrayList<Character> list = new ArrayList<>();
for(Map.Entry<Character, Integer> entry : map.entrySet()){
list = sortedMap.get(entry.getValue());
if(list == null){
list = new ArrayList<>();
entry.put(entry.getValue(), list);
}
list.add(entry.getKey());
}
HashMap Reversal
HashMap<String,Integer> aHM = new HashMap<String,Integer>();
HashMap<Integer,ArrayList<String>> aHMrev = new HashMap<Integer,ArrayList<String>>();
//HashMap Reversal
for (String f : aHM.keySet()){
if(aHMrev.get(aHM.get(f)) != null){
ArrayList<String> al = aHMrev.get(aHM.get(f));
al.add(f);
aHMrev.put(aHM.get(f), al);
}
else{
ArrayList<String> al = new ArrayList<String>();
al.add(f);
aHMrev.put(aHM.get(f), al);
}
}
Example
aHM = [LMWEB1B-VMH.log=Group1,PLMWEB1E-VMH.csv=Group3,
LMWEB1A-VMH.log=Group1,
LMWEB1E-VMH.log=Group2,
PLMWEB1D-VMH.csv=Group3,
LMWEB1F-VMH.log=Group2,
PLMWEB1C-VMH.csv=Group3,
PLMWEB1A-VMH.csv=Group2,
PLMWEB1B-VMH.csv=Group3,
PLMDB1-VMH.csv=Group2,
LMWEB1D-VMH.log=Group1,
PLMWEB1F-VMH.csv=Group3,
LMWEB1C-VMH.log=Group1]
aHMrev = {
Group1=[LMWEB1B-VMH.log, LMWEB1A-VMH.log, LMWEB1D-VMH.log,LMWEB1C-VMH.log],
Group2=[LMWEB1E-VMH.log, LMWEB1F-VMH.log,PLMWEB1A-VMH.csv,PLMDB1-VMH.csv],
Group3=[D:\Trash\Logs\Perfmon\PLMWEB1E-VMH.csv,PLMWEB1D-VMH.csv,PLMWEB1C-VMH.csv,PLMWEB1B-VMH.csv,PLMWEB1F-VMH.csv]}
You just create HaspMap like below:
Map<Integer, List<String>> map1 = new HashMap<>();
ArrayList<String> al = new ArrayList<>();
al.add("B");
map1.put(1, al);
al = new ArrayList<>();
al.add("A");
al.add("C");
map1.put(2, al);