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.
Related
I am using io.quarkus.redis.client.RedisClient which inside use Vert.x for a Redis Client in Java. When I use HSCAN method it return a list of key and values in differents rows like this:
The keys are 0,2,4... and the values are the JSONs. Is there a way to obtain a Map<key,value> instead of a list with a key and values mix in a elegant/clean way?
You can do it with a simple for
Map<String, String> result = new HashMap<>();
for (int i = 0; i < source.length; i+=2) {
result.put(source[i], source[i + 1]);
}
In Kotlin you can use a more elegant solution but I think this one works
You will need to iterate over the Response in order to get the map.
Here is an example for Map<String, String> with for loop:
Map<String, String> map = new HashMap<>();
for (String key : results.getKeys()) {
map.put(key, results.get(key).toString());
}
Here is the same example but using java lambdas:
Map<String, String> map = result.getKeys().stream()
.collect(Collectors.toMap(key -> key, key -> result.get(key).toString()));
For your case with json you can just change the transformation function from .toString() to something that suits your need.
Edit 1:
As HSCAN returns array as defined:
return a two elements multi-bulk reply, where the first element is a
string representing an unsigned 64 bit number (the cursor), and the
second element is a multi-bulk with an array of elements.
There is not a simple solution to create a map but this is what I recommend:
Iterator<Response> iterator = response.get(1).stream().iterator();
Map<String, String> map = new HashMap<>();
while (iterator.hasNext()) {
map.put(iterator.next().toString(), iterator.next().toString());
}
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 ) ) ;
);
I am making something like tags using Java collections. I made a map using list as a value.
Can I get a key searching by words from list? How I can do that?
Map<String, List<String>> map = new HashMap<String, List<String>>();
List<String> list1 = new ArrayList<String>();
List<String> list2 = new ArrayList<String>();
list1.add("mammal");
list1.add("cute");
list2.add("mammal");
list2.add("big");
map.put("cat", list1);
map.put("dog", list2);
If I understand you correctly, you want to obtain the key given one of the value in the list stored as the corresponding value? Of course, you can always get all these lists using the values() method of the Map interface and then iterate over those. However, how about having a second map where you use your tags as keys and store a list of all the entries carrying this tag? For large data sets, this will probably perform better.
for (Entry<String, List<String>> entry : map.entrySet()) {
if (entry.getValue().contains(animalYouSearch)) {
System.out.println(animalYouSearch + " is in " + entry.getKey());
}
}
Output if you search for "mammal":
mammal is in cat
mammal is in dog
There is no 'magic' way for it, you need to search inside the values and then report the correct key.
For example:
String str = "cute";
List<String> matchingKeys = map.entrySet().stream().filter( e -> e.getValue().contains(str))
.map(e -> e.getKey()).collect(Collectors.toList());
But you probably want to store your data other way arround, the list of "features" being the key, and the value the animal name.
If you want to retrieve set of tags, use this method:
public Set<String> findMatchingKeys(Map<String, List<String>> map, String word){
Set<String> tags = new HashSet<String>();
for(Map.Entry<String,List<String> > mapEntry : map.entrySet()){
if(mapEntry.getValue().contains(word))
tags.add(mapEntry.getKey());
}
return tags;
}
I have a multi-line value that needs to be processed. I'm using the map.get() method to retrieve that value but it seems to be getting only the last line value.
Here's my code:
map = new LinkedHashMap();
updateMap("BUG", parser, map, bugRec);
map.put(nextBuildIdTagName, nextBuildId); // putting the new value in
String value = (String)map.get(nextBuildIdTagName); // This is where it is not working
nextBuildIdTagName already has a value, and the new value gets inserted as a new line. I need to be able to retrieve the existing value as well as the new value.
So from what I understand you want to store multiple values in the map under single key. Easiest way to do it (without using any external lib with multimap impl) is to create a map of lists like this:
Map<String, List<String>> map = new HashMap<>();
then when you add to map you can do sth like this:
if(!map.containsKey(nextBuildIdTagName)) {
map.put(nextBuildIdTagName, new ArrayList<>());
}
map.get(nextBuildIdTagName).add(nextBuildId);
then to get all the items and iterate over them
for(String value : map.get(nextBuildIdTagName)) {
// do sth to each line
}
In the javadoc for HashMap (of which LinkedHashMap is a subclass), it is clearly stated that: "If the map previously contained a mapping for the key, the old value is replaced". To store multiple values (the lines) for the same key, you'd need to do something like:
Map<String, List<String>> map = new Linked HashMap<>();
And the add to the list (which is the map value) the needes lines. Note: here I assumed your key to be a String, change its type as needed.
Source: http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html#put(K,%20V)
I have two hashmaps, in particular vocabs of two languages say english and german.I would like to concatenate both these map to return a single map.I tried :
hashmap.putall()
But, removed some of the entries which are common in both maps and replace it by single entry only.But i want to keep both the vocabs intact just concatenate those. Is there any method to do it? if not any other way to do. I would prefer any methods in hashmap.
[EDIT]
To make more clear, lets see two maps
at the 500 um die 500
0 1 2 0 1 2
resutls into
at the 500 um die 500
0 1 2 3 4 5
You'll have to write your own custom "putAll()` method then. Something like this would work:
HashMap<String> both = new HashMap<String>(english);
for(String key : german.keySet()) {
if(english.containsKey(key)) {
both.put(key, english.get(key)+german.get(key));
}
}
This first copies the English HashMap. Then puts in all the German words, concatenating if there is a duplicate key. You might want some kind of separator character like a / in between so you can later extract the two.
There isn't anything like that in the Java main library itself, you will have to use something provided by third parties like Google Guava's Multimap, it does exactly what you want, or build something like this manually.
You can download the Guava library at the project's website. Using a multimap is the same as using a map, as in:
Multimap<String,String> both = new ArrayListMultimap <String,String>();
both.putAll( german );
both.putAll( english);
for ( Entry<String,String> entry : both.entrySet() ) {
System.out.printf( "%s -> %s%n", entry.getKey(), entry.getValue() );
}
This code will print all key-value pairs including the ones that are present on both maps. So, if you have me->me at both german and english they would be printed twice.
You cannot do that directly with any Map implementation, since in a map, each key is unique.
A possible workaround is to use Map<Key, List<Value>>, and then do the concatenation of your maps manually. The advantage of using a List for the concatenated map, is that it will be easy to retrieve each of the individual values without any extra fiddling.
Something like that would work:
public Map<Key, List<Value>> concat(Map<Key, Value> first, Map<Key, Value> second){
Map<Key, List<Value>> concat = new HashMap<Key, List<Value>>();
putMulti(first, concat);
putMulti(second, concat);
return concat;
}
private void putMulti(Map<Key, Value> content, Map<Key, List<Value>> dest){
for(Map.Entry<Key, Value> entry : content){
List<Value> vals = dest.get(entry.getKey());
if(vals == null){
vals = new ArrayList<Value>();
dest.put(entry.getKey(), vals);
}
vals.add(entry.getValue());
}
}
Similar to #tskuzzy's answer
Map<String, String> both = new HashMap<String, String>();
both.putAll(german);
both.putAll(english);
for (String e : english.keySet())
if (german.containsKey(e))
both.put(e, english.get(e) + german.get(e));
Slight improvisation of #tskuzzy and #Peter's answer here. Just define your own StrangeHashMap by extending HashMap.
public class StrangeHashMap extends HashMap<String, String> {
#Override
public String put(String key, String value) {
if(this.containsKey(key)) {
return super.put(key, super.get(key) + value);
} else {
return super.put(key, value);
}
}
}
You can use it as so:
Map<String, String> map1 = new HashMap<String, String>();
map1.put("key1", "Value1");
map1.put("key2", "Value2");
Map<String, String> map2 = new HashMap<String, String>();
map2.put("key1", "Value2");
map2.put("key3", "Value3");
Map<String, String> all = new StrangeHashMap();
all.putAll(map1);
all.putAll(map2);
System.out.println(all);
The above prints the below for me:
{key3=Value3, key2=Value2, key1=Value1Value2}
Given the new elements in the question, it seems that what you actually need to use is lists. In this case, you can just do:
List<String> english = ...;
List<String> german = ...;
List<String> concat = new ArrayList<String>(english.size() + german.size());
concat.addAll(english);
concat.addAll(german);
And there you are. You can still use concat.get(n) to retreive the value nth value in the concatenated list.