So I have this data
Key Value
--- -----
fruit apple
fruit banana
fruit grapes
cars honda
cars lexus
cars bmw
schools harvard
schools yale
...
And I would like to construct the data to a Map<String, Collections<String>> and make a method that adds all the data. So far, I have a constructor that instantiates my Map variable
public Map<String, Collection<String>> keys;
public newMap() {
keys = new HashMap<>();
}
public void addkeys(String K, String V) {
Collection<String> names = new HashSet<String>();
if (keys.containsKey(K) && !keys.values().contains(V)) {
names.add(V);
courses.put(K, names);
} else if (!keys.containsKey(K) && !keys.values().contains(V)) {
names.add(V);
courses.put(student, classes);
}
}
So when I run my test
newMap.addkeys("fruit", "apple");
newMap.addkeys("fruit, "banana");
newMap.addkeys("fruit", "grapes");
newMap.addkeys("cars, "honda");
newMap.addkeys("cars", "lexus");
newMap.addkeys("cars, "bmw");
newMap.addkeys("schools", "harvard");
newMap.addkeys("schools, "yale");
it should return
fruit = [apple, banana, grapes]
cars = [honda, lexus, bmw]
schools = [hardvard, yale]
but instead I get
fruit = [grapes]
cars = [bmw]
schools = [yale]
it seems like it's only adding the last instance because whenever I call Collection<String> names = new HashSet<String>(), i'm re-instantiating names but when I this instantiation to the begininng of the class, It just adds on everything. So it returns fruit = [apple, banana, grapes, honda, lexus, bmw, hardvard, yale].
Pre-Java 8:
Use the fact that Map#get returns null when there is not a mapping in order to determine if you need to put a new collection in the map, then add the value to the collection.
public void addkeys(String K, String V) {
Collection<String> values = keys.get(K);
if (values == null) {
values = new HashSet<>();
keys.put(K, values);
}
values.add(V);
}
Java 8+:
Use Map#computeIfAbsent to add a new collection to the map if there is not a mapping, and then add the value to the returned collection.
public void addkeys(String K, String V) {
keys.computeIfAbsent(K, k -> new HashSet<>()).add(V);
}
You can rewrite your function like this:
public void addkeys(String K, String V) {
if (keys.containsKey(K)) {
keys.get(K).add(V); // add to existing hashset
} else {
Collection<String> names = new HashSet<String>();
names.add(V);
keys.put(K, names); // add new hashset for key
}
}
Try this
public Map<String, Set<String>> myMap;
public newMap() {
myMap = new HashMap<>();
}
public void putInMap(String key, String val) {
if (myMap.containsKey(key)) {
myMap.get(K).add(V);
} else {
Set<String> values = new HashSet<String>();
values.add(val);
myMap.put(key, values);
}
}
Related
The question is how to pritn the books that have value = 1 in the HashMap, using the printWhiteRavens(ArrayList<String> whiteRavens) method ? In the code ive deleted the array with the books so the code could be shorter.
public static HashMap<String, Integer> createBooksCounter() {
HashMap<String,Integer> createBooksCounter = new HashMap<>();
createBooksCounter.put("Chicken Abroad",5);
createBooksCounter.put("Lord With Sins",3);
createBooksCounter.put("Fish And Horses",4);
createBooksCounter.put("Mistress Of Devotion",2);
createBooksCounter.put("Guardians And Gangsters",3);
createBooksCounter.put("Bandit Of The Void",1);
createBooksCounter.put("Lions Of Dread",1);
createBooksCounter.put("Future Of Fire",2);
createBooksCounter.put("Driving Into The Depths",1);
createBooksCounter.put("Starting The Demons",1);
createBooksCounter.put("Maid With Blue Eyes",2);
createBooksCounter.put("Lovers In The Forest",1);
createBooksCounter.put("Destruction Of The Faceless Ones",1);
return null;
}
public static void countBook(HashMap<String, Integer> booksCounter, String book) {
}
public static ArrayList<String> findWhiteRavens(HashMap<String, Integer> booksCounter) {
return null;
}
public static void printWhiteRavens(ArrayList<String> whiteRavens) {
return null;
}
public static void main(String[] args) {
}
Using Stream :
yourMap.entrySet().stream().filter(e -> e.getValue() == 1)
.forEach(element -> System.out.println(element.getKey()));
Your method createBooksCounter should probably return the map instead of null. Note that usually you declare members as the highest common interface offering the contract you need. So even though you might instantiate HashMap, you declare the field as a Map. The same holds for method parameters and return types. Regarding the name, I would name it bookCounts, since the map is not counting anything, just representing books and their 'counts'.
public static Map<String, Integer> createBookCounts() {
Map<String, Integer> bookCounts = new HashMap<>();
bookCounts .put("Chicken Abroad",5);
// ...
return bookCounts;
}
The method findWhiteRavens needs to look up all books in the map which have count==1. This is contrary to the normal usage of a Map, where you look things up by their key rather than their value. I suggest either to use a map where count is the key, and the values are lists of books (having that count) OR to use a List with Book objects with the fields title and count. The latter approach is in my opinion the simplest and most object-oriented design (using maps to collect related fields is a code smell):
class Book {
String title;
int count;
Book(String title, int count) {
this.title = title;
this.count = count;
}
// accessors to be added
}
// ...
List<Book> books = new ArrayList<>();
books.add(new Book("Chicken Abroad", 5));
You could print books with count==1 in one go like this:
books.stream()
.filter(book -> book.getCount() == 1)
.map(book -> book.getTitle());
forEach(book -> System.out.println(book));
If you prefer to use a map, then this would make more sense:
private static Map<Integer, List<String>> bookTitlesByCount = new HashMap<>();
public static void main(String[] args) {
addBookTitle("Chicken Abroad", 5);
List<String> whiteRavens = bookTitlesByCount.get(1);
// print them
}
private static void addBookTitle(String title, int count) {
List<String> bookTitles = bookTitlesByCount.get(count);
if (bookTitles == null) {
bookTitles = new ArrayList<>();
bookTitlesByCount.put(count, bookTitles);
}
bookTitles.add(title);
}
Filter all entries of the map, create a list from the map's keys, these are your book titles. Pass that list to printWhiteRavens which then prints the titles:
public static void main(String[] args) {
Map<String,Integer> map = createBooksCounter();
List<String> list = map.entrySet().stream().
filter(e->e.getValue()==1).
map(e->e.getKey()).
collect(Collectors.toList());
printWhiteRavens(list);
}
public static void printWhiteRavens(List<String> whiteRavens) {
whiteRavens.stream().forEach(System.out::println);
}
Also you have to edit your createBooksCounter method to return the map using
return createBooksCounter;
I have two similar methods. One of them prints something and one of them save somethings. As you can see there are a lot of duplicate code. How should I refactor it and remove this duplication ?
public static void printSomething(List<String> list) {
for (String item : list) {
if (item.contains("aaa")) {
System.out.println("aaa" + item);
}
if (item.contains("bbb")) {
System.out.println("bbb" + item);
} else {
System.out.println(item);
}
}
}
public static Map<String, String> getSomething(List<String> list) {
Map<String, String> map = new HashMap<String, String>();
for (String item : list) {
if (item.contains("aaa")) {
map.put("aaa", item);
}
if (item.contains("bbb")) {
map.put("bbb", item);
} else {
//do nothing
}
}
return map;
}
UPDATE:
Code was updated to solve problem when method are not exactly similar
Assuming the order of which the println of "aaa" and "bbb" appear does not matter, you could replace the implementation of printSomething with
public static void printSomething(List<String> list) {
Map<String, String> map = getSomething(list);
for(Map.Entry<String, String> entry : map) {
System.out.println(entry.getKey() + entry.getValue());
}
}
A generic Interface Action that have a method action(T t) can reduce the code.
public interface Action<E> {
void action(E e);
}
Example:
public static void forEach(List<String> list, Action <String> action) {
for(String s : list){
action.action(s);
}
Now you just need 2 different implementations of Action.
You can use annonymous types if you don't want to create a class.
If you know c# this is similar to lambdas.
edit:
Using annonymous type:
public static Map<String, String> getSomething(List<String> list) {
final Map<String, String> map = new HashMap<String, String>();
forEach(list, new Action<String>() {
#Override
public void action(String e) {
if (e.contains("aaa")) {
map.put("aaa", e);
}
if (e.contains("bbb")) {
map.put("bbb", e);
} else {
// do nothing
}
}
});
return map;
}
Creating the class:
public static Map<String, String> getSomething2(List<String> list) {
final Map<String, String> map = new HashMap<String, String>();
forEach(list, new ListToMapAction(map));
return map;
}
public class ListToMapAction implements Action<String> {
Map<String, String> map;
public ListToMapAction(Map<String, String> map) {
this.map = map;
}
#Override
public void action(String e) {
if (e.contains("aaa")) {
map.put("aaa", e);
}
if (e.contains("bbb")) {
map.put("bbb", e);
} else {
// do nothing
}
}
}
In a programming language with first-class functions, you'd pass around a function as a parameter indicating what you want to do inside the loop (for an example see the update, below). Java is going to have lambdas in version 8, but they're not quite up to the job.
In the current state of Java, you'll have to settle with something uglier - for example, passing an extra parameter to the method; or you could pass around anonymous inner classes that implement an interface, but IMHO that's even uglier than what I'm about to suggest:
static void printSomething(List<String> list, boolean print)
If print is true then print inside the loop, otherwise add to the Map. Of course, you'll have to add a couple of ifs inside the loop for checking this condition, and at the beginning, one extra if to determine if the Map is to be initialized. Either way, the method returns a Map, but the Map can be null for the printing case. This is what I mean:
static Map<String, String> processSomething(List<String> list, boolean print) {
Map<String, String> map = null;
if (!print)
map = new HashMap<String, String>();
for (String item : list) {
if (item.contains("aaa")) {
if (print)
System.out.println("aaa" + item);
else
map.put("aaa", item);
}
if (item.contains("bbb")) {
if (print)
System.out.println("bbb" + item);
else
map.put("bbb", item);
} else if (print) {
System.out.println(item);
}
}
return map;
}
UPDATE
For example, in Python - which allows passing functions as parameters, this is how you'd solve the problem in an elegant fashion:
def processSomething(lst, func):
result = None
for item in lst:
if 'aaa' in item:
result = func(item, 'aaa', result)
elif 'bbb' in item:
result = func(item, 'bbb', result)
else:
result = func(item, '', result)
return result
def printer(item, key, result):
print key + item
def mapper(item, key, result):
if not result:
result = {}
if key:
result[key] = item
return result
See how it works:
processSomething(['aaa', 'bbb', 'ccc'], printer)
=> aaaaaa
bbbbbb
ccc
processSomething(['aaa', 'bbb', 'ccc'], mapper)
=> {'aaa': 'aaa', 'bbb': 'bbb'}
I wanted to create a table/list in Java, and I wonder what is the best way to handle it.
The table should have a structure like this:
Term propertyList entitiesList
a1 p1=1, p2=2, p3=2 T1,T2
a2 p5=0, p4=5 ,p3=3 T2,T1
a3 p1=1 ,p4=3, p3=9 T3,T1,T2
...
a10
I have a list with exactly 10 terms, and for every term there is a list of properties (deep with key and value), and the properties can be either in one or more entities.
I need some help on how to create it, e.g. should I use list, map, collection etc.
How can I add hardcoded values to them as literals in the code, and what is the best way to read data from it, taking into account performance, given that later I will need to use this for every entity and find the related properties that participate in every term.
first off Create Term class.
So you have list of Terms: List<Term>
Term class
public class Term {
private String mName = "";
private Map<String, Integer> mPropertyMap = new LinkedHashMap<String, Integer>();
private List<String> mEntitiesList = new ArrayList<String>();
public Term(String name) {
mName = name;
}
public void generate(Map<String, Integer> propertyMap, List<String> entitiesList) {
mPropertyMap = propertyMap;
mEntitiesList = entitiesList;
}
public Map<String, Integer> getPropertyMap() {
return mPropertyMap;
}
public void setPropertyMap(Map<String, Integer> propertyMap) {
this.mPropertyMap = propertyMap;
}
public List<String> getEntitiesList() {
return mEntitiesList;
}
public void setEntitiesList(List<String> entitiesList) {
this.mEntitiesList = entitiesList;
}
public String getName() {
return mName;
}
public void setmName(String name) {
this.mName = name;
}
}
Main Class
public class MyClass {
private List<Term> mTermList = null;
private void init() {
mTermList = new ArrayList<Term>();
}
private void addSomeTerm() {
Map<String, Integer> propertyMap = new LinkedHashMap<String, Integer>();
propertyMap.put("p1", 1);
propertyMap.put("p2", 2);
propertyMap.put("p3", 3);
List<String> entitiesList = new ArrayList<String>();
entitiesList.add("T1");
entitiesList.add("T2");
Term term = new Term("a1");
term.generate(propertyMap, entitiesList);
mTermList.add(term);
}
private String printTerms() {
StringBuilder buff = new StringBuilder();
for(Term currTerm : mTermList){
buff.append(currTerm.getName()).append(" ");
Map<String, Integer> propertyMap = currTerm.getPropertyMap();
Set<String> sets = propertyMap.keySet();
Iterator<String> itr = sets.iterator();
String key = null;
Integer value = null;
while(itr.hasNext()){
key = itr.next();
value = propertyMap.get(key);
buff.append(key + "=" + value).append(",");
}
buff.setLength(buff.length()-1); // remove last ','
buff.append(" ");
List<String> entitiesList = currTerm.getEntitiesList();
for(String str : entitiesList){
buff.append(str).append(",");
}
buff.setLength(buff.length()-1); // remove last ','
}
return buff.toString();
}
/**
* #param args
*/
public static void main(String[] args) {
MyClass m = new MyClass();
m.init();
m.addSomeTerm();
System.out.println(m.printTerms());
}
}
Output:
a1 p1=1,p2=2,p3=3 T1,T2
It looks like you could have the following structure:
class Term {
String id;
Map<String, String> properties;
List<Entity> entities; // (or Set<Entity> if no duplicates are allowed)
}
But it's not very clear what you mean by "deep" and by "the properties can be either in one or more entities".
I need a mapping from a list of keys to a value. I know I could write my own code like this:
Map<Person, Map<Daytime, Map<Food, Integer>>> eaten = ...;
Now I want to have some get and put methods like these:
Integer numberOfEggsIAteInTheMorning = eaten.get(me, morning, scrambledEggs);
eaten.put(me, evening, scrambledEggs, 1);
Do you know of an existing class that has this kind of API? I'm too lazy of writing it myself. ;)
If you look for a more generic approach, and you might have more than 2 or 3 'chain steps', I would suggest in applying some different structural approach, rather than sticking to using only basic collection classes. I have feeling that Composite Pattern could be the right choice if it's correctly applied.
EDIT: due to example requested
The full example would be somewhat time consuming, so let me just explain my idea with dirty Java/pseudocode mix (I'm not even sure if I've missed something!!!). Let's consider we have class BaseMap:
abstract class BaseMap {
public abstract Object getValue(Object.. keys);
public abstract void putValue(Object value, Object.. keys);
}
Then we could have ObjectMap that would be the 'leaf' of our composite structure:
class ObjectsMap extends BaseMap {
private Map<Object, Object> map = new [...]
public Object getValue(Object.. keys) {
// assert that keys.length == 1
return map.get(keys[0]);
}
public void putValue(Object value, Object.. keys) {
// assert that keys.length = 1
map.put(keys[0], value);
}
}
And the actual composite would be as such:
class CompositeMap extends BaseMap {
private Map<Object, BaseMap> compositeMaps = new [...]
public Object getValue(Object.. keys) {
// assert that keys.length > 1
return compositeMap.get(keys[0]).getValue(/* System.arrayCopy => subset of elements {keys_1, .. ,keys_max} */);
}
public void putValue(Object value, Object.. keys) {
// assert keys.length > 1
BaseMap newMap = null;
if (keys.length = 2) -> newMap = new ObjectsMap()
else newMap = new CompositeMap();
newMap.putValue(value, /*subset of keys {keys_1, .. , keys_max}*/);
}
}
You can use org.apache.commons.collections.keyvalue.MultiKey for that: Map<Multikey, Object>
It would be hard to implement a general chained map.
How would the declaration of the class look like? (You can't have a variable number of type parameters.
class ChainedMap<K1..., V>
Another option would be to have a ChainedMapUtil class that performs put / get recursively.
Here is an example of a recursive get. (Quite ugly solution though I must say.)
import java.util.*;
public class Test {
public static Object chainedGet(Map<?, ?> map, Object... keys) {
Object k = keys[0];
if (!map.containsKey(k)) return null;
if (keys.length == 1) return map.get(k);
Object[] tailKeys = Arrays.copyOfRange(keys, 1, keys.length);
return chainedGet((Map<?,?>) map.get(k), tailKeys);
}
public static void main(String[] arg) {
Map<String, String> m1 = new HashMap<String, String>();
m1.put("ipsum", "dolor");
Map<Integer, Map<String, String>> m2 =
new HashMap<Integer, Map<String, String>>();
m2.put(17, m1);
Map<String, Map<Integer, Map<String, String>>> chained =
new HashMap<String, Map<Integer, Map<String, String>>>();
chained.put("lorem", m2);
System.out.println(chainedGet(chained, "lorem", 17, "ipsum")); // dolor
System.out.println(chainedGet(chained, "lorem", 19, "ipsum")); // null
}
}
If you are going to write your own, I would suggest
eaten.increment(me, evening, scrambledEggs);
You could use a composite key
eaten.increment(Key.of(me, evening, scrambledEggs));
(TObjectIntHashMap supports increment and adjust)
You may not even need a custom key.
eaten.increment(me + "," + evening + "," + scrambledEggs);
It is fairly easy to decompose the key with split()
I once made a map using 3 keys just for fun.May be you can use it instead of using chained maps:
public class ThreeKeyMap<K1,K2,K3,V>{
class wrap{
K1 k1;
K2 k2;
K3 k3;
public wrap(K1 k1,K2 k2,K3 k3) {
this.k1=k1;this.k2=k2;this.k3=k3;
}
#Override
public boolean equals(Object arg0) {
// TODO Auto-generated method stub
wrap o=(wrap)arg0;
if(!this.k1.equals(o.k1))
return false;
if(!this.k2.equals(o.k2))
return false;
if(!this.k2.equals(o.k2))
return false;
return true;
}
#Override
public int hashCode() {
int result=17;
result=37*result+k1.hashCode();
result=37*result+k2.hashCode();
result=37*result+k3.hashCode();
return result;
}
}
HashMap<wrap,V> map=new HashMap<wrap, V>();
public V put(K1 k1,K2 k2,K3 k3,V arg1) {
return map.put(new wrap(k1,k2,k3), arg1);
}
public V get(Object k1,Object k2,Object k3) {
return map.get(new wrap((K1)k1,(K2)k2,(K3)k3));
}
public static void main(String[] args) {
ThreeKeyMap<Integer,Integer,Integer,String> birthDay=new ThreeKeyMap<Integer, Integer, Integer, String>();
birthDay.put(1, 1,1986,"Emil");
birthDay.put(2,4,2009, "Ansih");
birthDay.put(1, 1,1986,"Praveen");
System.out.println(birthDay.get(1,1,1986));
}
}
UPDATE:
As #Arturs Licis suggested.I looked up in net for composite pattern and I wrote a sample using it.I guess this is composite..Please comment if it is not so.
Person class:
public class Person {
private final String name;
private Map<Time, Food> map = new HashMap<Time, Food>();
public Person(String name) {
this.name = name;
}
void addTimeFood(Time time, Food food) {
map.put(time, food);
}
public String getName() {
return name;
}
Food getFood(Time time) {
Food tmp = null;
return (tmp = map.get(time)) == null ? Food.NoFood : tmp;
}
// main to test the person class
public static void main(String[] args) {
Person p1 = new Person("Jack");
p1.addTimeFood(Time.morning, Food.Bread);
p1.addTimeFood(Time.evening, Food.Chicken);
Person p2 = new Person("Jill");
p2.addTimeFood(Time.morning, Food.Egg);
p2.addTimeFood(Time.evening, Food.Rice);
Map<String, Person> map = new HashMap<String, Person>();
map.put(p1.getName(), p1);
map.put(p2.getName(), p2);
System.out.println(map.get("Jack").getFood(Time.evening));
}
#Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append(name).append("\n");
b.append(map);
return b.toString();
}
}
Food class:
public enum Food {
Rice,
Egg,
Chicken,
Bread,
NoFood;
}
Time class:
public enum Time {
morning,
evening,
night
}
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;
}
}