Retrieving Values From Nested HashMap from user input - java

I'm trying to retrieve values from a nested hashmap depending on user selection. I'm having trouble getting the values from the nested Hashmap.
I've created the HashMap like this
private Map<String, Map<String, String>> contentTypesMap = new HashMap<String, Map<String, String>>();
If the user selects MDX i want to go to the nested HashMap and get the two string values in the 2nd hashmap.
contentTypesMap.put("MDX", new HashMap<String,String>()).put("HELLO1", "FOO");
And this my function for where to use hashmap,
public void getDatabaseSelectionValues (){
resourceType = (String) FileUtilityScreen.contentTypes.getSelectedItem();
sqlTableName = contentTypesMap.get(resourceType).get(key);
languageSelected = FileUtilityScreen.languagesFromDataBase.get(
FileUtilityScreen.languageDropDown.getSelectedItem());
}

with your exemple, if the selection is "MDX", you get your second map by doing
HashMap<String,String> selection = contentTypesMap.get("MDX")
then you can use entrySet() method to retrieve all entries of your map and iterate over entries.

See the documentation from Map.put(K key, V value)
#return the previous value associated with key, or
null if there was no mapping for key
So calling contentTypesMap.put("MDX", new HashMap<String,String>()) will always return the value that is not in the contentTypesMap map anymore.
Solution :
HashMap<String, String> map = contentTypesMap.get("MDX");
if (map == null) {
map = new HashMap<>();
contentTypesMap.put("MDX", map);
}
map.put("HELLO1", "FOO");
Or you can use guava Multimap instead.

Is the HELLO1 and FOO meant to be the value for MDX?
You are not putting it correct. Try this:
Map<String, String> value = new HashMap<String, String>();
value.put("HELLO1", "FOO");
contentTypesMap.put("MDX", value);
I'd also consider wrapping this outer map in its own object as it may get complex.

What I meant by a wrapper class in my comment above was something along these lines, to make it more readable. I'm assuming the key in the outer map is the table name, and in the inner map is the column name and data type for that column.
public class DatabaseTables {
private Map<String, Map<String, String>> dbTables = new HashMap<String, Map<String, String>>();
public void addColumn(String tableName, String column, String type){
Map<String, String> columns = dbTables.get(tableName);
if(dbTables.get(tableName) == null) {
columns = new HashMap<String, String>();
}
columns.put(column, type);
dbTables.put(tableName, columns);
}
public Map<String,String> getColumnsForTable(String tableName){
return dbTables.get(tableName);
}
}
That still feels ugly though. I think a better solution than this, would be to have a class called Table, with a string property for the table name, and a map for its columns and data types.
And then, elsewhere, you could have a list or a set of type Table.
Edit: Or, in Table.java, rather than a map for the columns, you could have a list or set of type Column. And then create a column class with two fields, one for the column name and the other for the type.

Related

Remove a key from a nested map in Java

I have a map object whose value is:
{Name={
Id=9999,
status=OK,
Idn={
source=TEST,
value=123,
TS=2018-11-14T18:10:33.998Z
}
}}
I have to remove the value=123 part from it so that my final Map looks like:
{Name={
Id=9999,
status=OK,
Idn={
source=TEST,
TS=2018-11-14T18:10:33.998Z
}
}}
the .remove(key) part would work on "Name" tag but I am unable to reach the nested Name.Idn.value and remove it.
Edited code:
My map is populated from mongoTemplate.findOne()
Map<String, Map<String, Object>> response
Query query = new Query();
Criteria criteria = Criteria.where("Name.Idn.value").is(123);
query.addCriteria(criteria).fields().exclude("_id");
response = mongoTemplate().findOne(query, Map.class, "dummy_col");
From response, while sending response back, the Name.Idn.value is to be removed.
I'd guess you have a Map<String, Object> map to be able to hold such different types as values, so you need to case first value as Map<String, Map> to be able to continue your path
((Map<String, Map<String, Object>>) map.get("Name")).get("idn").remove("source");
In fact yo have a Map<String, Map<String, Object>> map, so you know the first get() will return a Map, this is the second one that you need to cast
((Map<String, Object>) map.get("Name").get("idn")).remove("source");
You seem to have 3 levels of nested maps.
The value of name is a map, which has a key Idn with value that is also a map.
So, you would need a code that would look like this:
Map<String, Map<>> top = { ... };
Map<String, Map<>> name = top.get("Name");
Map<String, String> idn = name.get("Idn");
idn.remove("value");
You could also do it in one line like this:
Map <String, Map> top = { ... };
top.get("Name").get("Idn").remove("value");

