Improper output while filtering a hashmap - java

I have a hashmap which has key and value in String. The data is in the form of (table1, "table1:ssn1,ssn2,ssn3"). The table name in key is of the source table name and table name in the value is of the destination table name along with the corresponding source systems names separated by a ":".
I am trying to pass source system names in arguments from the command line to filter out the key and value along with the received source system name.
I came up with the following code so far:
public class FilterKeyValues {
public static void main(String[] args) {
String[] valArr;
String ky;
Map<String, String> hmap = new HashMap<String, String>();
Map<String, String> filtered = new HashMap<String, String>();
hmap.put("Table1", "Table1:SSN1,SSN2,SSN3,SSN4,SSN5");
hmap.put("Table2", "Table2:SSN1,SSN4,SSN2,SSN5,SSN8,SSN9,SSN10");
hmap.put("Table3", "Table3:SSN4,SSN1");
hmap.put("Table4", "Table4:SSN5,SSN6,SSN7");
hmap.put("Table5", "Table5:SSN8,SSN1,SSN5,SSN2");
if(args.length > 0) {
for(String ssname: args) {
for (Entry<String, String> entry : hmap.entrySet()) {
if (entry.getValue().contains(ssname)) {
ky = entry.getKey();
valArr = entry.getValue().split(":");
filtered.put(ky, valArr[0]+":"+ssname);
}
}
}
}
for (String iter: filtered.keySet()){
String key = iter.toString();
String value = filtered.get(key).toString();
System.out.println(key + "->" + value);
}
}
}
In the arguments, I am passing: SSN1 SSN2 in the arguments. The output should be
Table1->Table1:SSN1
Table2->Table2:SSN1
Table3->Table3:SSN1
Table5->Table5:SSN1
Table1->Table1:SSN2
Table2->Table2:SSN2
Table5->Table5:SSN2
Instead, I am getting the output of:
Table2->Table2:SSN2
Table3->Table3:SSN1
Table5->Table5:SSN2
Table1->Table1:SSN2
Could anyone let me know what is the mistake I am doing here ?

You are trying to put multiple values into a Map using the same key. For each key, a map can only ever hold one value.
Therefore, only the last value that you add for any given key will be visible in the end.
Generally speaking your code suggests that Strings are not the correct data type for your data and that you should be storing it in a more structured form (such as a Map<String,List<String>>).

As i have observed, key is included in value.. so you can use a simple arraylist fro targeted data.. this may help you
public static void main(String[] args) {
String[] valArr;
String ky;
Map<String, String> hmap = new HashMap<String, String>();
List<String> filtered = new ArrayList<String>();
hmap.put("Table1", "Table1:SSN1,SSN2,SSN3,SSN4,SSN5");
hmap.put("Table2", "Table2:SSN1,SSN4,SSN2,SSN5,SSN8,SSN9,SSN10");
hmap.put("Table3", "Table3:SSN4,SSN1");
hmap.put("Table4", "Table4:SSN5,SSN6,SSN7");
hmap.put("Table5", "Table5:SSN8,SSN1,SSN5,SSN2");
if(args.length > 0) {
for(String ssname: args) {
for (Entry<String, String> entry : hmap.entrySet()) {
if (entry.getValue().contains(ssname)) {
ky = entry.getKey();
valArr = entry.getValue().split(":");
filtered.add(valArr[0]+":"+ssname);
}
}
}
}
for (String iter: filtered){
System.out.println(iter.split(":")[0]+ "->" + iter);
}
}

You need to iterate over the items:
valArr = entry.getValue().substring(entry.getValue().indexOf(":")).split(",");
Here is complete code which produces the output you desire:
public static void main(String[] args) {
String[] valArr;
String ky;
Map<String, String> hmap = new HashMap<String, String>();
Map<String, String> filtered = new HashMap<String, String>();
hmap.put("Table1", "Table1:SSN1,SSN2,SSN3,SSN4,SSN5");
hmap.put("Table2", "Table2:SSN1,SSN4,SSN2,SSN5,SSN8,SSN9,SSN10");
hmap.put("Table3", "Table3:SSN4,SSN1");
hmap.put("Table4", "Table4:SSN5,SSN6,SSN7");
hmap.put("Table5", "Table5:SSN8,SSN1,SSN5,SSN2");
if(args.length > 0) {
for(String ssname: args) {
for (Entry<String, String> entry : hmap.entrySet()) {
if (entry.getValue().contains(ssname)) {
ky = entry.getKey();
valArr = entry.getValue().substring(entry.getValue().indexOf(":")).split(",");
for (String val : valArr) {
if (val.equals(ssname)) {
filtered.put(ky, ky+":"+ssname);
}
}
}
}
}
}
for (String iter: filtered.keySet()){
String key = iter.toString();
String value = filtered.get(key).toString();
System.out.println(key + "->" + value);
}
}

