Retrieving nested key:value pairs in Java - java

I am wondering how to build a nested Map and then retrieve the nested key:value pairs. This is how I created the map to be nested.
//Create List of Nested HashMaps
List<Map> nestedMap = new ArrayList<>();
nestedMap.add(building3Map);
nestedMap.add(building31Map);
nestedMap.add(buildingHFTFMap);
System.out.println("The nested map is: " + nestedMap);
This is the system output for the following code:
The nested map is: [{buildingname=[Building 3], buildingid=[3]}, {buildingname=[Building 31], buildingid=[1]}, {buildingname=[HFTF], buildingid=[4]}]
This is correct as I want a list of maps. But the next step is what is confusing to me. When I try to build the outer layer Map as below:
HashMap<String, List<Map>> queryMap = new HashMap<>();
queryMap.put("buildings", nestedMap);
System.out.println(queryMap.get("buildings.buildingid"));
I get a system output of null when attempting the .get("buildings.buildingid") method. Ideally, I need the output to look like this:
[[3, 1, 4]]
Where it returns all values with a key of "buildings.buildingid" in an array. I am new to coding so please let me know if I'm making any fundamental errors and how I can create a nested Map where I can access the inner layer or if there is another method I should be using.

I think you are making it way too complicated than it should be. you can store your data in a simple map where the ids are the keys for example and the names are the values. So you only need to read the keys or the values from the map to get your result.
Map<Integer, String> myMap = new HashMap<>();
myMap.put(3, "Building 3");
myMap.put(31, "Building 31");
myMap.put(4, "HFTF");
System.out.println(myMap.keySet());
System.out.println(myMap.values());
However, Java is an object-oriented language. If it makes sense for your use case you might want to create a custom class "Building" instead of frantically trying to store your data in nested data structures. See below for an example of how it might look using a custom class:
import java.util.ArrayList;
import java.util.List;
public class Example {
public static void main(String args[]) {
List<Building> buildings = new ArrayList<>();
buildings.add(new Building(3, "Building 3"));
buildings.add(new Building(31, "Building 31"));
buildings.add(new Building(4, "HFTF"));
List<Integer> buildingIds = new ArrayList<>();
buildings.forEach(building -> buildingIds.add(building.getBuildingId()));
List<String> buildingNames = new ArrayList<>();
buildings.forEach(building -> buildingNames.add(building.getBuildingName()));
System.out.println(buildingIds);
System.out.println(buildingNames);
}
public static class Building {
int buildingId;
String buildingName;
public Building(final int buildingId, final String buildingName) {
this.buildingId = buildingId;
this.buildingName = buildingName;
}
public int getBuildingId() {
return buildingId;
}
public void setBuildingId(final int buildingId) {
this.buildingId = buildingId;
}
public String getBuildingName() {
return buildingName;
}
public void setBuildingName(final String buildingName) {
this.buildingName = buildingName;
}
}
}

queryMap.get("buildings.buildingid") returns null, because queryMap only contains a value under the key
buildings. A HashMap can only access the value using the same key it was stored under. The key and the value is not processed in any further way.
A simple alternative could be
queryMap.get("buildings").stream() // Create a Stream<Map>
.map(building -> building.get("buildingid")) // Create Stream<String> by extracting the buildingid values.
.collect(Collectors.toList()); // Collect Stream into a List<String> which contains the buildingid's
If you don't like this solution you could take a deeper look into property expressions, and write your own map implementation that can resolve these expressions. But it will be a lot of work to get it working correctly...

Related

How to use HashMap when Json file has same key value