Merge maps and make values as array

public class MapCheck {
public static void main(String[] args) {
Map<String, String> data = new HashMap<String, String>();
data.put("John", "Taxi Driver");
data.put("Mark", "Professional Killer");
Map<String, String> data1 = new HashMap<String, String>();
data1.put("John", "Driver");
data1.put("Mark", "Murderer");
Map<String, String> data3 = new HashMap<String, String>();
data3.putAll(data);
data3.putAll(data1);
System.out.println(data3);
}
}
I have few maps which contains same key, their values are different. I want to merge them. But when I merge them with the usual putAll() it gives me only the value of the key which was inserted latest.
Output from above code is {John=Driver, Mark=Murderer}
Is there a method which will get me all the values associated with the key and give me as a array like
{John=[Taxi Driver, Driver], Mark=[Professional Killer, Murderer]}
You can produce a Map<String, List<String>> quite easily with Java 8 Streams:
Map<String, List<String>>
merged =
Stream.of(data,data1) // create a Stream<Map<String,String> of all Maps
.flatMap(map->map.entrySet().stream()) // map all the entries of all the
// Maps into a
// Stream<Map.Entry<String,String>>
.collect(Collectors.groupingBy(Map.Entry::getKey, // group entries by key
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())));
The output Map:
{John=[Taxi Driver, Driver], Mark=[Professional Killer, Murderer]}
Something like this?
void add(String key, String value, Map<String,List<String>> map) {
List<String> list = map.get(key);
if (list == null) {
list = new ArrayList<>();
map.put(key, list);
}
list.add(value);
}
the data structure Multimap may be the best choice. The implementation in guava is highly recommended.
https://github.com/google/guava/wiki/NewCollectionTypesExplained#multimap
You may want to create Map<String, <ArrayList<String>>. That way you can store multiple values that are associated with one key.
You may consider creating method that provides storing multiple values associated to unique key in specified map:
public static void add(String key, String value, Map<String, List<String>> map) {
// Here you create a refeREnce to instance of the list storing values associated with given key:
List<String> listOfValues = map.get(key);
// If given key was already present in the map...
if(listOfValues != null) {
// Add given value to list of values associated with given key:
listOfValues.add(value);
// If given key wasn't present in the map...
} else {
// Initialize an ArrayList that will store given value in the map:
listOfValues = new ArrayList<>();
// Put new entry into the given map (given key and to-be-filled list of values:
map.put(key, listOfValues);
// Put given value to the list of values associated with given key
listOfValues.add(value);
}
}

How to Extract a Map object from another Map object Java

I have the following data structure:
Map<String,Map<String,String>>
I'd like to extract its value (which itself is another string Map) from this complex Map object. I am currently doing it as such:
Map<String, Map<String, String>> map = getStructure(data,format);
Map<String,String> newMap = new LinkedHashMap<String, String>();
for(Entry<String, Map<String,String>> entry : map.entrySet()) {
for (Entry<String, String> value : entry.getValue().entrySet()) {
newMap.put(value.getKey(),value.getValue());
}
}
The above implementation gives me a new Map object with repeating key-value pairs due to the outer foreach loop, it is iterating through. Seems like I'm missing something.
How can I extract the inner Map object from the complex Map object?
Edit:
Addressing AlexWien's comment
Original Data Structure:
The reasoning behind the original data structure is to store a single value for a pair of IDs (ID1 and ID2). ID1 and ID2 may be different. So it is structured as:
Map<String,Map<String,String>> ===> <someValue, <ID1,ID2>>
What I am trying to achieve is to get the entire list of the id pairs (ID1 and ID2) for every someValue. So I can store them in a database to keep track of aeronautical information.
Having an map of maps:
Map<String, Map<String, String>> map
you get the inner map simply by calling get
String key = ...; // TODO
Map<String, String> innerMap = map.get(key);
Update to your edit:
It further seems you need something like a map of pairs:
Map<String, Pair<String, String>> mapOfPairs.
Unfortuneatly java has no Pair class.
So write one yourself:
public class Pair {
String id1;
String id2;
}
and have a
Map<String, Pair> mapOfPairs;

