I have a List<Map<String, Object>> that returns output as below.
[{ID:55, Item=6455, Quantity=3, Cost=150$},{ID:89, Item=0566, Quantity=2, Cost=30$},{ID:112, Item=5477, Quantity=1, Cost=50$},{ID:345, Item=6768, Quantity=10, Cost=280$}]
I'm trying to add key value pair "Size=large" at beginning of each Map<String, Object> of List. I used below code to add the key and value.
List<Map<String, Object> returnList = null;
returnList = jdbcTemplate.query(itemQuery, extractor);
Map<String,Object> map = new LinkedHashMap<>();
map.put("Size","large");
returnList.add(map);
System.out.println(returnList);
Right now I'm getting output as:
[{ID:55, Item=6455, Quantity=3, Cost=150$},{ID:89, Item=0566, Quantity=2, Cost=30$},{ID:112, Item=5477, Quantity=1, Cost=50$},{ID:345, Item=6768, Quantity=10, Cost=280$}, {Size=large}]
How can I get below output to add value for each Map of the list?
[{Size=large, ID:55, Item=6455, Quantity=3, Cost=150$},{Size=large, ID:89, Item=0566, Quantity=2, Cost=30$},{Size=large, ID:112, Item=5477, Quantity=1, Cost=50$},{Size=large, ID:345, Item=6768, Quantity=10, Cost=280$}]
// I was able to get answer for my question based on #Quadslab reply as below//
int size=returnList.size();
for(Map<String,Object> map : returnList) {
Map<String,Object> mapcopy = new HashMap<String, Object>();
for(Map<String,Object> entry: map.entrySet()){
mapcopy.put(entry.grtKey(),entry.getValue());
}
map.clear();
map.put("Size","large");
map.putAll(mapcopy);
}
for(Map<String,Object> map : returnList) {
map.put("Size","large");
}
This adds it to the end.
To add it at the beginning, there is a bit more work.
int size=returnList.size();
for(Map<String,Object> map : returnList) {
Map<String,Object> mapcopy = new LinkedHashMap<String,Object>(map);
map.clear();
map.put("Size","large");
map.putAll(mapcopy);
}
This code is basically from this answer.
Update the existing map, do not instantiate
Do not instantiate a new Map object, no need for your new LinkedHashMap<>(). You want to update your existing map, not replace the map so append an additional map.
You could use a for-each loop to modify each of the map objects in your list.
for( Map<String, Object> map , listOfMaps )
{
map.put( "Size" , "large" ) ;
}
Map order
You said:
at beginning of each Map<String, Object>
Your underlying implementation of Map may or may not support an order. To quote the Javadoc:
The order of a map is defined as the order in which the iterators on the map's collection views return their elements. Some map implementations, like the TreeMap class, make specific guarantees as to their order; others, like the HashMap class, do not.
Use a class
Your code is screaming out for a custom class. Java is a powerful object-oriented language, so use objects to represent this kind of data.
record
Java 16 brings a new feature, records, to more briefly define a class whose main purpose is to communicate data transparently and immutably. You simply declare the member fields. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString.
public record LineItem ( String size , int id , String item , int quantity , BigDecimal cost ) {}
Use like any class.
list.add(
new LineItem( "large" , 55 , "6455" , 3 , new BigDecimal( 150 ) ) ;
);
Related
Have a
List<Map<String, Object>> allPoints = new LinkedList<>();
Each map contains a "name" key with a String value;
Need to create a
List<Map<String, Object>> expectedPoints
There are duplicate names in the list; for these, want to keep the last one only.
E.g. if the list has three items, and first and third items both have"name" with value "abc", the resulting list should only contain the second and third items from the original list.
One way to do it is by using an auxiliary map:
Map<String, Map<String, Object>> map = new LinkedHashMap<>(allPoints.size(), 0.75f, true);
allPoints.forEach(point -> map.put((String)point.get("name"), point));
List<Map<String, Object>> expectedPoints = new ArrayList<>(map.values());
This works because Map.put either puts a new entry to the map or overwrites the value of an existing entry with the new one, thus keeping only the last point associated with the name.
I'm creating an access-ordered LinkedHashMap by using its overloaded constructor. This is to maintain the same order as in the allPoints list.
In case you have the constraint on one or more key-value pairs and flexible to use a Set, write your own Comparator and use descendingIterator on LinkedList and write to TreeSet. See code below:
LinkedList<Map<String, Object>> allPoints = new LinkedList<>();
Set<Map<String, Object>> expectedPoints = new TreeSet<>((objectMap1, objectMap2) ->
objectMap2.get("name").equals(objectMap1.get("name")) ? 0 : -1
);
allPoints.descendingIterator().forEachRemaining(expectedPoints::add);
Is there any fast and reliable way/approach to remove a set of entries based on an attribute on the entry's value. Below approach loops every entry which in undesirable.
e.g: ConcurrentHashMap - Entries will be in millions
Iterator<Map.Entry<String, POJO>> iterator = map.entrySet().iterator();
for (Iterator<Map.Entry<String, POJO>> it = iterator; it.hasNext(); ) {
Map.Entry<String, POJO> entry = it.next();
POJO value = entry.getValue();
if (value != null && *attribute*.equalsIgnoreCase(value.getAttribute())) {
it.remove();
}
}
There is no better way of mutating map in place without utilizing additional data structures. What you are basically asking for is secondary index like employed in databases, where you have pointers to entries based on some non-primary key properties. If you don't want to store extra index, there is no easier way then to iterate through all entries.
What I would suggest you to look into is composing map view over your original map. For example something like (using guava)
Map<String,POJO> smallerMap = Maps.filterValues(map,
v -> !attribute.equalsIgnoreCase(v.getAttribute())
);
You will need to be careful with such view (don't call size() on it for example), but for access etc it should be fine (depending on your exact needs, memory constraints etc).
And side note - please note I have removed null check for value. You cannot store null values in ConcurrentHashMap - and it is also not that great idea in normal map as well, better to remove entire key.
There are two solutions that I can think of
First solution:
Create another map for storing the object hashcode as key and corresponding key as value. The structure could be like as below
Map<Integer, String> map2 = new HashMap<>();
Here is working solution. Only drawback of this is getting a unique hashcode might be tough if there are large number of objects.
import java.util.HashMap;
import java.util.Map;
public class DualHashMap {
public static void main(String a[]){
Map<String, Pojo> map = new HashMap<>();
Map<Integer, String> map2 = new HashMap<>();
Pojo object1 = new Pojo();
Pojo object2 = new Pojo();
map.put( "key1", object1);
map.put( "key2", object2);
map2.put(object1.hashCode(), "key1");
map2.put(object2.hashCode(), "key2");
// Now let say you have to delete object1 you can do as follow
map.remove(map2.get(object1.hashCode()));
}
}
class Pojo{
#Override
public int hashCode(){
return super.hashCode(); //You must work on this yourself, and make sure hashcode is unique for each object
}
}
2nd solution:-
Use the solution provided for dual hashmap by Guava or Apache
The Apache Commons class you need is BidiMap.
Here is another for you by Guava
Here is what I'm trying to do.
Map<String, List<Address>> mapObj = someService.getSomeAddress();
Using above call I'm getting mapObj of type Map<String, List<Address>>
And I want to send mapObj as a parameter in a another method (that I cannot change) as a LinkedHashMap<String, List<LinkedHashMap>> which does some further processing.
Is there any way that I can solve this problem without affecting data inside mapObj?
You will need to perform a couple of conversion steps.
Convert all Address objects to LinkedHashMap objects.
Put all Map entries into a LinkedHashMap object.
For 1st one, you can write a utility method some where that can do this conversion. For example,
public static List<LinkedHashMap> addresstoMap(List<Address> addresses)
{
List<LinkedHashMap> list = new ArrayList<>();
for(Address a: addresses){
LinkedHashMap map = new LinkedHashMap();
// Add address fields to map here
list.add(map);
}
return list;
}
Then, for the 2nd step, you can do this:
LinkedHashMap<String, List<LinkedHashMap>> map = new LinkedHashMap<?,?>();
iterate through the entry sets of mapObj and put them into the above map object.
for (Map.Entry<String, List<Address>> e : m.entrySet()) {
map.put(e.getKey(), addresstoMap(e.getValue()));
}
The final map object above will contain the correct representation in the LinkedHashMap<String, List<LinkedHashMap>> datatype.
Hope this helps!
I want to store a key,values pair that allows me to get the values by its position (ordering must persist) or by its key name. I was thinking to use HashMap but i do not want to iterate through all values to get the value by index.
I would need something like this:
MyCollection<String, Object> objects = new MyCollection<String, Object>();
objects.put ("id", Object1);
objects.put ("name", Object2);
// And now access the values by index:
Object obj1 = objects.get(1);
// or by key name:
Object obj2 = objects.getByKeyName("name");
What is the best collection to use (it must be android < 11 compatible>
You can use LinkedHashMap. It maintains the inserted order. You cannot get by index as they all implement the Map interface. You can do this way-
public List<String> getByIndex(final LinkedHashMap<String, List<String>> myMap, final int index){
return (List<String>)myMap.values().toArray()[index];
}
I am calling a service and I get the XML response in the below format.
How do I retrieve multiple values under a single key from this response?
I want to store all the values in a List<String>
<p700:item xmlns:p700="http://abc.test.com">
<p700:key xsi:type="xsd:string">Key1</p700:key>
<p700:value xsi:type="xsd:string">Value1</p700:value>
<p700:value xsi:type="xsd:string">Value2</p700:value>
<p700:value xsi:type="xsd:string">Value3</p700:value>
<p700:value xsi:type="xsd:string">Value14</p700:value>
</p700:item>
<p700:item xmlns:p700="http://abc.test.com">
<p700:key xsi:type="xsd:string">Key1</p700:key>
<p700:value xsi:type="xsd:string">Value1</p700:value>
<p700:value xsi:type="xsd:string">Value2</p700:value>
</p700:item>
Create a map String <-> List<String>:
Map<String, List<String>> map = new HashMap<...>();
Use this code to add values:
List<String> values = map.get( key );
if( null == values ) {
values = new ArrayList<String>();
map.put( key, values );
}
values.add( "xxx" );
Guava has a Multimap interface and a ListMultimap implementation, which is a map of keys to multiple values contained in a list structure (as opposed to a set or sorted set). It's essentially a Map<K, Collection<V>>. You can also find examples here. As for actually parsing the XML, there are a number of questions about that here on SO, and you can start with this one.
You can iterate over the map and add them manually to a list if you don't want to make the Map<String, List<String>> mentioned in the comments under your question.
List<String> theList = new ArrayList<String>();
String theKey = "The Key";
for(Map.Entry<String, String> entry : map.entrySet()) {
if(entry.getKey().equals(theKey)) {
theList.add(entry.getValue());
}
}
This of course assumes you've already extracted the data from the XLS into a Map<String, String>. If you haven't, that's another matter entirely.
If you need a package to ingest your XLS, consider JXLS.