My json file looks like this [actually it has more, I am just putting 2 blocks for example]
[{
"answerValue": "2021-02-01",
"parentId": "Policy",
"instance": 1,
"fieldId": "PolicyEffectiveDate"
},
{
"answerValue": "2012",
"parentId": "Insured",
"instance": 1,
"fieldId": "DateBusinessStarted"
}
]
I want to store them in a HashMap and print them.
public void MapCheck() {
Map<String, Object> dataMap = new HashMap<>();
List<Map> lstMap = new ArrayList<>();
dataMap.put("answerValue:", "2021-02-01");
dataMap.put("parentId:", "Policy");
dataMap.put("instance:", 1);
dataMap.put("fieldId:", "PolicyEffectiveDate");
lstMap.add(dataMap);
dataMap.put("answerValue:", "Assurestart LLC");
dataMap.put("parentId:", "Insured");
dataMap.put("instance:", 1);
dataMap.put("fieldId:", "Business_Name");
lstMap.add(dataMap);
System.out.println(lstMap);
}
public static void main(String[] args) {
Test t = new Test();
t.MapCheck();
}
}
Expected: I wanted it to print
[{parentId:=Policy, fieldId:=PolicyEffectiveDate, answerValue:=2021-02-01, instance:=1}, {parentId:=Insured, fieldId:=Business_Name, answerValue:=Assurestart LLC, instance:=1}]
Actual: It is printing, the last value twice.
[{parentId:=Insured, fieldId:=Business_Name, answerValue:=Assurestart LLC, instance:=1}, {parentId:=Insured, fieldId:=Business_Name, answerValue:=Assurestart LLC, instance:=1}]
How can I make it print 2 different values? Thanks in advance for your time and ideas.
You should create a new map for the second entry instead of overwriting the first entry’s values. Add
dataMap = new HashMap<>();
After adding the first entry to the list.
You should create a new map for the second map in the list:
Map<String, Object> dataMap = new HashMap<>();
List<Map<String, Object>> lstMap = new ArrayList<>();
dataMap.put("answerValue:", "2021-02-01");
dataMap.put("parentId:", "Policy");
dataMap.put("instance:", 1);
dataMap.put("fieldId:", "PolicyEffectiveDate");
lstMap.add(dataMap);
dataMap = new HashMap<>(); // create a new map!
dataMap.put("answerValue:", "Assurestart LLC");
dataMap.put("parentId:", "Insured");
dataMap.put("instance:", 1);
dataMap.put("fieldId:", "Business_Name");
lstMap.add(dataMap);
That said, if you actually want to generate JSON, or read a JSON file, I recommend using a JSON serialisation/deserialisation library, such as GSON. That way, you can represent your data not as hash maps, but a class like this:
class MyObject {
private String answerValue;
private String parentId;
private int instance;
private String fieldId;
// getters & setters...
}
HashMap as you know is a data structure that works based on unique key and value pair property.
In the example above when you perform dataMap.put("answerValue:", "2021-02-01"); it saves the value for this key in the HashMap. However when you perform, dataMap.put("answerValue:", "Assurestart LLC"); the second time, it will override the value of "answerValue:" key as it already exists there.
A better approach is to create a new class that can contain all this data in it and then you can decide on a unique key to store this data in. Thus your values will be an object that contains this entire block of data.
For example,
public class MyData {
private String answerValue;
private String parentId;
private Integer instance;
private String fieldId;
//Setters and getters
...
}

use reflection to set or get array in a Java Map

