If I have a map with the following data:
"a", "hello"
"b", "bye"
"c", "good morning"
and a second map with the following data:
"key1","a"
"key2", "b"
"key3", "c"
is it then possible to perform an operation such that I can map the value of my second map onto the key as my first map? Which would result in the final map looking like this:
"key1","hello"
"key2", "bye"
"key3", "good morning"
Apparently you want a third map made of keys from the second map, and matching values from the first map.
Map< String , String > thirdMap = new HashMap<>() ;
for ( Map.Entry< String , String > entry : secondMap.entrySet() ) {
thirdMap.put(
entry.getKey() , // Second map’s key.
firstMap.get( entry.getValue() ) // First map’s value.
);
}
The below code should work, for what you are trying to do
public static void main(final String[] args) throws Exception {
HashMap<String, String> keyToValue = new HashMap<String, String>() {{
put("a", "hello");
put("b", "bye");
put("c", "good morning");
}};
HashMap<String, String> keyToSecondaryKey = new HashMap<String, String>() {{
put("key1", "a");
put("key2", "b");
put("key3", "c");
}};
keyToSecondaryKey.entrySet().forEach(e-> {
e.setValue(keyToValue.get(e.getValue()));
});
System.out.println(keyToSecondaryKey);
}
In this way:
Map<String, String> map1 = new HashMap<>();
map1.put("a", "hello");
map1.put("b", "bye");
map1.put("c", "good morning");
Map<String, String> map2 = new HashMap<>();
map2.put("key1", "a");
map2.put("key2", "b");
map2.put("key3", "c");
map2.forEach((key2, value2) -> map2.put(key2, map1.get(value2)));
This may work for you. Note that this creates a third map.
Map<String, String> map1 =
Map.of("a", "hello", "b", "bye", "c", "good morning");
Map<String, String> map2 =
Map.of("key1", "a", "key2", "b", "key3", "c");
Stream the entry set of map2
Use that entry's key as the key to the result map
use that entry's value as the key to retrieve the value of map1
Map<String, String> result = map2.entrySet().stream()
.collect(Collectors.toMap(Entry::getKey,
e->map1.get(e.getValue())));
result.entrySet().forEach(System.out::println);
prints
key1=hello
key2=bye
key3=good morning
As I've mentioned in the comment the statement below will print "hello". And there's no need to do create a third map if you want to access the content of these two maps right in the same code.
System.out.println(fistMap.get(secondMap.get("key1")));
With the following code, you can iterate print the keys of the second map and the values of the first map (with the assumption that all values of the second map are present in the first map as keys).
fistMap.forEach((k, v) -> System.out.println(k + " -> " + secondMap.get(v)));
And only if you need to pass the data from these two maps to another part of your application it might make sense to create the third combined map. It could be done with the Stream IPA like that:
Map<String, String> combinedMap =
secondMap.keySet().stream()
.collect(Collectors.toMap(Function.identity(), // key of the new map
k -> fistMap.get(secondMap.get(k)))); // value of the new map
Related
HashMap<String, String> map = new HashMap<String, String>();
HashMap<String, String> newMap = new HashMap<String, String>();
map.put("A","1");
map.put("B","2");
map.put("C","2");
map.put("D","1");
Expected Output: "AD", "1" and "BC", "2" present inside the newMap which means, if the data values were same it needs combine its keys to have only one data value by combining its keys inside the newMap created how to achieve this in Java?
You want to group by the "integer" value using Collectors.groupingBy and collect the former keys as a new value. By default, grouping yields in List. You can further use downstream collector Collectors.mapping and another downstream collector Collectors.reducing to map and concatenate the individual items (values) as a single String.
Map<String, String> groupedMap = map.entrySet().stream()
.collect(Collectors.groupingBy(
Map.Entry::getValue,
Collectors.mapping(
Map.Entry::getKey,
Collectors.reducing("", (l, r) -> l + r))));
{1=AD, 2=BC}
Now, you can switch keys with values for the final result, though I really think you finally need what is already in the groupedMap as further processing might cause an error on duplicated keys:
Map<String, String> newMap = groupedMap.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getValue,
Map.Entry::getKey));
{BC=2, AD=1}
It is possible, put it all together using Collectors.collectingAndThen (matter of taste):
Map<String, String> newMap = map.entrySet().stream()
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(
Map.Entry::getValue,
Collectors.mapping(
Map.Entry::getKey,
Collectors.reducing("", (l, r) -> l + r))),
m -> m.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getValue,
Map.Entry::getKey))));
Based on logic:
Loop through your map
For each value, get the corresponding key from the new map (based on the value)
If the new map key exists, remove it and put it again with the extra letter at the end
If not exists, just put it without any concatenation.
for (var entry : map.entrySet())
{
String newMapKey = getKey(newMap, entry.getValue());
if (newMapKey != null)
{
newMap.remove(newMapKey);
newMap.put(newMapKey + entry.getKey(), entry.getValue());
continue;
}
newMap.put(entry.getKey(), entry.getValue());
}
The extra method:
private static String getKey(HashMap<String, String> map, String value)
{
for (String key : map.keySet())
if (value.equals(map.get(key)))
return key;
return null;
}
{BC=2, AD=1}
Using Java 8
You can try the below approach in order to get the desired result.
Code:
public class Test {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
Map<String, String> newMap;
map.put("A","1");
map.put("B","2");
map.put("C","2");
map.put("D","1");
Map<String, String> tempMap = map.entrySet().stream()
.collect(Collectors.groupingBy(Map.Entry::getValue,
Collectors.mapping(Map.Entry::getKey,Collectors.joining(""))));
newMap = tempMap.entrySet().stream().sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey,(a,b) -> a, LinkedHashMap::new));
System.out.println(newMap);
}
}
Output:
{AD=1, BC=2}
If you want the keys of the source map to be concatenated in alphabetical order like in your example "AD", "BC" (and not "DA" or "CB"), then you can ensure that by creating an intermediate map of type Map<String,List<String>> associating each distinct value in the source map with a List of keys. Then sort each list and generate a string from it.
That how it might be implemented:
Map<String, String> map = Map.of(
"A", "1", "B", "2","C", "2","D", "1"
);
Map<String, String> newMap = map.entrySet().stream()
.collect(Collectors.groupingBy( // intermediate Map<String, List<String>>
Map.Entry::getValue,
Collectors.mapping(Map.Entry::getKey, Collectors.toList())
))
.entrySet().stream()
.collect(Collectors.toMap(
e -> e.getValue().stream().sorted().collect(Collectors.joining()),
Map.Entry::getKey
));
newMap.forEach((k, v) -> System.out.println(k + " -> " + v));
Output:
BC -> 2
AD -> 1
I need to initialize the keys of a map from a list and give a default value for each entry.
Currently I use a for loop :
Map<String, String > myMap = new HashMap<>();
List<String> keys = Arrays.asList("a", "b", "c", "d");
for (String key : keys){
myMap.put(key, "default");
}
Is there a cleaner way to do that ? Stream or lambda maybe ?
Yes, you can create a Stream of that List's elements and collect them to a Map:
Map<String,String> map = keys.stream ()
.collect (Collectors.toMap (Function.identity (),
k -> "default"));
I am trying to check whether a map contains all contents of another map. For example, I have a mapA which is a Map<String, List<String>> and the elements are:
"1" -> ["a","b"]
"2" -> ["c","d"]
another mapB which is also a Map<String, List<String>>, the elements are:
"1" -> ["a"]
"2" -> ["c","d"],
I want to create a function compare(mapA, mapB) which will return false in this case.
What is the best way to do this?
Inside your compare(mapA, mapB) method, you can simply use:
return mapA.entrySet().containsAll(mapB.entrySet());
The answer provided by #Jacob G wont work in your case. It will work only if there is an extra (key, value) pair in MapA. like
MapA = {"1" -> ["a","b"] "2" -> ["c","d"] }
and
MapB = {"1" -> ["a","b"] }.
What you need is this:
boolean isStrictlyDominate(LinkedHashMap<Integer, HashSet<Integer>> firstMap, LinkedHashMap<Integer, HashSet<Integer>> secondMap){
for (Map.Entry<Integer, HashSet<Integer>> item : secondMap.entrySet()) {
int secondMapKey = item.getKey();
if(firstMap.containsKey(secondMapKey)) {
HashSet<Integer> secondMapValue = item.getValue();
HashSet<Integer> firstMapValue = firstMap.get(secondMapKey) ;
if(!firstMapValue.containsAll(secondMapValue)) {
return false;
}
}
}
return !firstMap.equals(secondMap);
}
(if you do not want to check strict domination then just return true at last return statement)
Try this code :
Assert.assertTrue(currentMap.entrySet().containsAll(expectedMap.entrySet()));
you can try this.
static boolean compare(Map<String, List<String>> mapA, Map<String, List<String>> mapB){
return mapA.entrySet().containsAll(mapB.entrySet());
}
As suppose, provided data is something like this:
Map<String, List<String>> mapA = new HashMap<>();
Map<String, List<String>> mapB = new HashMap<>();
mapA.put("1", Arrays.asList("a","b"));
mapA.put("2", Arrays.asList("c","d"));
mapB.put("1", Arrays.asList("a"));
mapB.put("2", Arrays.asList("c", "d"));
System.out.println(compare(mapA, mapB));
In this case compare(mapA, mapB) method will return false.
But suppose provided data is something like this:
Map<String, List<String>> mapA = new HashMap<>();
Map<String, List<String>> mapB = new HashMap<>();
mapA.put("1", Arrays.asList("a","b"));
mapA.put("2", Arrays.asList("c","d"));
mapB.put("1", Arrays.asList("a", "b"));
mapB.put("2", Arrays.asList("c", "d"));
System.out.println(compare(mapA, mapB));
In this case, compare(mapA, mapB) method, which I have written will return true.
compare(mapA, mapB) method basically checking for all the entries in mapA with mapB, if same returning yes, else returning false;
Map<String, String> map1 = new HashMap<>();
map1.put("k1", "v1");
map1.put("k2", "v2");
map1.put("k3", "v3");
Map<String, String> map2 = new HashMap<>();
map2.put("v1", "val1");
map2.put("v2", "val2");
map2.put("v3", "vav3");
I want to update values of map1 so that it has entries:
"k1" , "val1",
"k2" , "val2",
"k3" , "val3"
My solution:
for (Map.Entry<String, String> entry : map1.entrySet()) {
map1.put(entry.getKey(), map2.get(entry.getValue()));
}
Is there any better way to do this?
Edit: I am using Java 7 but curious to know if there any better way in Java 8.
Starting with Java 8, you can just have
map1.replaceAll((k, v) -> map2.get(v));
replaceAll(function) will replace all values from the map map1 with the result of applying the given function. In this case, the function simply retrieves the value from map2.
Note that this solution has the same issues that your initial code: if map2 doesn't have a corresponding mapping, null will be returned. You may want to call getOrDefault to have a default value in that case.
public static void main(String[] args) {
Map<String, String> map1 = new HashMap<>();
map1.put("k1", "v1");
map1.put("k2", "v2");
map1.put("k3", "v3");
Map<String, String> map2 = new HashMap<>();
map2.put("v1", "val1");
map2.put("v2", "val2");
map2.put("v3", "val3");
map1.replaceAll((k, v) -> map2.get(v));
System.out.println(map1); // prints "{k1=val1, k2=val2, k3=val3}"
}
For Java 7 there is nothing more that you can do, you are already doing it in the best way possible.
I'm adding this answer as a reference to show that for such case using Lambda Expressions in Java 8 will be even worst. See this example:
public static void main(String[] args) {
Map<String, String> map1 = new HashMap<>();
final Map<String, String> map2 = new HashMap<>();
for ( int i=0; i<100000; i++ ){
map1.put("k"+i, "v"+i);
map2.put("v"+i, "val"+i);
}
long time;
long prev_time = System.currentTimeMillis();
for (Map.Entry<String, String> entry : map1.entrySet()) {
map1.put(entry.getKey(), map2.get(entry.getValue()));
}
time = System.currentTimeMillis() - prev_time;
System.out.println("Time after for loop " + time);
map1 = new HashMap<>();
for ( int i=0; i<100000; i++ ){
map1.put("k"+i, "v"+i);
}
prev_time = System.currentTimeMillis();
map1.replaceAll((k, v) -> map2.get(v));
time = System.currentTimeMillis() - prev_time;
System.out.println("Time after for loop " + time);
}
The output for this will be:
Time after for loop 40
Time after for loop 100
The second loop is variable but always bigger than the first one.
I'm not Lambda specialist but I guess that there are more to be processed with it than a plain "foreach" of the first scenario
Running this test case over and over you will get for lambda almost always twice the time of the first "foreach" case.
in Java 8 you can write:
map1.entrySet()
.stream()
.map(entry -> new SimpleEntry(entry.getKey(), map2.get(entry.getValue())))
.collect(Collectors.toMap(entry -> entry.getKey(), entry.getValue()));
Not the nicest thing, though, but still a non-mutating solution.
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)));
}