Related

Usng StringJoiner in complex HashMaps

I have a list of Maps as below:
List<Map<String,Object>> someObjectsList = new ArrayList<Map<String,Object>>();
I am storing the following data in each HashMap
key value
2017-07-21 2017-07-21-07.33.28.429340
2017-07-24 2017-07-24-01.23.33.591340
2017-07-24 2017-07-24-01.23.33.492340
2017-07-21 2017-07-21-07.33.28.429540
I want to iterate through the list of HashMaps and check if the key matches with the first 10 characters of any of the HashMap value, then I want to store those keys and values in the following format. i.e. by using the telemeter 'comma'. The ultimate aim is to group the unique keys of the HashMaps and their relative values (if the key matches with the first 10 characters of any of the HashMap value) in a new HashMap.
key value
2017-07-21 2017-07-21-07.33.28.429340,2017-07-21-07.33.28.429540
2017-07-24 2017-07-24-01.23.33.591340,2017-07-24-01.23.33.492340
I am trying with following java code using StringJoiner, but not getting the results as expected. Any clue on how to frame the logic here?
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
public class SampleOne {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Map<String, Object>> someObjectsList = new ArrayList<Map<String, Object>>();
Map<String, Object> mapOne = new HashMap<String, Object>();
mapOne.put("2017-07-21", "2017-07-21-07.33.28.429340");
Map<String, Object> mapTwo = new HashMap<String, Object>();
mapTwo.put("2017-07-24", "2017-07-24-01.23.33.591340");
Map<String, Object> mapThree = new HashMap<String, Object>();
mapThree.put("2017-07-24", "2017-07-24-01.23.33.492340");
Map<String, Object> mapFour = new HashMap<String, Object>();
mapFour.put("2017-07-21", "2017-07-21-07.33.28.429540");
someObjectsList.add(mapOne);
someObjectsList.add(mapTwo);
someObjectsList.add(mapThree);
someObjectsList.add(mapFour);
for (Map map : someObjectsList) {
StringJoiner sj = new StringJoiner(",");
for (Object key : map.keySet()) {
String value = ((String) map.get(key));
String date = value.substring(0, Math.min(value.length(), 10));
//System.out.println(str);
//System.out.println(value);
if(key.equals(date)) {
sj.add(value);
System.out.println(sj.toString());
}
}
}
}
}
output:
2017-07-21-07.33.28.429340
2017-07-24-01.23.33.591340
2017-07-24-01.23.33.492340
2017-07-21-07.33.28.429540
Make use of the .merge function:
Map<String, Object> finalMap = new HashMap<String, Object>();
for (Map map : someObjectsList) {
for (Object key : map.keySet()) {
String value = ((String) map.get(key));
finalMap.merge((String) key, value, (k, v) -> k + "," + v);
}
}
which outputs:
{2017-07-21=2017-07-21-07.33.28.429340,2017-07-21-07.33.28.429540,
2017-07-24=2017-07-24-01.23.33.591340,2017-07-24-01.23.33.492340}
The same can be achieved by the following one-liner:
someObjectsList.stream()
.flatMap(i -> i.entrySet().stream())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue,
(k, v) -> k + "," + v));
On your code, you are using different StringJoiner on each map. So, it's creating a new instance of it.
You can save your keys on a map. An example code:
(Edit: I did not remove your StringJoiner part.)
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Map<String, Object>> someObjectsList = new ArrayList<Map<String, Object>>();
Map<String, Object> mapOne = new HashMap<String, Object>();
mapOne.put("2017-07-21", "2017-07-21-07.33.28.429340");
Map<String, Object> mapTwo = new HashMap<String, Object>();
mapTwo.put("2017-07-24", "2017-07-24-01.23.33.591340");
Map<String, Object> mapThree = new HashMap<String, Object>();
mapThree.put("2017-07-24", "2017-07-24-01.23.33.492340");
Map<String, Object> mapFour = new HashMap<String, Object>();
mapFour.put("2017-07-21", "2017-07-21-07.33.28.429540");
someObjectsList.add(mapOne);
someObjectsList.add(mapTwo);
someObjectsList.add(mapThree);
someObjectsList.add(mapFour);
Map<String, Object> outputMap = new HashMap<String, Object>();
for (Map map : someObjectsList) {
StringJoiner sj = new StringJoiner(",");
for (Object key : map.keySet()) {
String value = ((String) map.get(key));
String date = value.substring(0, Math.min(value.length(), 10));
//System.out.println(str);
//System.out.println(value);
if(key.equals(date)) {
sj.add(value);
System.out.println(sj.toString());
if(outputMap.containsKey(key)) {
String str = (String) map.get(key);
str = str + "," + value;
outputMap.put((String)key, str);
} else {
outputMap.put((String)key, value);
}
}
}
}
for (String map : outputMap.keySet()) {
System.out.println(map + " " + outputMap.get(map));
}
}
You are looking for the grouping behavior of processing a List. You can use the advantage of java-stream since java-8. In any case, you need a new Map to store the values in order to print them. :
someObjectsList.stream()
.flatMap(i -> i.entrySet().stream()) // flatmapping to entries
.collect(Collectors.groupingBy(Entry::getKey)) // grouping them using the key
In case you want to use for-loops. In this case it is harder since the more entries might appear in each List item:
final Map<String, List<Object>> map = new HashMap<>();
for (Map<String, Object> m: someObjectsList) { // iterate List<Map>
for (Entry<String, Object> entry: m.entrySet()) { // iterate entries of each Map
List<Object> list;
final String key = entry.getKey(); // key of the entry
final Object value = entry.getValue(); // value of the entry
if (map.containsKey(key)) { // if the key exists
list = map.get(key); // ... use it
} else {
list = new ArrayList<>(); // ... or else create a new one
}
list.add(value); // add the new value
map.put(key, list); // and add/update the entry
}
}
Printing out of Map<String, List<Object>> map in both cased will produce the following output:
2017-07-21=[2017-07-21-07.33.28.429340, 2017-07-21-07.33.28.429540],
2017-07-24=[2017-07-24-01.23.33.591340, 2017-07-24-01.23.33.492340]
Any reason you're using Object over String and avoiding safety checks? That said, it's not "the first 10 characters", you want to see if value starts with key full-stop (all your keys are 10 characters). So in that case you can just do if (value.startsWith(key)) { ... }. Don't forget your newlines if the stringjoiner wasn't full. Lastly, you don't need a List, a Map can hold multiple keys at once. An alternative way of doing it:
//LinkedHashMap will preserve our insertion order
Map<String, String> map = new LinkedHashMap<>();
map.put("2017-07-21", "2017-07-21-07.33.28.429340");
map.put("2017-07-24", "2017-07-24-01.23.33.591340");
//note duplicates are overwritten, but no value change here
map.put("2017-07-24", "2017-07-24-01.23.33.492340");
map.put("2017-07-21", "2017-07-21-07.33.28.429540");
// You can also use Java 8 streams for the concatenation
// but I left it simple
List<String> matches = map.entrySet()
.filter(e -> e.getValue().startsWith(e.getKey())
.collect(Collectors.toList());
String concatenated = String.join("\n", matches);
If you wanted to generate that string without streams, it would look like this (again, not using #entrySet for simplicity, but it would be more efficient here):
List<String> matches = new ArrayList<>();
StringJoiner joiner = new StringJoiner("\n");
for (String key : map.keySet()) {
String value = map.get(key);
if (value.startsWith(key)) {
joiner.add(value);
}
}
//joiner#toString will give the expected result

Iterate over ArrayList of ArrayList of Map

I use SimpleExpandableListAdapter to create ExpandableListView for my application. I want to know better how to work with lists and maps and what they are in practice.
//collection for elements of a single group;
ArrayList<Map<String, String>> childDataItem;
//general collection for collections of elements
ArrayList<ArrayList<Map<String, String>>> childData;
Map<String, String> m;
I know how to iterate over ArrayList of Maps, it is not a problem for me, but I got stuck.
childData = new ArrayList<>();
childDataItem = new ArrayList<>();
for (String phone : phonesHTC) {
m = new HashMap<>();
m.put("phoneName", phone);
childDataItem.add(m);
}
childData.add(childDataItem);
childDataItem = new ArrayList<>();
for (String phone : phonesSams) {
m = new HashMap<String, String>();
m.put("phoneName", phone);
childDataItem.add(m);
}
childData.add(childDataItem);
// создаем коллекцию элементов для третьей группы
childDataItem = new ArrayList<>();
for (String phone : phonesLG) {
m = new HashMap<String, String>();
m.put("phoneName", phone);
childDataItem.add(m);
}
childData.add(childDataItem);
And I want to Log what childData contains (<ArrayList<Map<String, String>>), but I don't sure that I did that right. ( 2nd loop is a simple ArrayList of Map iteration)
for (ArrayList<Map<String, String>> outerEntry : childData) {
for(Map<String, String> i:outerEntry ) {
for (String key1 : i.keySet()) {
String value1 = i.get(key1);
Log.d("MyLogs", "(childData)value1 = " + value1);
Log.d("MyLogs", "(childData)key = " + key1);
}
}
for (Map<String, String> innerEntry : childDataItem) {
for (String key : innerEntry.keySet()) {
String value = innerEntry.get(key);
Log.d("MyLogs", "(childDataItem)key = " + key);
Log.d("MyLogs", "(childDataItem)value = " + value);
}
}
}
If you want to log all the elements for childData then there is no need for the last loop, you are already fetching them in the first loop. Please remove below code from the program and it will log all items of childData.
for (Map<String, String> innerEntry : childDataItem) {
for (String key : innerEntry.keySet()) {
String value = innerEntry.get(key);
Log.d("MyLogs", "(childDataItem)key = " + key);
Log.d("MyLogs", "(childDataItem)value = " + value);
}
}
Above loop is iterating over childDataItem and you are using the same reference again and again in your code so in this case above loop will contain only most recent map items.
For simplicity, I changed your log statements to sysout and here's the example and output:
ArrayList<Map<String, String>> childDataItem;
//general collection for collections of elements
ArrayList<ArrayList<Map<String, String>>> childData;
Map<String, String> m;
childData = new ArrayList<>();
childDataItem = new ArrayList<>();
m = new HashMap<>();
m.put("phoneName", "HTC");
m.put("phoneName1", "HTC1");
childDataItem.add(m);
childData.add(childDataItem);
childDataItem = new ArrayList<>();
m = new HashMap<String, String>();
m.put("phoneName", "Samsung");
childDataItem.add(m);
childData.add(childDataItem);
// создаем коллекцию элементов для третьей группы
childDataItem = new ArrayList<>();
m = new HashMap<String, String>();
m.put("phoneName", "LG");
childDataItem.add(m);
childData.add(childDataItem);
for (ArrayList<Map<String, String>> outerEntry : childData) {
for(Map<String, String> i:outerEntry ) {
for (String key1 : i.keySet()) {
String value1 = i.get(key1);
System.out.println("MyLogs (childData)value1 = " + value1);
System.out.println("MyLogs (childData)key = " + key1);
}
}
}
Output
MyLogs (childData)value1 = HTC1
MyLogs (childData)key = phoneName1
MyLogs (childData)value1 = HTC
MyLogs (childData)key = phoneName
MyLogs (childData)value1 = Samsung
MyLogs (childData)key = phoneName
MyLogs (childData)value1 = LG
MyLogs (childData)key = phoneName
So as you probably know, an array list is just a sequential store of data objects. And a map is a key-value pair mapping where the key is used as the lookup and must be unique. That is to say in a Map you may have many duplicate values but only one key.
As for iterating over a Map you can use an entry set which makes it a little easier. So if you wanted to iterate over an object of type <ArrayList<Map<String, String>> it would look something like this for your childDataItem class.
for(Map<String, String> map : childDataItem){
//Take each map we have in the list and iterate over the keys + values
for(Map.Entry<String, String> entry : map){
String key = entry.getKey(), value = entry.getValue();
}
}
And in your other case, the example is the same except you have another layer of array list.
for(List<Map<String, String>> childDataItemList : childData){
for(Map<String, String> map : childDataItemList){
//Take each map we have in the list and iterate over the keys + values
for(Map.Entry<String, String> entry : map){
String key = entry.getKey(), value = entry.getValue();
}
}
}

Adding two values to a key in a HashMap in Java

I am using a file that consists of:
"word","wordtype","definition"
"Base","n.","The lower part of a robe or petticoat."
"Base","n.","An apron."
The output is as follows:
key: "base" value: ["word""wordtype""definition", "Base""n.""The lower part of a robe or petticoat.", "Base""n.""An apron."]
key: "word" value: ["word""wordtype""definition", "Base""n.""The lower part of a robe or petticoat.", "Base""n.""An apron."]
Desired outcome:
key: "base" value: [ "Base""n.""The lower part of a robe or petticoat.", "Base""n.""An apron."]
key: "word" value: ["word""wordtype""definition"]
Can someone point me in the right direction?
BufferedReader br = new BufferedReader( new InputStreamReader(new FileInputStream(file)));
String line = null;
TreeMap<String, List<String>> def = new TreeMap<String, List<String>>();
List<String> values = new ArrayList<String>();
try {
while ((line = br.readLine()) != null) {
String []parts =line.split(",");
String key = null;
for (int i = 0; i < parts.length; i++){
key = parts[0];
}
values.add(parts[0] + parts[1] + parts[2]);
def.put(key.toLowerCase(), values);
}
A Map cannot work as you request. Any key can only be mapped to a single value.
If you want something akin to what you're doing, but where you can have multiple values for a given key, you could do something like:
List<Map.Entry<String, List<String>>> def = new ArrayList<>();
Map.Entry<String, List<String>> entry = new AbstractMap.SimpleEntry<>(key, list);
def.add(entry);
Then iterate through your def:
for (Map.Entry<String, List<String>> entry : def) {
System.out.println(String.format("Key: %s. Values: %s",
entry.getKey(),
Arrays.toString(entry.getValue().toArray())));
}
Edit:
For your comment: If you want that, you can always roll your own type to store in the Map (or List if you still need duplicate keys):
class WordDescription {
final String wordType;
final String definition;
WordDescription(String wordType, String definition) {
this.wordType = wordType;
definition = definition;
}
String getWordType() {
return wordType;
}
String getDefinition() {
return definition;
}
}
And use that in a List<Map.Entry<String, WordDescription>>. You can make wordType an enum if there's a pre-defined set of them (adjective, noun, etc.).

replace duplicate values string in hashmap java

I have a hashmap which contains student id as key and some string as value.
It contains data like
a abc.txt
b cde.txt
d abc.txt
I want to find the duplicate values in map and replace them with genreic values. I want a map like
a abc.txt
b cde.txt
d abc_replacevalue.txt
I have tried with the code but its not working
Map<String,String> filemap = new HashMap<String,String>();
// filemap is the original hash map..
Set<String> seenValues = new HashSet<String>();
Map<String, String> result = new HashMap<String, String>();
for (Map.Entry<String, String> entry : filemap.entrySet()) {
String value = entry.getValue();
if (seenValues.contains(value)) {
value = "updated"; // update value here
}
result.put(entry.getKey(), value);
seenValues.add(value);
}
for (String key : result.keySet() ) {
String value = result.get( key );
System.out.println(key + " = " + value);
}
The output is still the same
a abc.txt
b cde.txt
d abc.txt
You can generate a new map from an existing one, checking every new value that you come across to see if it has already been seen:
Set<String> seenValues = new HashSet<String>();
Map<String, String> result = new HashMap<String, String>();
for (Map.Entry<String, String> entry : original.entrySet()) {
String value = entry.getValue();
if (seenValues.contains(value)) {
value = ...; // update value here
}
result.put(entry.getKey(), value);
seenValues.add(value);
}

Java - using Map how to find matching Key when Values are stored a List of strings

All,
I have a map with categories and subcategories as lists like this:
Map<String,List<String>> cat = new HashMap<String,List<String>>();
List<String> fruit = new ArrayList<String>();
fruit.add("Apple");
fruit.add("Pear");
fruit.add("Banana");
cat.put("Fruits", fruit);
List<String> vegetable = new ArrayList<String>();
vegetable.add("Carrot");
vegetable.add("Leak");
vegetable.add("Parsnip");
cat.put("Vegetables", vegetable);
I want to find if "Carrot" is in the map and to which key ("Fruit') it matches, however:
if (cat.containsValue("Carrot")) {System.out.println("Cat contains Leak");}
gives False as outcome. How can I match "Carrot" and get back the key value "Vegetable"
Thx.
You need to create the inversed map:
Map<String, String> fruitCategoryMap = new HashMap<>();
for(Entry<String, List<String>> catEntry : cat.entrySet()) {
for(String fruit : catEntry.getValue()) {
fruitCategoryMap.put(fruit, catEntry.getKey());
}
}
Then you can simply do:
String category = fruitCategoryMap.get("Banana"); // "Fruit"
Iterate thought all the keys and check in the value if found then break the loop.
for (String key : cat.keySet()) {
if (cat.get(key).contains("Carrot")) {
System.out.println(key + " contains Carrot");
break;
}
}
You have to search for the value in the entire map:
for (Entry<String, List<String>> entry : cat.entrySet()) {
for (String s : entry.getValue()) {
if (s.equals("Carrot"))
System.out.println(entry.getKey());
}
}
try this,
for (Map.Entry<String, List<String>> entry : cat.entrySet()) {
String names[] = entry.getValue().toArray(new String[entry.getValue().size()]);
for (int i = 0; i < names.length; i++) {
if (names[i].equals("Carrot")) {
System.out.println("found"+names[i]);
break;
}
}
}

Categories

Resources