Searching for key/value pair in Collection, return all values in that Collection if successfully matched

My last attempt was poorly explained, so recreated with hopefully a clearer explanation.
I have a Collection that has the following data structure.
LinkedHashMap<String, String> currentHashMap = new LinkedHashMap<String, String>();
currentHashMap.put("id","12345");
currentHashMap.put("firstName","John");
currentHashMap.put("lastName","Doe");
haystack.add(currentHashMap);
*repeat for roughly 250,000 more entries
I need to search the HashMaps within each list entry, check if the key exists, and if it has the corresponding value. If it does, I want to return all the values in that particular hashmap
This is the code that I'm currently using.
private TreeSet<String> searchWithinCollection(List<LinkedHashMap<String, String>> haystack, String needle, String needleKey) {
TreeSet<String> returnValueSet = new TreeSet<>();
for (LinkedHashMap<String, String> mappedData : haystack) {
System.out.println(mappedData.values());
for (Entry<String, String> specificData : mappedData.entrySet()) {
if (needleKey.equals(specificData.getKey()) && needle.equals(specificData.getValue())) {
//where I want to collect the values within the current hashmap, as it's satisfied the search criteria
//the current code would only return the current key/balue pair, even if all values were collected outside the for loop. Instead, I'd want to collect "12345", "John", "Doe" if needleKey = id and needle = 12345.
}
}
}
return returnValueSet;
}
What I'd want is if a key in the current collection equaled "id", and it's value equaled "12345", then I could returned all values within that particular collection (and to use the example above, would be "12345", "John", "Doe").
The best I can do is return the first key/value, which isn't very helpful obviously. Attempting to capture all values in mappedData outside the second for loop brought no result (either with creating a new instance of a Collection, clone, or what have you.). I also found nothing that could manually advance the pointer in the for each loop to manually capture all values in the Collection.
EDIT: If it helps, where there is a System.out.println(mappedData.values()); I can get the values I'm expecting, but only the id in the example is return if values() is accessed within the if statement.
You should not iterate over all the entries in a hashmap (currently, the loop for (Entry<String, String> specificData : mappedData.entrySet()) in your code. This defeats the purpose of maintaining a hashmap.
Instead, the inner loop should become
String potentialNeedle = mappedData.get(needleKey);
if (needle.equals(potentialNeedle))
returnValueSet.addAll(mappedData.values());
You're using the map as if it was a list of entries, which completely defeats the purpose of a map: quickly access to a value for a given key:
for (Map<String, String> mappedData : haystack) {
String value = mappedData.get(needleKey);
if (needle.equals(value)) {
returnValueSet.addAll(mappedData.values());
}
}
Maybe you should take redesign in account to improve performance. it could be smart to have a map for each field you want to search. instead of your haystack create your data-structure like that:
public class PersonDataManager {
private final Map<Integer, Map<String, String>> baseData;
private final Map<String, List<Integer>> firstNameLookupMap;
private final Map<String, List<Integer>> lastNameLookupMap;
public PersonDataManager(){
this.baseData = new HashMap<>();
this.firstNameLookupMap = new HashMap<>();
this.lastNameLookupMap = new HashMap<>();
}
public void addPerson(Integer id, String firstName, String lastName){
//try to find existing person to update:
Map<String, String> personMap = baseData.get(id);
if(personMap == null){
personMap = new HashMap<>();
baseData.put(id, personMap);
}
personMap.put("firstName", firstName);
personMap.put("lastName", lastName);
//add to lookup-maps
addLookupName(firstNameLookupMap, id, firstName);
addLookupName(lastNameLookupMap, id, lastName);
}
private static void addLookupName(Map<String, List<Integer>> nameMap, Integer id, String name){
//get existing list of the name:
List<Integer> idList = nameMap.get(name);
if(idList == null){
idList = new ArrayList<>();
}
if(!idList.contains(id)){
idList.add(id);
}
}
private List<Map<String, String>> searchByName(Map<String, List<Integer>> nameMap, String name){
List<Integer> matchingIds = nameMap.get(name);
List<Map<String, String>> result = new ArrayList<>();
if(matchingIds != null){
for(Integer id : matchingIds){
result.add(searchById(id));
}
}
return result;
}
public Map<String, String> searchById(Integer id){
return baseData.get(id);
}
public List<Map<String, String>> searchByFirstName(String name){
return searchByName(firstNameLookupMap, name);
}
public List<Map<String, String>> searchByLastName(String name){
return searchByName(lastNameLookupMap, name);
}
}
This way you can easily get a hashmap of a person using its id. if you need to search by firstname or lastname you can utilize the additional maps to get the matching person-ids. hope this helps
EDIT: Just implemented a class which does exactly what i think you might need. have fun ;)

