Concatenating two hashmaps without removing common entires from both the maps - java

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.

Related

How to map HSCAN response to Map<key, value> using Vert.x Redis

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

How to put together multiple Maps <Character, Set<String>> without overriding Sets

In my project I am using two maps Map<Character, Set<String>>.
map1 - is temporally holding needed values
map2 - is summing all data from map1 after each loop
for example i got:
map2 = (B; Beryllium, Boron, Bromine)
map2 = (H; Hellum, Hydrogen, Hafnium)
now new map1 is:
map1 = (B; Bismuth)
map1 = (O; Oxygen)
In my code adding Oxygen as new entry is ok, but adding new entry for B ends by overraidding existing data in values and leave me only Bismuth.
My code:
while (iterator.hasNext()) {
Set<String> words = new TreeSet<>();
String word = iterator.next();
char[] wordChars = word.toCharArray();
//some code
words.add(word);
map1.put(wordChars[i], words);
}
map2.putAll(map1);
I tought about using .merge but I have no idea how to use it with Sets as values, and I cannot use simple Strings with concat.
You can use Map#merge like this:
Map<String, Set<String>> map1; // [key="B";values=["Beryllium", "Boron", "Bromine"]]
Map<String, Set<String>> map2; // [key="B";values=["Bismuth"] key="I";values=["Iron"]]
for (Entry<String, Set<String>> entry : map2.entrySet()) {
map1.merge(entry.getKey(), entry.getValue(), (s1, s2) -> {s1.addAll(s2); return s1;});
}
//map1 = [key="B";values=["Beryllium", "Boron", "Bromine", "Bismuth"] key="I";values=["Iron"]]
Map::compute is probably what you're looking for. This gives you a way to map any existing value (if there is one), or provide one if not.
For example, in your case something like the following would probably suffice:
oldMap.compute("B", current -> {
if (current == null) {
// No existing entry, so use newMap's one
return newMap.get("B");
} else {
// There was an existing value, so combine the Sets
final Set<String> newValue = new HashSet<>(current);
newValue.addAll(newMap.get("B"));
return newValue;
}
});
There's also MultiValueMap and Multimap from spring and guava respectively (if you're ok bringing in dependencies) which cover this case with less work already.
Temporary map1 will not be needed in this case. Get the set for that character, if null create a new set. Add the word to that set and put in the map:
while (iterator.hasNext()) {
String word = iterator.next();
//some code
Set<String> words = map2.get(word.charAt(0));
if(words == null) {
words = new TreeSet<>();
}
words.add(word);
map2.put(word.charAt(0), words);
}
When using the merge() function, if the specified key is not already associated with a value or the value is null, it associates the key with the given value.
Otherwise, i.e if the key is associated with a value, it replaces the value with the results of the given remapping function. So in order to do not overwrite the old value you must write your remapping function so that it combines the old and new values.
To do so replace this line :
map2.putAll(map1);
with
map1.forEach( (key, value)->{
map2.merge(key, value, (value1,value2) -> Stream.of(value1,value2)
.flatMap(Set::stream)
.collect(Collectors.toSet()));
});
This will iterate over map1 and add echh key which is not present into map2 and associate it with the given value and for each key which is already present it combines the old values and new values.
Alternative you can also work with Map.computeIfPresent and Map.putIfAbsent
map1.forEach( (key, value)->{
map2.computeIfPresent(key, (k,v) -> Stream.of(v,value).flatMap(Set::stream).collect(Collectors.toSet()));
map2.putIfAbsent(key, value);
});

Convert ArrayList to HashMap<String, String>

