I have a nested HashMap in this form:
{key1=val1, key2=val2,
key3=[
{key4=val4, key5=val5},
{key6=val6, key7=val7}
]
}
I now want to flatten that map, so that all entries are on the same level:
{key1=val1, key2=val2, key4=val4, key5=val5,key6=val6, key7=val7}
When I try
map.values().forEach(map.get("key3")::addAll);
as described in this post, I get the following error:
invalid method reference
cannot find symbol
symbol: method addAll(T)
location: class Object
where T is a type-variable:
T extends Object declared in interface Iterable
Is there any generic way to flatten any given Map?
Not sure if I understood the question correctly, but something like this might work.
Haven't checked all the syntax yet, so there might be some mistake somewhere.
Stream<Map.Entry<String, String>> flatten(Map<String, Object> map) {
return map.entrySet()
.stream()
.flatMap(this::extractValue);
}
Stream<Map.Entry<String, String>> extractValue(Map.Entry<String, Object> entry) {
if (entry.getValue() instanceof String) {
return Stream.of(new AbstractMap.SimpleEntry(entry.getKey(), (String) entry.getValue()));
} else if (entry.getValue() instanceof Map) {
return flatten((Map<String, Object>) entry.getValue());
}
}
Then you could do:
Map<String, String> flattenedMap = flatten(yourmap)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
You can make use of a recursive helper method:
static void forEachValue(Map<String, Object> source, BiConsumer<? super String, ? super Object> action) {
for (final Map.Entry<String, Object> entry : source.entrySet()) {
if (entry.getValue() instanceof Map) {
forEachValue((Map<String, Object>) entry.getValue(), action);
} else {
action.accept(entry.getKey(), entry.getValue());
}
}
}
Which then can be called like this:
Map<String, Object> map = ...;
Map<String, Object> flattened = new HashMap<>();
forEachValue(map, map::put);
I've used this approach with the BiConsumer to not limit the method to only flatten the nested map into another map, but the caller may decide himself what he wants to do with every key-value pair.
You should try this:
Map<String, Object> flatenedMap = new HashMap<>();
map.forEach((key, value) -> {
if(value instanceof Map) {
flatenedMap.putAll((Map) value);
} else {
flatenedMap.put(key, value);
}
});
If you have more than one level of nesting you can use recursive alg.
static Map<String, Object> flatMap(Map<String, Object> map) {
Map<String, Object> flatenedMap = new HashMap<>();
map.forEach((key, value) -> {
if(value instanceof Map) {
flatenedMap.putAll(flatMap((Map) value));
} else {
flatenedMap.put(key, value);
}
});
return flatenedMap;
}
Related
I'm using the following code to create a map where the key is "String" and value are of type "SomeClass". How do I pass SomeClass as an argument so that I can reuse the function with multiple classes?
public Map<String, SomeClass> getMap(String mappingFilePath) throws IOException {
Resource mappingResource = resourceLoader.getResource(mappingFilePath);
return objectMapper.readValue(
mappingResource.getInputStream(), new TypeReference<Map<String, SomeClass>>() {});
}
For eg:
Map<String, Integer> tempMap = getMap(someFilePath, Integer)
// or
Map<String, SomeClass> tempMap = getMap(someFilePath, SomeClass)
Additional question:
Can we pass Map as the argument? So that in some cases, we can make it LinkedHashMap if needed.
Map<String, Integer> tempMap = getMap(someFilePath, Map<String, Integer>)
// or
Map<String, SomeClass> tempMap = getMap(someFilePath, LinkedHashMap<String, SomeClass>)
TypeReference is already a generic class, so you can taking it as second argument and return that type of object
public <T> T getMap(String mappingFilePath, TypeReference<T> typeReference) throws IOException {
Resource mappingResource = resourceLoader.getResource(mappingFilePath);
return objectMapper.readValue(
mappingResource.getInputStream(), typeReference);
}
Then you can use it for any type
Map<String, Integer> tempMap = getMap(someFilePath, new TypeReference<Map<String, Integer>>() {})
// or
Map<String, SomeClass> tempMap = getMap(someFilePath, new TypeReference<Map<String, SomeClass>>() {})
I have a unit test that needs to check for a nested map value. I can get my assertion to work by pulling out the entry and matching the underlying Map, but I was looking for a clear way to show what the assertion is doing. Here is a very simplified test:
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasEntry;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
public class MapContainsMapTest {
#Test
public void testMapHasMap() {
Map<String, Object> outerMap = new HashMap<String, Object>();
Map<String, Object> nestedMap = new HashMap<String, Object>();
nestedMap.put("foo", "bar");
outerMap.put("nested", nestedMap);
// works but murky
assertThat((Map<String, Object>) outerMap.get("nested"), hasEntry("foo", "bar"));
// fails but clear
assertThat(outerMap, hasEntry("nested", hasEntry("foo", "bar")));
}
}
It seems the problem is the outer map is being compared using hasEntry(K key, V value) while what I want to use is hasEntry(Matcher<? super K> keyMatcher, Matcher<? super V> valueMatcher). I am not sure how to coerce the assertion to use the second form.
Thanks in advance.
If you only want to put Map<String, Object> as values in your outerMap adjust the declaration accordingly. Then you can do
#Test
public void testMapHasMap() {
Map<String, Map<String, Object>> outerMap = new HashMap<>();
Map<String, Object> nestedMap = new HashMap<String, Object>();
nestedMap.put("foo", "bar");
outerMap.put("nested", nestedMap);
Object value = "bar";
assertThat(outerMap, hasEntry(equalTo("nested"), hasEntry("foo", value)));
}
Object value = "bar"; is necessary for compile reasons. Alternatively you could use
assertThat(outerMap,
hasEntry(equalTo("nested"), Matchers.<String, Object> hasEntry("foo", "bar")));
If You declare outerMap as Map<String, Map<String, Object>> you don't need the ugly cast. Like this:
public class MapContainsMapTest {
#Test
public void testMapHasMap() {
Map<String, Map<String, Object>> outerMap = new HashMap<>();
Map<String, Object> nestedMap = new HashMap<>();
nestedMap.put("foo", "bar");
outerMap.put("nested", nestedMap);
assertThat(outerMap.get("nested"), hasEntry("foo", "bar"));
}
}
I would probably extend a new Matcher for that, something like that (beware, NPEs lurking):
class SubMapMatcher extends BaseMatcher<Map<?,?>> {
private Object key;
private Object subMapKey;
private Object subMapValue;
public SubMapMatcher(Object key, Object subMapKey, Object subMapValue) {
super();
this.key = key;
this.subMapKey = subMapKey;
this.subMapValue = subMapValue;
}
#Override
public boolean matches(Object item) {
Map<?,?> map = (Map<?,?>)item;
if (!map.containsKey(key)) {
return false;
}
Object o = map.get(key);
if (!(o instanceof Map<?,?>)) {
return false;
}
Map<?,?> subMap = (Map<?,?>)o;
return subMap.containsKey(subMapKey) && subMap.get(subMapKey).equals(subMapValue);
}
#Override
public void describeTo(Description description) {
description.appendText(String.format("contains %s -> %s : %s", key, subMapKey, subMapValue));
}
public static SubMapMatcher containsSubMapWithKeyValue(String key, String subMapKey, String subMapValue) {
return new SubMapMatcher(key, subMapKey, subMapValue);
}
}
Try like this :
assertThat(nestedMap).contains(Map.entry("foo", "bar"));
assertThat(outerMap).contains(Map.entry("nested", nestedMap));
If I have a class
public class Op {
public Map<String, String> ops;
}
How can I make it executable in forEach loop?
for (String key : op) {
System.out.println(op.ops.get(key))
}
UPD
Here is my solution
Op op = new Op(new HashMap<String, String>() {{
put("a", "1");
put("b", "2");
put("c", "3");
}});
for (String key : op) System.out.println(op.map.get(key));
class Op implements Iterable<String> {
Map<String, String> map;
public Op(Map<String, String> map) {
this.map = map;
}
public Iterator<String> iterator() {
return map.keySet().iterator();
}
}
But I'm not sure about verbosity. Is it too verbose? Maybe there is a much concise way to implement it?
You are interested in looping over a SetEntry list.
for (Map.Entry<String, String > entry : ops.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
// ...
}
Propable duplicate of: Iterate through a HashMap
In this exact case, use #entrySet, #keySet or #values as specified in the API docs.
In a general case, an object is iterable using the enhanced for loop, if it is implementing the interface Iterable. The most of the standard java collections are implementing this interface.
public class Op implements Iterable<Map.Entry<String,String>> {
public Map<String, String> ops;
#Override
public Iterator<Map.Entry<String,String>> iterator() {
return ops.entrySet().iterator();
}
}
Example:
for (Map.Entry<String, String> n : testOp){
System.out.println("Key: " + n.getKey() + " Value: " + n.getValue());
}
Im trying to make my removeItemFromMapByValue method work together with removeItemFromMapByValue, but when i start to compile my code i get that ConcurrentModificationException. removeItemFromMapByValue have to remove the same names in values.
public class Solution
{
public static HashMap<String, String> createMap()
{
HashMap<String, String> map = new HashMap<String, String>();
map.put("Stallone", "Silvest");
map.put("Morikone", "Enio");
map.put("Vivaldi","Antonio");
map.put("Belucci", "Monica");
map.put("Gudini", "Harry");
map.put("Verdo", "Dhuzeppe");
map.put("Maracci", "Bruno");
map.put("Carleone", "Vito");
map.put("Bracco", "Luka");
map.put("Stradivari", "Antonio");
return map;
}
public static void removeTheFirstNameDuplicates(HashMap<String, String> map)
{
for (Map.Entry<String, String> pair : map.entrySet()){
String name = pair.getValue();
removeItemFromMapByValue(map, name);
}
}
public static void removeItemFromMapByValue(HashMap<String, String> map, String value)
{
HashMap<String, String> copy = new HashMap<String, String>(map);
for (Map.Entry<String, String> pair: copy.entrySet())
{
if (pair.getValue().equals(value))
map.remove(pair.getKey());
}
}
public static void main(String[] args)
{
HashMap<String, String> map = createMap();
removeTheFirstNameDuplicates(map);
System.out.println(map);
}
}
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:926)
at java.util.HashMap$EntryIterator.next(HashMap.java:966)
at java.util.HashMap$EntryIterator.next(HashMap.java:964)
at com.javarush.test.level08.lesson08.task05.Solution.removeTheFirstNameDuplicates(Solution.java:32)
at com.javarush.test.level08.lesson08.task05.Solution.main(Solution.java:52)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Process finished with exit code 1
The simple solution is to fix the first method, as this will avoid the error and behave correctly.
// remove any duplicated values, leaving one entry.
public static void removeTheFirstNameDuplicates(HashMap<String, String> map) {
Map<K,V> map2 = invert(invert(map));
map.clear();
map.putAll(map2);
}
public static <K, V> Map<V, K> invert(Map<K, V> map) {
Map<V, K> map2 = new HashMap<>();
for(Map.Entry<K< V> entry: map.entrySet())
map2.put(entry.getValue(), entry.getKey());
return map2;
}
Your compiler will not produce a ConcurrentModifcationException. You should look at the line in in the stack track to see where you are modifying the collection while iterating over it e.g.
for (Map.Entry<String, String> pair: copy.entrySet())
{
if (pair.getValue().equals(value))
map.remove(pair.getKey());
}
In this case you are removing an entry while iterating. A simple solution is to use the Iterator directly. Usually you IDE can do this refactoring.
for (Iterator<String> iter = copy.values().iterator(); iter.hasNext();) {
if (iter.next().equals(value))
iter.remove();
}
The problem with this solution is this is called from a nested and it will remove ALL matching entries as it does in your code. i.e. it will remove all the entries.
Here:
for (Map.Entry<String, String> pair : map.entrySet()){
String name = pair.getValue();
removeItemFromMapByValue(map, name); //you are about to delete a map item here!
}
You are modifying the Set that you are currently looping through ==> ConcurrentModificationException
I have a HashMap in Java, the contents of which (as you all probably know) can be accessed by
HashMap.get("keyname");
If a have a HashMap inside another HashMap i.e. a nested HashMap, how would i access the contents? Can i do this like this, inline:
HashMap.get("keyname").get("nestedkeyname");
Thank you.
You can do it like you assumed. But your HashMap has to be templated:
Map<String, Map<String, String>> map =
new HashMap<String, Map<String, String>>();
Otherwise you have to do a cast to Map after you retrieve the second map from the first.
Map map = new HashMap();
((Map)map.get( "keyname" )).get( "nestedkeyname" );
You can get the nested value by repeating .get(), but with deeply nested maps you have to do a lot of casting into Map. An easier way is to use a generic method for getting a nested value.
Implementation
public static <T> T getNestedValue(Map map, String... keys) {
Object value = map;
for (String key : keys) {
value = ((Map) value).get(key);
}
return (T) value;
}
Usage
// Map contents with string and even a list:
{
"data": {
"vehicles": {
"list": [
{
"registration": {
"owner": {
"id": "3643619"
}
}
}
]
}
}
}
List<Map> list = getNestedValue(mapContents, "data", "vehicles", "list");
Map first = list.get(0);
String id = getNestedValue(first, "registration", "owner", "id");
Yes.
See:
public static void main(String args[]) {
HashMap<String, HashMap<String, Object>> map = new HashMap<String, HashMap<String,Object>>();
map.put("key", new HashMap<String, Object>());
map.get("key").put("key2", "val2");
System.out.println(map.get("key").get("key2"));
}
If you plan on constructing HashMaps with variable depth, use a recursive data structure.
Below is an implementation providing a sample interface:
class NestedMap<K, V> {
private final HashMap<K, NestedMap> child;
private V value;
public NestedMap() {
child = new HashMap<>();
value = null;
}
public boolean hasChild(K k) {
return this.child.containsKey(k);
}
public NestedMap<K, V> getChild(K k) {
return this.child.get(k);
}
public void makeChild(K k) {
this.child.put(k, new NestedMap());
}
public V getValue() {
return value;
}
public void setValue(V v) {
value = v;
}
}
and example usage:
class NestedMapIllustration {
public static void main(String[] args) {
NestedMap<Character, String> m = new NestedMap<>();
m.makeChild('f');
m.getChild('f').makeChild('o');
m.getChild('f').getChild('o').makeChild('o');
m.getChild('f').getChild('o').getChild('o').setValue("bar");
System.out.println(
"nested element at 'f' -> 'o' -> 'o' is " +
m.getChild('f').getChild('o').getChild('o').getValue());
}
}
As others have said you can do this but you should define the map with generics like so:
Map<String, Map<String, String>> map = new HashMap<String, Map<String,String>>();
However, if you just blindly run the following:
map.get("keyname").get("nestedkeyname");
you will get a null pointer exception whenever keyname is not in the map and your program will crash. You really should add the following check:
String valueFromMap = null;
if(map.containsKey("keyname")){
valueFromMap = map.get("keyname").get("nestedkeyname");
}
Yes, if you use the proper generic type signature for the outer hashmap.
HashMap<String, HashMap<String, Foo>> hm = new HashMap<String, HashMap<String, Foobar>>();
// populate the map
hm.get("keyname").get("nestedkeyname");
If you're not using generics, you'd have to do a cast to convert the object retrieved from the outer hash map to a HashMap (or at least a Map) before you could call its get() method. But you should be using generics ;-)
I prefer creating a custom map that extends HashMap. Then just override get() to add extra logic so that if the map doesnt contain your key. It will a create a new instance of the nested map, add it, then return it.
public class KMap<K, V> extends HashMap<K, V> {
public KMap() {
super();
}
#Override
public V get(Object key) {
if (this.containsKey(key)) {
return super.get(key);
} else {
Map<K, V> value = new KMap<K, V>();
super.put((K)key, (V)value);
return (V)value;
}
}
}
Now you can use it like so:
Map<Integer, Map<Integer, Map<String, Object>>> nestedMap = new KMap<Integer, Map<Integer, Map<String, Object>>>();
Map<String, Object> map = (Map<String, Object>) nestedMap.get(1).get(2);
Object obj= new Object();
map.put(someKey, obj);
I came to this StackOverflow page looking for a something ala valueForKeyPath known from objc. I also came by another post - "Key-Value Coding" for Java, but ended up writing my own.
I'm still looking for at better solution than PropertyUtils.getProperty in apache's beanutils library.
Usage
Map<String, Object> json = ...
public String getOptionalFirstName() {
return MyCode.getString(json, "contact", "firstName");
}
Implementation
public static String getString(Object object, String key0, String key1) {
if (key0 == null) {
return null;
}
if (key1 == null) {
return null;
}
if (object instanceof Map == false) {
return null;
}
#SuppressWarnings("unchecked")
Map<Object, Object> map = (Map<Object, Object>)object;
Object object1 = map.get(key0);
if (object1 instanceof Map == false) {
return null;
}
#SuppressWarnings("unchecked")
Map<Object, Object> map1 = (Map<Object, Object>)object1;
Object valueObject = map1.get(key1);
if (valueObject instanceof String == false) {
return null;
}
return (String)valueObject;
}
import java.util.*;
public class MyFirstJava {
public static void main(String[] args)
{
Animal dog = new Animal();
dog.Info("Dog","Breezi","Lab","Chicken liver");
dog.Getname();
Animal dog2= new Animal();
dog2.Info("Dog", "pumpkin", "POM", "Pedigree");
dog2.Getname();
HashMap<String, HashMap<String, Object>> dogs = new HashMap<>();
dogs.put("dog1", new HashMap<>() {{put("Name",dog.name);
put("Food",dog.food);put("Age",3);}});
dogs.put("dog2", new HashMap<>() {{put("Name",dog2.name);
put("Food",dog2.food);put("Age",6);}});
//dogs.get("dog1");
System.out.print(dogs + "\n");
System.out.print(dogs.get("dog1").get("Age"));
}
}
Example Map:
{
"data": {
"userData": {
"location": {
"city": "Banja Luka"
}
}
}
}
Implementation:
public static Object getValueFromMap(final Map<String, Object> map, final String key) {
try {
final String[] tmpKeys = key.split("\\.");
Map<String, Object> currentMap = map;
for (int i = 0; i < tmpKeys.length - 1; i++) {
currentMap = (Map<String, Object>) currentMap.get(tmpKeys[i]);
}
return currentMap.get(tmpKeys[tmpKeys.length - 1]);
} catch (Exception exception) {
return null;
}
}
Usage:
final Map<String, Object> data = new HashMap<>();
final Map<String, Object> userData = new HashMap<>();
final Map<String, Object> location = new HashMap<>();
location.put("city", "Banja Luka");
userData.put("location", location);
data.put("userData", userData);
System.out.println(getValueFromMap(data, "userData.location.city"));
Result:
Banja Luka
Process finished with exit code 0
I hit this discussion while trying to figure out how to get a value from a nested map of unknown depth and it helped me come up with the following solution to my problem. It is overkill for the original question but maybe it will be helpful to someone that finds themselves in a situation where you have less knowledge about the map being searched.
private static Object pullNestedVal(
Map<Object, Object> vmap,
Object ... keys) {
if ((keys.length == 0) || (vmap.size() == 0)) {
return null;
} else if (keys.length == 1) {
return vmap.get(keys[0]);
}
Object stageObj = vmap.get(keys[0]);
if (stageObj instanceof Map) {
Map<Object, Object> smap = (Map<Object, Object>) stageObj;
Object[] skeys = Arrays.copyOfRange(keys, 1, keys.length);
return pullNestedVal(smap, skeys);
} else {
return null;
}
}