Java convert {String,String}[] to Map<String,String[]> - java

Given the class:
public class CategoryValuePair
{
String category;
String value;
}
And a method:
public Map<String,List<String>> convert(CategoryValuePair[] values);
Given that in values we can receive many entries with the same category, I want to convert these into a Map grouped on category.
Is there a quick / efficient way to perform this conversion?

As far as I know there is not easier way than iterating on values, and then putting the values in the map (like some predefined method).
Map<String, List<String>> map = new HashMap<String, List<String>>();
if (values != null) {
for (CategoryValuePair cvp : values) {
List<String> vals = map.get(cvp.category);
if (vals == null) {
vals = new ArrayList<String>();
map.put(cvp.category, vals);
}
vals.add(cvp.value);
}
}
I changed the map values from String[] to List<String> since it seems easier to me to use that so you don't have to hassle with array resizing.

To make it in fewer lines of code, use Google Collections:
public Map<String, Collection<String>> convert(CategoryValuePair[] values) {
Multimap<String, String> mmap = ArrayListMultimap.create();
for (CategoryValuePair value : values) {
mmap.put(value.category, value.value);
}
return mmap.asMap();
}
If you don't want to allow duplicate values, replace ArrayListMultimap with HashMultimap.

With lambdaj you just need one line of code to achieve that result as it follows:
group(values, by(on(CategoryValuePair.class).getCategory()));

Just for the sake of implementation... The method returns Map and also checks for duplicates in the arrays... though performance wise its heavy ...
public Map<String,String[]> convert(CategoryValuePair[] values)
{
Map<String, String[]> map = new HashMap<String, String[]>();
for (int i = 0; i < values.length; i++) {
if(map.containsKey(values[i].category)){
Set<String> set = new HashSet<String>(Arrays.asList(map.get(values[i].category)));
set.add(values[i].value);
map.put(values[i].category, set.toArray(new String[set.size()]));
}else {
map.put(values[i].category, new String[]{values[i].value});
}
}
return map;
}

Related

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 ;)

How to sort " ArrayList<HashMap<String, String>> arrList " alphabatically?

Can you guide me how can I sort Array List having Hash Map alphabatically?
JSONArray jArr2 = new JSONArray(jsonString2);
for(int i=0;i<jArr2.length();i++){
HashMap<String, String> map = new HashMap<String, String>();
map.put("titleName",jArr2.getJSONObject(i).getString("titleName"))
programList.add(map);
}
Implement a Comparator<HashMap<String, String>> which just extracts the value assocated with the value key, then use Collections.sort method to sort your arraylist.
For e.g.:
class MyComparator implements Comparator<Map<String, String>>{
private final String key;
public MyComparator(String key)
{
this.key = key;
}
public int compare(Map<String, String> first,
Map<String, String> second)
{
// TODO: Null checking, both for maps and values
String firstValue = first.get(key);
String secondValue = second.get(key);
return firstValue.compareTo(secondValue);
}
}
Looking at your example, I don't think you need a Map to be involved at all. What you have is a list of Maps, where every Map only has one key, which is "titleName". Why not just have a list of titlenames? Then your code would look like this:
JSONArray jArr2 = new JSONArray(jsonString2);
List<String> titleNames = new ArrayList<>();
for (int i = 0; i < jArr2.length(); i++) {
titleNames.add(jArr2.getJSONObject(i).getString("titleName"))
}
You know that the list only contains titleNames, you don't need to complicate the data structure with Maps!
Then you can sort the list simply by using
Collections.sort(titleNames);
Note that this will work while the other answers that suggests Collections.sort() on the list of maps will not work. This is because titleNamees is a List of Strings, which implement Comparable (ie the sort() method knows how to order them with respect to each other), while Map does not implement comparable (as there are multiple ways to order Maps - number of entries, total number of bytes, etc).
Yes you can use Collections.sort(); with a custom comparator. Here is the doc.
Collections.sort(YOUR_ARRAY_LIST, new YourCustomComparator());
And this should be the class you must have
class YourCustomComparator implements Comparator<HashMap<String, String>> {
#Override
public int compare(HashMap<String, String> lhs, HashMap<String, String> rhs) {
// check here your objects. lhs and rhs. compare them as you want
// return 1 if lhs is greater than rhs
// return 0 if ther are same
// return -1 otherwise
}
}