Converting Each Value in static HashMap to String Java

I have got some troubles converting each value in my HashMap to a String.
private static HashMap<String, List<Music>> musiksammlung = new
HashMap<String, List<Music>>();
This is my constructor for the HashMap. The key represents the album, the value a list of tracks from this album.
Now I want to convert each Music object to a String without creating a new HashMap, is this
possible?
I've tried it with the Iterator scheme, for loop over the entry set and so on but nothing seems to work.
Edit://
My code for the convertmethod:
public HashMap<String, List<String>> generateFormatList() {
HashMap<String, List<String>> formatList = new HashMap<String, List<String>>();
for(String key : musiksammlung.keySet())
formatList.put(key, musiksammlung.get(key).toString());
return musiksammlung;
}
But this always results in an error "is not applicable for the Arguments (String, String) so I have no idea. Do I have to override toString()?
You're on the right path but you need to convert the existing List<Music> to a List<String> and put the List<String> into your new HashMap.
You also then want to return your newly created HashMap<String, List<String>> instead of your original one.
public HashMap<String, List<String>> generateFormatList() {
HashMap<String, List<String>> formatList = new HashMap<String, List<String>>();
for(String key : musiksammlung.keySet()) {
// Value to store in map
List<String> value = new ArrayList<String>();
// Get the List<Music>
List<Music> musicList = musiksammlung.get(key);
for (Music m: musicList) {
// Add String of each Music object to the List
value.add(m.toString);
}
// Add the value to your new map
formatList.put(key, value);
}
// Return the new map
return formatList;
}
So answer your question:
Now I want to convert each Music object to a String without creating a
new HashMap, is this possible?
You need to create a new HashMap, because it's storing different type of value: List<Music> is different from List<String>.
Also as mentioned in my previous answer, make sure you override Music.toString() so that it returns a meaningful String for you instead of the one it inherits from its parent classes, which includes at least java.lang.Object
formatList wants a List<String>, but musiksammlung.get(key).toString() returns a String (not a List<String>). Did you mean this?
HashMap<String, String> formatList = new HashMap<String, String>();
Have you tried something like this:
Iterator<String> it = musiksammlung.keySet().iterator();
while (it.hasNext()) {
List<Music> ml = musiksammlung.get(it.next());
for (Music m : ml)
System.out.println(m.toString());
}
And of course you should override the Music#toString() method with something you could use.
Try to change your HashMap like this:
private static HashMap<String, List<Object>> musiksammlung = new HashMap<String,List<Object>>();
So you can save any kind of objects in this HashMap. Also use instanceof to check the type of the object before using it.

Categories

Resources