Is there a way to get or set an array element stored in a Java Map?
Example:
If we have a map like this:
{
name: "Blah",
friends: ["Foo", "Bar"]
}
Map<String, Object> myMap = new HashMap<>();
List<String> friends = new ArrayList<>();
myMap.put("name", "Blah");
myMap.put("friends", friends);
Is it possible to use Reflection to get or set the first element in the friends array in the "myMap" from the string: "myMap.friends[0]"
Your question is not very clearly written and I believe that's why you are not getting the answer you expect but, If I understood your question correctly, you need to parse the following input string at runtime that you don't know beforehand:
myMap.friends[0]
And this should be parsed into components like:
mapName = "myMap"
mapKey = "friends"
valueIndex = 0
And with this information, you need to manipulate data in a Map at runtime through reflection.
Note: This only makes sense if you could potentially have more complex expressions, using different sort of objects and accessing nested properties of retrieved objects, otherwise you wouldn't need reflection at all.
Note 2: You may want to have a look at JXPath which already does a lot of this for you based on a XPath-like syntax for navigating object graphs.
That said, if my assumptions are correct and you still want to do it yourself, consider the following example.
For the sake of demonstration, let's consider our map is returned by a method myMap inside a Context.
private static class Context {
public Map<String, Object> myMap() {
Map<String, Object> myMap = new HashMap<>();
List<String> friends = new ArrayList<>();
friends.add("Foo");
friends.add("Bar");
myMap.put("name", "Blah");
myMap.put("friends", friends);
return myMap;
}
}
I'm assuming you are already parsing the input string into the different components. If not, for this simple string you could do it with simple regular expressions. If you already have the components, let's consider the following method:
public static Object readContextMap(Context context,
String mapName, String mapKey, Integer mapValueIndex) throws Exception {
// gets Context class for inspection
Class<?> cls = context.getClass();
// search for a method based on supplied mapName
Method mapMethod = cls.getDeclaredMethod(mapName);
// get a value from the retrieved map based on mapKey
Object mapValue = mapMethod.getReturnType()
.getDeclaredMethod("get", Object.class)
.invoke(mapMethod.invoke(context), mapKey);
// if the result is of type list, use the index to return the indexed element
if (List.class.isAssignableFrom(mapValue.getClass())) {
return ((List<?>)mapValue).get(mapValueIndex);
}
// otherwise return the object itself
return mapValue;
}
For testing purposes, consider the following main method:
public static void main(String[] args) throws Exception {
Context context = new Context();
String input = "myMap.friends[0]";
// parse input into...
String mapName = "myMap";
String mapKey = "friends";
Integer valueIndex = 0;
Object firstFriend = readContextMap(context, mapName, mapKey, valueIndex);
System.out.println(firstFriend);
// prints Foo
Object name = readContextMap(context, "myMap", "name", null);
System.out.println(name);
// prints Blah
}
This should be approximately what you want. You can easily create variations of this to set values as well. Please bear in mind that this code is just for demo purposes and needs a better error handling (e.g. verify if the context is really returning a map and nothing else).
This should be something along the lines you are looking for.
There's no need to use reflection here. You can simply cast it (which is also unsafe, but less so).
You can just do this:
List<String> friends = (List<String>) myMap.get("friends");
friends.set(0, "Bob");

How to get individual array names as strings for array of arrays

// Facility table attributes to be read in json format
String facilityName[], recApp[], recFacility[], sendApp[],
sendFacility[], enable[], doneness[], retryLimit[],
watchDelay[], retryDelay[], ackTimeout[],
keepConOpen[], sendTimeout[], cleanupDelay[],
host[], port[];
String facilityTableAttrs[][] = new String[][] {
facilityName, recApp, recFacility, sendApp,
sendFacility, enable, doneness, retryLimit,
watchDelay, retryDelay, ackTimeout, keepConOpen,
sendTimeout, cleanupDelay, host, port};
I have array of arrays called facilityTableAttrs declared as above.
I have 2 questions:
1) Is it possible to do the above array declaration in a single step ?
2) I wish to get the individual array names of these 1D array using something like:
for(i = 0; i < facilityTableAttrs.length; i++) {
System.out.println(facilityTableAttrs[i].toString());
}
but it fails. How to get the individual array names as string?
The first list of arrays you declare don't seem to be initialized anywhere.
As such they are null, and invoking toString on any of them will cause a NullPointerException to be thrown, hence "it fails".
By the way, invoking toString on an non-null array would actually print something similar to the Object.toString representation, which is not what you want (Arrays.toString(myArray) is your friend here).
You could initialize each and every single array and populate them optionally, before adding them to the main String[][] but I recommend you don't.
Instead, investigate the collections framework.
What you could use here is a Map<String, List<String>>.
Or better even, a custom object with properties such as List<String> facilityName, List<String> recApp, etc.
Finally, note the variable naming, which is camelBack according to code conventions.
This is not possible with arrays. You need to use map, like so:
Map<String, List<String>> myMap = new HashMap<String, List<String>>();
You need to choose correct data structure for your problem.
Arrays are used only for storing values, thay are not interestd in bounding names to them.
Maps on the other hands are great with bounding names (keys that are unique) to any type of value.
I propose to use a wrapper class:
public class Facility {
private final String name;
private final List<String> values;
public Facility(String name) {
this.name = name;
this.values = new ArrayList<>();
}
public String getName() {
return name;
}
public List<String> getValues() {
return values;
}
}
and then do:
Facility[] facilities = new Facility[] {
new Facility("facility 1"),
new Facility("facility 2"),
new Facility("facility 3"),
new Facility("facility 4"),
};
for(Facility facility : facilities) {
System.out.println(facility.getName());
}
To add a value to a facility you'd do:
Facility facility = facilities.get(0);
facility.getValues().add("bla");
If you need to look up facilities by name, then use a Map instead of an array:
...
// see createLookup method below
Map<String, Facility> facilities = createLookup(
new Facility("facility 1"),
new Facility("facility 2"),
new Facility("facility 3"),
new Facility("facility 4"));
// print names
for(Facility facility : facilities.values()) {
System.out.println(facility.getName());
}
// add a value
Facility facility = facilities.get("facility 3");
facility.getValues().add("bla");
}
private Map<String, Facility> createLookup(Facility.. facilities) {
// use TreeMap to have sorted keys
Map<String, Facility> lookup = new TreeMap<>();
for(Facility facility : facilities) {
lookup.put(facility.getName(), facility);
}
return lookup;
}

