Adding two values to a key in a HashMap in Java - 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.).

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

Improper output while filtering a hashmap

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

How can I iterate over the results of a hash map where the value is a list?

I've created a hash map that groups unique keys that combine three parameters, i.e. customer, sc and admin. I want to create a unique list of keys with a list of servers attached. I've implemented the following:
public static void main(String[] args) {
String items = "customer1^sc1^admin1|server1~" +
"customer1^sc1^admin1|server2~" +
"customer1^sc1^admin1|server3~" +
"customer2^sc1^admin1|server1~" +
"customer3^sc1^admin1|server3~" +
"customer3^sc1^admin1|server2~";
// Set up raw data
List<String> splitItems = Arrays.asList(items.split("\\s*~\\s*"));
// Display raw data
System.out.println("Raw List: " + items);
// Create a hash map containing customer name as key and list of logs as value
HashMap<String, List<String>> customerHashMap = new HashMap<>();
// Loop through raw data
for (String item : splitItems) {
// Create new lists. One for customers and one for logs
// List<String> customerList = new ArrayList<>();
List<String> logList;
String list[] = item.split("\\|");
String customer = list[0];
String log = list[1];
logList = customerHashMap.get(customer);
if (logList == null){
logList = new ArrayList<>();
customerHashMap.put(customer, logList);
}
logList.add(log);
// System.out.println(logList);
}
// Print out of the final hash map. Customer "a" should only have "a" logs, customer "b" with "b", etc.
System.out.println("");
List<String> hashMapList = new ArrayList<String>();
Iterator it = customerHashMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
String output = pair.getKey() + "|" + pair.getValue().toString();
hashMapList.add(output);
it.remove();
}
String hashMapResultString = hashMapList.toString();
String hashMapResultFormatted = hashMapResultString.replaceAll("[\\[\\]]", "");
System.out.println(hashMapResultFormatted);
}
Raw List: customer1^sc1^admin1|server1~customer1^sc1^admin1|server2~customer1^sc1^admin1|server3~customer2^sc1^admin1|server1~customer3^sc1^admin1|server3~customer3^sc1^admin1|server2~
Hash Map String:
customer2^sc1^admin1|server1, customer3^sc1^admin1|server3, server2, customer1^sc1^admin1|server1, server2, server3
I now want to use the hash map to create a string which will be parsed further (don't ask lol). So I set the keys and values of the hash map to a string which separates them with a unique delimiter |. The problem is that because the key is a List<String>, when printing I can't ascertain the beginning of every new key if its value is a list with more than one item, i.e. customer3^sc1^admin1|server3, server2, is followed immediately by customer1^sc1^admin1|server1, server2, server3. I need a delimiter here that separates them.
My ideal output would look like this:
customer2^sc1^admin1|server1~customer3^sc1^admin1|server3, server2~customer1^sc1^admin1|server1, server2, server3~...
How can I achieve this?
Update:
This is the answer I ultimately found useful for my particular problem:
StringBuilder s = new StringBuilder();
for (Map.Entry<String, List<String>> entry : customerHashMap.entrySet()) {
s.append(entry.getKey() + "|");
List<String> list = entry.getValue();
for (String item : list) {
if (item != list.get(list.size() - 1)) {
s.append(item + "^");
} else {
s.append(item);
}
}
s.append("~");
}
System.out.println(s.toString());
You can iterate through a map's entry set:
StringBuilder s = new StringBuilder();
for(Map.Entry<String,List<String>> entry : map.entrySet()) {
s.append(entry.getKey() + "\n");
List<String> list = entry.getValue();
for(String item : list) {
s.append(" " + item + "\n");
}
}
return s.toString();
For the sake of a clearer example, I've output a different format from the one you asked for, but this illustrates how to work with a map of list values. When adapting to your needs, have a look at java.util.StringJoiner and the related Collectors.joining(); it may well be useful.
Streams can be handy here:
String encoded = map.entrySet().stream()
.map( entry -> entry.getValue().stream()
.collect(Collectors.joining("^"))
+ "|" + entry.getKey())
.collect(Collectors.joining("~"));
What happens here is:
We get a stream of Entry<String,List<String> out of the map
The lambda entry -> ... converts each entry into a string of the form val1^v2^v3^...^valN|key, i.e. we are mapping a Stream<Entry<>> into a Stream<String>.
the final collect() joins the stream of strings into a single string using ~ as a delimiter.

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

Get value of a param in a string using java

I have string variable String temp="acc=101&name=test"; and now how to get the value of name param from temp string.
temp.split("&")[1].split("=")[1]
public static Map<String, String> getParamMap(String query)
{
String[] params = query.split("&");
Map<String, String> map = new HashMap<String, String>();
for (String param : params)
{
String name = param.split("=")[0];
String value = param.split("=")[1];
map.put(name, value);
}
return map;
}
String temp="acc=101&name=test";
Map<String, String> map = getParamMap(temp);
for(Object object :map.keySet()){
System.out.println("key= "+object +" value= "+map.get(object));
}
System.out.println(map.get("name"));
Here is a non-general way
String str = "name=";
System.out.println(temp.substring(temp.indexOf(str) + str.length()));
It could be implemented in more general way of course:
String temp = "acc=101&name=test";
StringTokenizer st = new StringTokenizer(temp, "&");
String paramName = "name";
String paramValue = "";
while(st.hasMoreElements()) {
String str = st.nextToken();
if (str.contains(paramName)) {
paramValue = str.substring(str.indexOf(paramName) + paramName.length() + 1);
break;
}
}
System.out.println(paramValue);
You can use a method like below
public static String getValue(String queyStr, String paraamName){
String[] queries=queyStr.split("&");
for(String param:queries){
if(param.indexOf(paraamName)!=-1)
return param.split("=")[1];
}
return null;
}
And call the method like
getValue(temp, "name")
public static void main(String[] args)
{
String temp = "acc=101&name=test";
System.out.println(temp.split("&")[1].split("=")[1]);
}
If you are looking for a way to parse GET-Parameters out of an URL:
public static Map<String, String> splitQuery(URL url) throws UnsupportedEncodingException {
Map<String, String> query_pairs = new LinkedHashMap<String, String>();
String query = url.getQuery();
String[] pairs = query.split("&");
for (String pair : pairs) {
int idx = pair.indexOf("=");
query_pairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
}
return query_pairs;
}
You can access the returned Map using <map>.get("name"), with the URL given in your question this would return "test".
Assuming you have constant format :
String temp="acc=101&name=test";
String result =temp.substring(temp.lastIndexOf("=")+1,temp.length());
//result is test
String temp="acc=101&name=test";
String[] split = temp.split("&");
String[] name = split[1].split("=");
System.out.println(name[1]);
I would put the whole parameter in a HashMap so it is easy to get the values.
HashMap<String, String> valuemap = new HashMap<String, String>();
If you do it like so, you have to split the values at the right place...
String temp="acc=101&name=test";
valuemap.put(temp.split("&")[0].split("=")[0], temp.split("&")[0].split("=")[1]);
valuemap.put(temp.split("&")[1].split("=")[0], temp.split("&")[1].split("=")[1]);
...and put them into your HashMap. Than you have a nice collection of all your values and it is also better if you have more than only that two values. If you want the value back, use:
valuemap.get("acc")
valuemap.get("name")

Categories

Resources