I have this ArrayList
public ArrayList<HashMap<String, String>> xmlFileNames = new ArrayList<>();
and I want to convert this to:
HashMap<String, String> comparemap2 = new HashMap<>();
What I want is: I want all the Items inside the ArrayList and want to put them into the HashMap
My HashMap looks like:
KEY VALUE
job_id 032014091029309130921.xml
job_id 201302149014021492929.xml
job_id 203921904901920952099.xml
EDIT:
Later I want to compare this map with an existing map:
Properties properties = new Properties();
try {
properties.load(openFileInput("comparexml.kx_todo"));
} catch (IOException e) {
e.printStackTrace();
}
for (String key : properties.stringPropertyNames()) {
compareMap.put(key, properties.get(key).toString());
}
HashMap<String, String> oldCompareMap = new HashMap<>();
for (HashMap key : xmlFileNames) {
oldCompareMap.putAll(key);
}
isEqualMaps(oldCompareMap, compareMap);
I only want to compare, if the filename exists in the compareMap. If not, than add it to the xmlFileName Map
I've looked up in StackOverFlow, how I can convert ArrayList to HashMap. But the other Threads treat data types like Item or Product.
I hope you can help me!
Kind Regards
Given...
public ArrayList<HashMap<String, String>> xmlFileNames = new ArrayList<>();
then something like this should do it.
HashMap<String, String> nhm = new HashMap<>();
for (HashMap xmlFileHm : xmlFileNames ) {
nhm.putAll(xmlFileHm);
}
but be aware if you have duplicate keys in your hashmaps they will get overwritten.
You should also think about coding to interfaces. Take a look at Map and List rather than typing your collections to implementations (ArrayList and HashMap). Take a look at this thread which is quite interesting What does it mean to "program to an interface"?
Depending on what you are trying to do as well you might consider a MultiMap as this might server your purposes better
Edit After update to the question...
A multimap would be better here with one key and multiple values. Although arguably if the key never changes then you could just store the values in a list. For multiamps you can use Google's guava library or do one yourself. For example (not checked for compilation errors as Im doing this from my head)
Map<String, List<String>> m = new HashMap<>();
if (m.containsKey("key")) {
m.get("key").add("new value");
}
else {
List<String> l = new ArrayList<>();
l.add("new value");
m.put("key", l);
}
You can create a new HashMap, then iterate through the list and put all elements from the map from the list to the main map.
List<Map<String, String>> list = new ArrayList<>();
Map<String, String> map = new HashMap<>();
for (Map<String, String> mapFromList : list) {
map.putAll(mapFromList);
}
You can try something like this..
ArrayList<HashMap<String, String>> xmlFileNames = new ArrayList<>();
HashMap<String, String> comparemap2 = new HashMap<>();
for(HashMap<String, String> i:xmlFileNames){
comparemap2.putAll(i);
}
You may need to consider the case of duplicate keys. else they will get override.
Create a new map and put All each element of arrayList to the map.
But in that case if you have same keys in two element of arrayList (hashmap) then it will override the previous one.

How to sort a Map in Java by its String keys which are numeric

I have created a map called result.
In the sortByKeys method as my keys are String with Numeric values, I have converted them to Integer key type Map then sorted them.
The sorting is working fine when I am looping and printing individually, but not when I am setting them in another Map.
public class TestDate {
public static void main (String args[]){
Map<String, String> result = new HashMap<String, String>();
result.put("error", "10");
result.put("1","hii");
result.put("Update","herii");
result.put("insert","insert");
result.put("10","hiiuu");
result.put("7","hii");
result.put("21","hii");
result.put("15","hii");
Map<String, String> sorted = sortByKeys(result);
//System.out.println(sorted);
}
private static Map<String, String> sortByKeys(Map<String, String> map) {
Map <Integer,String> unSorted = new HashMap<Integer, String>();
Map <String,String> sorted = new HashMap<String, String>();
for (Map.Entry<String, String> entry : map.entrySet())
{
try{
int foo = Integer.parseInt(entry.getKey());
unSorted.put(foo, entry.getValue());
}catch (Exception e){
}
}
Map<Integer, String> newMap = new TreeMap<Integer, String>(unSorted);
Set set = newMap.entrySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()) {
Map.Entry me = (Map.Entry)iterator.next();
System.out.println(me.getKey());
System.out.println(me.getValue());
sorted.put(me.getKey().toString(), me.getValue().toString());
}
System.out.println(sorted);
return null;
}
}
Here is the o/p :
1
hii
7
hii
10
hiiuu
15
hii
21
hii
{21=hii, 10=hiiuu, 1=hii, 7=hii, 15=hii}
If you don't need the last inch of performance, you can solve this rather directly, without an extra step to sort the map, by using SortedMap:
Map<String,String> result = new TreeMap<>(Comparator.comparingInt(Integer::parseInt));
If you are among the unfortunate bunch who are still being denied access to Java 8, you'll have to implement the Comparator in long-hand:
new TreeMap<>(new Comparator<String,String> { public int compare(String a, String b) {
return Integer.compare(Integer.parseInt(a), Integer.parseInt(b));
}});
The above approach works only under the assumption that all keys are parseable integers. If that is not the case, then you won't be able to use the SortedMap directly, but transform your original map into it, filtering out the unparseable keys.
It's because the Map you're putting them into is a HashMap, which isn't sorted. There's no guarantee of the ordering of results you'll get out of the HashMap, even if you put them in in the right order.
(And calling it sorted doesn't change anything :) )
You print 2 different maps and not the same: you iterate over and print the entries of newMap map, and at the end you print sorted map.
You see the sorted entries printed because you iterate over your sorted newMap.
Then you print the sorted map which is unsorted (despite by its name). You print a different map instance.
Print this:
System.out.println(newMap); // This is the instance of the sorted "TreeMap"

Hashmap - single key, multiple values - How to retrieve?

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.

Categories

Resources