Creating subset of HashMap based on some specifications?

So I have the following HashMap:
HashMap<String, List<someDataType>> map;
I want to create a new HashMap that is only composed of the k/v pairs in map that have a value (the list) whose length is less than a certain "x". The only way I know how to do this is to iterate through the HashMap and put k/v pairs into a new HashMap. Is there a more concise way to achieve what I'm looking for? Thanks.
Using guava:
Map<String, List<String>> newMap =
Maps.filterEntries(originalMap, new MyEntryPredicate(10));
where:
private static class MyEntryPredicate implements Predicate<Map.Entry<String, List<String>>> {
// max list length, exclusive
private int maxLength;
private MyEntryPredicate(int maxLength) {
this.maxLength = maxLength;
}
#Override
public boolean apply(Map.Entry<String, List<String>> input) {
return input != null && input.getValue().size() < maxLength;
}
}
If the Guava library is available to your project, you could use Maps.filterValues (somewhat echoing Keith's answer):
final int x = 42;
Map<String, List<String>> filteredMap =
Maps.filterValues(map, new Predicate<Collection<?>>() {
#Override
public boolean apply(final Collection<?> collection) {
return collection.size() < x;
}
});
Map<String, List<String>> filteredMapCopy = ImmutableMap.copyOf(filteredMap);
Note the need for a copy because filterValues returns a filtered view of the original map.
Update: with Java 8 you can simplify the predicate to a lambda expression:
Map<String, List<String>> filteredMap = Maps.filterValues(map, list -> list.size() < x);
Nowadays (Java 8+) this could be done with streams:
Predicate<Map.Entry<String, List<String>>> test = entry -> entry.getValue().size() <= x; // note this is java.util.function.Predicate
Map<String, List<String>> filteredMap = map.entrySet().stream().filter(test)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
This helps to avoid the dependency to guava which might be undesired.
You may want to look at the Guava library from Google. There's an enormous number of Collections and Map related utils in there, which let you do complex stuff quite concisely. An example of what you can do is:
Iterable<Long> list =
Iterables.limit(
Iterables.filter(
Ordering.natural()
.reverse()
.onResultOf(new Function<Long, Integer>() {
public Integer apply(Long id) {
return // result of this is for sorting purposes
}
})
.sortedCopy(
Multisets.intersection(set1, set2)),
new Predicate<Long>() {
public boolean apply(Long id) {
return // whether to filter this id
}
}), limit);
I'm sure you can find something in there which can do what you're looking for :-)
Going along with the other Guava examples, you can use Guava's MultiMaps:
final MultiMap<K, V> mmap = ArrayListMultiMap.create();
// do stuff.
final int limit = 10;
final MultiMap<K, V> mmapView =
MultiMaps.filterKeys(mmap, new Predicate<K>(){
public boolean apply(K k) {
return mmap.get(k).size() <= limit;
}
});
The MultiMaps.newListMultiMap method takes arguments you don't want to provide. You can't use MultiMaps.filterValues or .filterEntries here because those use the individual values, not the lists of values. On the other hand, mmap.get(k) never returns null. You cam, of course, use a static inner class that you pass mmap and limit to instead of using anonymous inner classes.
Alternatevely you can make a copy of the original map and iterate over the values removing those whose length is less than x.

How can I sort a map contains another map by numbers