Creating Arrays with automated names?

I have a method in which I load tiles from a text file. The tiles are placed in an array when created, so that they can be cleared later. This has began to cause problems and I am wondering if there would be a way to create an array with a name that corresponds to the text file loaded. For example, I call
loadMap("map1");
With "map1" being the name of the txt file that the map is stored in. And if I were to call the loadMap method with the argument of "map1" how can I create an array titled something like "map1TileArray", or if the argument is "finalMap" I would want an array called "finalMapTileArray". Is it possible to do something like this, and if so, how?
EDIT:
I'm getting a NPE.
I declare my Map like this:
Map<String, ArrayList<Tile>> tileMap = new HashMap<String, ArrayList<Tile>>();
I then store an ArrayList in the tileMap with a string of the current map:
tileMap.put(map, tilearray);
But I get an error at this line:
if(tileMap.get(currentMap).size()>0) {
Which is the start of my unloadTiles method. currentMap is just the String for the map the program is on.
You will want to use a Map such as a HashMap, perhaps a Map<String, Integer[]>. This will allow you to create an array of Integer (or whatever) and associate it with a String.
For example:
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class Foo {
public static void main(String[] args) {
Map<String, Integer[]> myMap = new HashMap<>();
myMap.put("foo", new Integer[] { 1, 2, 3 });
myMap.put("bar", new Integer[] { 3, 4, 5 });
myMap.put("spam", new Integer[] { 100, 200, 300 });
for (String key : myMap.keySet()) {
System.out.printf("%8s: %s%n", key, Arrays.toString(myMap.get(key)));
}
}
}
Use a java.util.Map and assign the value to a variable. Probably you will be better if use a List instead of an array
List<Integer> currentArray = loadMap("map1");
....
// inside
private List<Integer> loadMap( String fileName ) {
List<Integer> result = allTheMaps.get( fileName );
if ( result == null ) {
// load it from file...
result = ....
allTheMaps.put( fileName, result );
}
return result;
}
As others have said, a map will work for this.
What others have not said is that you would probably also benefit from using a class to represent your tiles as well.
This way, any array logic you have for manipulating the tiles can be nicely encapsulated in one place. I would imagine something like this:
public class Tiles{
private int[] tiles;
private String name;
private Tile(int[] tiles, String name){
this.tiles = tiles;
}
public static Tiles getTiles(Map<String, Tiles> tilesCache, String tileName){
if (tilesCache.containsKey(tileName)){
return tilesCache.get(tileName);
}
// load from file
return tile;
}
public void clear(Map<String, Tiles> tilesCache){
tilesCache.remove(this.name);
this.tiles = null;
}
//Other logic about tiles
}
You might want to consider using a HashMap with a String as the key and an Integer[] for the value.
Map<String, Integer[]> maps = new HashMap<String, Integer[]>();
and when you call your loadMap function you could do something like this.
public Integer[] loadMap(String name) {
if (maps.contains(name)) {
return maps.get(name);
}
// Falls through if map is not loaded
int[] mapData = new int[##];
// load map
maps.put(name, mapData);
return mapData;
}

Create dynamic ArrayLists

I have a problem related to "dynamic ArrayLists". I have a List that contains usernames and their data. I want for every distinct username to create a single list that contains all data of this user. For example, I have an arraylist (username,tweet) that has: lefteris,"Plays ball", Kostas, "Plays basketball", lefteris, "Nice weather". And I want after that to create two lists. One list with kostas and his tweets and another with lefteris and its tweets (2 tweets). The parent arraylist may have 20 distinct usernames or more. How can I do that ?
I recommend you to use hashmap or hashset instead because if you need to store something in pairs, hashing is a perfect solution......
I'd go with the following data structure:
HashMap<String, ArrayList<String>>
Then you could manipulate a "dynamic" list of properties keyed to each name, if the properties are single items:
Lefteris->("Plays ball", "Nice weather",...)
Kostas->("Plays basketball",...)
If the properties are key-value pairs, do:
HashMap<String, HashMap<String, Object>>
Data looking like:
Lefteris->(Sport->"Plays ball", Weather->"Nice",...)
Kostas->(Sport->"basketball",...)
Since you parse the items from a file, you can do the following.
Create a map that contains the tweets associated to a particular username
Map<String,List<String>> userTweets = new HashMap<String,List<String>>();
Then, have a method to associate a tweet to certain user, verifying that it is already added in the map and adding it if it isn't.
public void addTweetToUser(String user, String tweet) {
if(userTweets.containsKey(user))
userTweets.get(user).add(tweet);
else {
List<String> newUserTweets = new LinkedList<String>();
newUserTweets.add(tweet);
userTweets.put(user, newUserTweets);
}
}
As a plus, you can improve this by creating an object UserTweet that contains:
public class UserTweet {
private String user;
private String tweet;
//Constructor, Setters & Getters or all of them
}
Then your addTweetToUser method can have an UserTweet parameter instead.
When you want to know the tweets for a certain user, you just obtain the corresponding list from the userTweets map. I alsomethods to remove tweets and/or remove users, just in case.
Several libraries add excellent collection-processing functionality to Java along the lines of what functional languages provide. One such library is Google Guava. Guava provides a MultiMap suitable for grouping things the way you want. There are also many utility methods, like MultiMaps.index(), which collects items from a list into a map by applying some function to the elements of the list to calculate a key. With such support, it only takes a few lines of code and one Function implementation (a closure in any other language) to solve your problem:
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import java.util.Arrays;
import java.util.List;
public class Tweets {
public static final int NAME = 0;
public static final int TWEET = 1;
public static void main(String[] args) {
List<String> namesAndTweets = Arrays.asList(
"lefteris", "Plays ball",
"Kostas", "Plays basketball",
"lefteris", "Nice weather");
List<List<String>> nameTweetPairs =
Lists.partition(namesAndTweets, 2);
Multimap<String, List<String>> namesAndTweetsByName =
Multimaps.index(nameTweetPairs, get(NAME));
Multimap<String, String> tweetsByName =
Multimaps.transformValues(namesAndTweetsByName, get(TWEET));
System.out.println(tweetsByName);
}
private static Function<List<String>, String> get(final int n) {
return new Function<List<String>, String>() {
#Override
public String apply(List<String> nameAndTweet) {
return nameAndTweet.get(n);
}
};
}
}
Outputs:
{lefteris=[Plays ball, Nice weather], Kostas=[Plays basketball]}
Update: To explain the code a bit more, there are three basic steps:
Take the list that has names and tweets all mixed together and use Lists.partition() to break it into pairs of (name, tweet).
Use MultiMaps.index() to build a MultiMap from the pairs, taking the name as the map key. This gives you a map where map keys are names and map values are the (name, tweet) pairs.
Use MultiMaps.transformValues() to reduce the map values from (name, tweet) pairs to just the tweets.
P.S. does anyone know if there's a built-in Function that does what my get() does? It seems like a useful Function that should be provided, but I can't find it anywhere.

Categories

Resources