I have a HashMap of type:
HashMap<String, HashMap<String, Integer>>
This HashMap has values like
{name, {dateasString, numberOfTasks}}
I want to sort this by numberOfTasks. I can't find the way to do that.
You can't sort a HashMap, but you can get a sorted Array (or a List) of
the keys. How you want to define your sort is up to you - just modify the
comparator (or the numberOfTasks function that it calls:)
So something like this?
public static void main() {
final
HashMap<String, HashMap<String, Integer>> map = new HashMap<String, HashMap<String, Integer>>();
String[] keys = (String[]) map.keySet().toArray();
Arrays.sort(keys, new Comparator<String>() {
#Override
public int compare(String k1, String k2) {
int v1 = numberOfTasks(map.get(k1));
int v2 = numberOfTasks(map.get(k2));
return Integer.valueOf(v1).compareTo(Integer.valueOf(v2));
}
});
// 'keys' is now sorted the way you want.
}
public static int numberOfTasks(HashMap<String, Integer> map) {
int max = 0;
for (Integer i : map.values()) {
if (i > max) max = i;
}
return max;
}
Create your custom Comparator that compares elemetns according to the sum of objects, and use Arrays.sort() or Collections.sort() after populating a list/array with the data:
//populate example data:
final Map<String,Map<String,Integer>> map = new HashMap<String, Map<String,Integer>>();
map.put("x", new HashMap<String, Integer>());
map.get("x").put("t1",1);
map.get("x").put("t2",1);
map.get("x").put("t3",1);
map.put("y", new HashMap<String, Integer>());
map.get("y").put("t1",2);
map.get("y").put("t2",2);
map.get("y").put("t3",2);
map.put("z", new HashMap<String, Integer>());
map.get("z").put("t1",3);
map.get("z").put("t2",3);
map.get("z").put("t3",3);
//populate the data in a list:
List<String> list = new ArrayList<String>(map.keySet());
//sort the data with a custom comparator:
Collections.sort(list, new Comparator<String>() {
private int getSum(String s) {
int sum = 0;
for (Integer x : map.get(s).values()) {
if (x != null) sum += x;
}
return sum;
}
public int compare(String o1, String o2) {
return new Integer(getSum(o1)).compareTo(new Integer(getSum(o2)));
}
});
System.out.println(list);
Note, to improve performance you could use a caching mechanism to avoid recalculating the sum of each element every time it is being compared.
Does the second HashMap have to be a HashMap? Can you convert the second HashMap into a class specifically for this? Perhaps something like this:
private class TaskList
{
String dateAsString;
int numTasks;
public TaskList(String dateAsString, int numTasks)
{
this.dateAsString = dateAsString;
this.numTasks = numTasks;
}
public getDateAsString()
{
return dateAsString;
}
public getNumTasks()
{
return numTasks;
}
}
Then you can say HashMap<String, TaskList> and access the number of tasks directly and sort them accordingly. Even then I don't think HashMap is right for this.
I doubt there is a way to do this in the Java APIs. You will either have to do it manually by extracting the data in a separate model and sort that or redesign the model such as it will allow sorting on that field easier.
You would have to convert the hashmap to an array. If it were possible to sort a hashmap it would completely defeat the purpose of using one in the first place.
hashMap.keySet().toArray(); // returns an array of keys
hashMap.values().toArray(); // returns an array of values
Arrays.sort(array); // sorts an array
Hope that helps.

Java HashMap: How to get a key and value by index?

I am trying to use a HashMap to map a unique string to a string ArrayList like this:
HashMap<String, ArrayList<String>>
Basically, I want to be able to access the keys by number, not by using the key's name. And I want to be able to access said key's value, to iterate over it. I'm imagining something like this:
for(all keys in my hashmap) {
for(int i=0; i < myhashmap.currentKey.getValue.size(); i++) {
// do things with the hashmaps elements
}
}
Is there an easy way to do this?
Here is the general solution if you really only want the first key's value
Object firstKey = myHashMap.keySet().toArray()[0];
Object valueForFirstKey = myHashMap.get(firstKey);
You can iterate over keys by calling map.keySet(), or iterate over the entries by calling map.entrySet(). Iterating over entries will probably be faster.
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
List<String> list = entry.getValue();
// Do things with the list
}
If you want to ensure that you iterate over the keys in the same order you inserted them then use a LinkedHashMap.
By the way, I'd recommend changing the declared type of the map to <String, List<String>>. Always best to declare types in terms of the interface rather than the implementation.
HashMaps are not ordered, unless you use a LinkedHashMap or SortedMap. In this case, you may want a LinkedHashMap. This will iterate in order of insertion (or in order of last access if you prefer). In this case, it would be
int index = 0;
for ( Map.Entry<String,ArrayList<String>> e : myHashMap.iterator().entrySet() ) {
String key = e.getKey();
ArrayList<String> val = e.getValue();
index++;
}
There is no direct get(index) in a map because it is an unordered list of key/value pairs. LinkedHashMap is a special case that keeps the order.
Kotlin HashMap Answer
You can get key by index. Then get value by key.
val item = HashMap<String, String>() // Dummy HashMap.
val keyByIndex = item.keys.elementAt(0) // Get key by index. I selected "0".
val valueOfElement = item.getValue(keyByIndex) // Get value.
You can do:
for(String key: hashMap.keySet()){
for(String value: hashMap.get(key)) {
// use the value here
}
}
This will iterate over every key, and then every value of the list associated with each key.
A solution is already selected. However, I post this solution for those who want to use an alternative approach:
// use LinkedHashMap if you want to read values from the hashmap in the same order as you put them into it
private ArrayList<String> getMapValueAt(LinkedHashMap<String, ArrayList<String>> hashMap, int index)
{
Map.Entry<String, ArrayList<String>> entry = (Map.Entry<String, ArrayList<String>>) hashMap.entrySet().toArray()[index];
return entry.getValue();
}
for (Object key : data.keySet()) {
String lKey = (String) key;
List<String> list = data.get(key);
}
I came across the same problem, read a couple of answers from different related questions and came up with my own class.
public class IndexableMap<K, V> extends HashMap<K, V> {
private LinkedList<K> keyList = new LinkedList<>();
#Override
public V put(K key, V value) {
if (!keyList.contains(key))
keyList.add(key);
return super.put(key, value);
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
#Override
public void clear() {
keyList.clear();
super.clear();
}
public List<K> getKeys() {
return keyList;
}
public int getKeyIndex(K key) {
return keyList.indexOf(key);
}
public K getKeyAt(int index) {
if (keyList.size() > index)
return keyList.get(index);
return null;
}
public V getValueAt(int index) {
K key = getKeyAt(index);
if (key != null)
return get(key);
return null;
}
}
Example (types are differing from OPs question just for clarity):
Map<String, Double> myMap = new IndexableMap<>();
List<String> keys = myMap.getKeys();
int keyIndex = myMap.getKeyIndex("keyString");
String key = myMap.getKeyAt(2);
Double value myMap.getValueAt(2);
Keep in mind that it does not override any of the complex methods, so you will need to do this on your own if you want to reliably access one of these.
Edit: I made a change to the putAll() method, because the old one had a rare chance to cause HashMap and LinkedList being in different states.
Try this:
myhashmap.entrySet()
.forEach{
println(it.getKey())
println(it.getValue())
}
or if you want by index
myhashmap.entrySet()[0].getKey()
myhashmap.entrySet()[0].getValue()
myhashmap.entrySet()[1].getKey()
myhashmap.entrySet()[1].getValue()
HashMaps don't keep your key/value pairs in a specific order. They are ordered based on the hash that each key's returns from its Object.hashCode() method. You can however iterate over the set of key/value pairs using an iterator with:
for (String key : hashmap.keySet())
{
for (list : hashmap.get(key))
{
//list.toString()
}
}
If you don't care about the actual key, a concise way to iterate over all the Map's values would be to use its values() method
Map<String, List<String>> myMap;
for ( List<String> stringList : myMap.values() ) {
for ( String myString : stringList ) {
// process the string here
}
}
The values() method is part of the Map interface and returns a Collection view of the values in the map.
You can use Kotlin extension function
fun LinkedHashMap<String, String>.getKeyByPosition(position: Int) =
this.keys.toTypedArray()[position]
fun LinkedHashMap<String, String>.getValueByPosition(position: Int) =
this.values.toTypedArray()[position]
You'll need to create multiple HashMaps like this for example
Map<String, String> fruitDetails = new HashMap();
fruitDetails.put("Mango", "Mango is a delicious fruit!");
fruitDetails.put("Guava" "Guava is a delicious fruit!");
fruitDetails.put("Pineapple", "Pineapple is a delicious fruit!");
Map<String, String> fruitDetails2 = new HashMap();
fruitDetails2.put("Orange", "Orange is a delicious fruit!");
fruitDetails2.put("Banana" "Banana is a delicious fruit!");
fruitDetails2.put("Apple", "Apple is a delicious fruit!");
// STEP 2: Create a numeric key based HashMap containing fruitDetails so we can access them by index
Map<Integer, Map<String, String>> hashMap = new HashMap();
hashMap.put(0, fruitDetails);
hashMap.put(1, fruitDetails2);
// Now we can successfully access the fruitDetails by index like this
String fruit1 = hashMap.get(0).get("Guava");
String fruit2 = hashMap.get(1).get("Apple");
System.out.println(fruit1); // outputs: Guava is a delicious fruit!
System.out.println(fruit2); // outputs: Apple is a delicious fruit!

Categories

Resources