Replacing multimap with mapping an element to a collection of elements - java

I am trying to map a meterId to a list of MeterBlinks that have that Id. I'm mainly confused on how to build the list up for the HashMap.put() call. Code below:
Map<String, List<MeterBlink>> IdToMetersMap = new HashMap<>();
for (MeterBlink meterBlink : createData()) {
List<MeterBlink> meterBlinkList = new ArrayList<>();
meterBlinkList.add(meterBlink);
String meterId = meterBlink.getMeterId();
idToMetersMap.put(meterId, meterBlinkList)
}
I think the issue is that I am creating a new list each time I iterate through but I am not sure how to resolve this.

Use the computeIfAbsent method added in jre 8:
Map<String, List<MeterBlink>> idToMetersMap = new HashMap<>();
for (MeterBlink meterBlink : createData()) {
String meterId = meterBlink.getMeterId();
idToMetersMap.computeIfAbsent(meterId, k -> new ArrayList<>()).add(meterBlinks);
}
Another option in java 8:
Map<String, List<MeterBlink>> idToMetersMap = createData().stream()
.collect(Collectors.groupingBy(MeterBlink::getMeterId));

I like the java8 answer, but here without java8 (without lambda expressions):
Map<String, List<MeterBlink>> idToMetersMap = new HashMap<>();
for (MeterBlink meterBlink : createData()) {
String meterId = meterBlink.getMeterId();
List<MeterBlink> meterBlinkList = idToMetersMap.get(meterId);
//if List doesn't exist create it and put in Map
if (meterBlinkList == null) {
meterBlinkList = new ArrayList<>();
idToMetersMap.put(meterId, meterBlinksList)
}
meterBlinkList.add(meterBlink);
}

Related

which data structure in java can hold hashmaps as its elements?

public static HashMap<String,Integer> users1 = new HashMap<>();
public static HashMap<String,Integer> users2 = new HashMap<>();
public static HashMap<String,Integer> users3 = new HashMap<>();
public static HashMap<String,Integer> users4 = new HashMap<>();
// Enter code here.
I need to add them into a arrayList or set or something and then they should be called when using that data structures index. Like is there any way to solve it
Or just simply use an array:
public static HashMap<String,Integer>[] users = HashMap<>[4];
users[0] = = new HashMap<>();
users[1] = = new HashMap<>();
users[2] = = new HashMap<>();
users[3] = = new HashMap<>();
And then use them:
users[0].add( "String", 123 );
you could as simply as :
List<HashMap<String, Integer>> list = new ArrayList<>();
// And then add them to list
list.add(users1);
list.add(users2);
...
ArrayList<HashMap> list = new ArrayList<>();
list.add(users1);

Streams filter and Grouping List on Map

I am making a filter that allows me to generate a Map whose values are lists of files, for this, I have tried to use Streams.
I have the following example of Files list:
CLA_FileName
CLA_FileName
CLA_FileName
CM_FileName
CM_FileName
CM_FileName
SP_FileName
SP_FileName
CON_FileName
CON_FileName
Then I need to take that list of files and take them to a map whose key groups all the files of a type within a single list;
Map <String, List < File>>: So this would be the expected result.
"CLA_", List <File> 3 CLA Files
"CM_", List <File> 3 CM Files
"SP_", List <File> 2 SP Files
"CON_", List <File> 2 CON Files
I have the following code, which is only grouping one file type for me, CLA. Please can you give me an idea of how to filter and load these files on the Map?
List<File> csvList = getFiles();
Function<File, String> filterCLA = new Function<File, String>() {
#Override
public String apply(File file) {
return String.valueOf(file.getName().startsWith("CLA_"));
}
};
Map<String, List<File>> map = csvList.stream()
.collect(groupingBy(f-> filterCLA.apply(f), toCollection(ArrayList::new)));
for(Map.Entry<String, List<File>> entry: map.entrySet()){
System.out.println(entry.getKey()+" - "+ entry.getValue());
}
This code is only saving the CLA files in a list within the map, with CLA_ key. This is correct but I need to add the rest of the files.
Try this. Any filename that does not begin with something like XXX_ will be mapped to a catch all key of type UnknownType
List<File> csvList = List.of(
new File("CLA_FileName"),
new File("CLA_FileName"),
new File("CLA_FileName"),
new File("CM_FileName"),
new File("CM_FileName"),
new File("CM_FileName"),
new File("SP_FileName"),
new File("SP_FileName"),
new File("CON_FileName"),
new File("CON_FileName"),
new File("BadFileName1"),
new File("BadFileName2"));
// get the prefix (e.g. CM_)
Function<File, String> getPrefix = f-> {
String name = f.getName();
// location of delimiter
int index = name.indexOf('_');
if (index < 0) {
return "UNKNOWN_TYPE";
}
return name.substring(0,index+1);
};
// create the map
Map<String, List<File>> map = csvList.stream()
.collect(Collectors.groupingBy(getPrefix));
//print the map
map.entrySet().forEach(System.out::println);
Prints
UNKNOWN_TYPE - [BadFileName1, BadFileName2]
CON_ - [CON_FileName, CON_FileName]
SP_ - [SP_FileName, SP_FileName]
CM_ - [CM_FileName, CM_FileName, CM_FileName]
CLA_ - [CLA_FileName, CLA_FileName, CLA_FileName]
Finally I was able to find a solution to filter and group. Here the code.
Thanks everyone for your help
List<Predicate<File>> allPredicates = new ArrayList<Predicate<File>>();
allPredicates.add(str -> str.getName().startsWith("CM"));
allPredicates.add(str -> str.getName().startsWith("CLA"));
allPredicates.add(str -> str.getName().startsWith("IDP"));
allPredicates.add(str -> str.getName().startsWith("SP"));
allPredicates.add(str -> str.getName().startsWith("CON"));
return Arrays.asList(csvFiles)
.stream()
.filter(allPredicates.stream().reduce(f->true, Predicate::or))
.collect(Collectors.groupingBy(f-> f.getName().substring(0,f.getName().indexOf("_"))));

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

MultiList not displaying all result from webservice (Codename One)

Im trying to display some json records using a MultiList. I followed what was done here https://www.codenameone.com/manual/graphics.html but mine is returning only one record (Please see this image). The response came from this webservice
Below is my code. Please kindly show me where i'm wrong.
#Override
protected void beforeFormA(Form f) {
Style s = UIManager.getInstance().getComponentStyle("Button");
FontImage p = FontImage.createMaterial(FontImage.MATERIAL_PORTRAIT, s);
EncodedImage placeholder = EncodedImage.createFromImage(p.scaled(p.getWidth() * 3, p.getHeight() * 4), false);
getattractive();//fetch results from webservice and store inside response variable
ArrayList arr = (ArrayList) response.get("results");
for (Object m:arr){
Map ma = (Map)m;
address =(String) ma.get("formatted_address");
name=(String)ma.get("name");
icon=(String)ma.get("icon");
ArrayList<Map<String, Object>> data = new ArrayList<>();
data.add(createListEntry(name,address,icon));
DefaultListModel<Map<String, Object>> model = new DefaultListModel<>(data);
MultiList ml = new MultiList(model);
ml.getUnselectedButton().setIconName("icon_URLImage");
ml.getSelectedButton().setIconName("icon_URLImage");
ml.getUnselectedButton().setIcon(placeholder);
ml.getSelectedButton().setIcon(placeholder);
f.add(BorderLayout.CENTER, ml);
}
}
private Map<String, Object> createListEntry(String name, String addr, String coverURL) {
Map<String, Object> entry = new HashMap<>();
entry.put("Line1", name);
entry.put("Line2", addr);
entry.put("icon_URLImage", coverURL);
entry.put("icon_URLImageName", name);
return entry;
You should fix the indentation. The for loop encapsulates everything so you are looping over all the elements and for X elements you are adding X multi lists.
This is something you would instantly see if you step over the code with a debugger...
done. I moved the line below out of the method and place it inside the class.
ArrayList> data = new ArrayList<>();

How to convert a nested complex scala Map to Java

I'm trying to use a Scala library in my Java program and I have some difficulties to convert a complex Scala Map to Java.
My used Scala object method has the following return type: scala.collection.mutable.Map<String, Map<Object, Seq<Object>>>
How do I convert that to a Java equivalent of Map<String, Map<Object, List<Object>>> ?
I already played around with the JavaConversions and JavaConvertors packages but no luck :(
public void getPartitionAssignmentForTopics(final List<String> topics) {
final Seq<String> seqTopics = scala.collection.JavaConversions.asScalaBuffer(topics).toList();
scala.collection.mutable.Map<String, Map<Object, Seq<Object>>> map2 = zkUtils
.getPartitionAssignmentForTopics(seqTopics);
val map:scala.collection.mutable.Map[String, Map[Object, Seq[Object]]] = scala.collection.mutable.Map()
map:
collection.mutable.Map[String, Map[Object, Seq[Object]]] =Map()
map.mapValues(_.mapValues(_.asJava).asJava).asJava
res2:
java.util.Map[String, java.util.Map[Object, java.util.List[Object]]] ={}
}
This does not compile :)
With playing around I meant that I use the following code to convert from Scala Seq to Java List:
scala.collection.JavaConversions.seqAsJavaList(zkUtils.getAllTopics());
I ended up with the following code. Not really nice :D
public java.util.Map<String, java.util.Map<Integer, java.util.List<Integer>>> getPartitionAssignmentForTopics(final List<String> topics) {
final scala.collection.Seq<String> seqTopics = scala.collection.JavaConversions.asScalaBuffer(topics).toList();
scala.collection.mutable.Map<String, scala.collection.Map<Object, scala.collection.Seq<Object>>> tmpMap1 =
zkUtils.getPartitionAssignmentForTopics(seqTopics);
final java.util.Map<String, java.util.Map<Integer, java.util.List<Integer>>> result = new HashMap<>();
java.util.Map<String, Map<Object, Seq<Object>>> tmpMap2 = JavaConversions.mapAsJavaMap(tmpMap1);
tmpMap2.forEach((k1, v1) -> {
String topic = (String)k1;
java.util.Map<Object, Seq<Object>> objectSeqMap = JavaConversions.mapAsJavaMap(v1);
java.util.Map<Integer, List<Integer>> tmpResultMap = new HashMap<>();
objectSeqMap.forEach((k2, v2) -> {
Integer tmpInt = (Integer)k2;
List<Integer> tmpList = (List<Integer>)(Object)JavaConversions.seqAsJavaList(v2);
tmpResultMap.put(tmpInt, tmpList);
});
result.put(topic, tmpResultMap);
});
return result;
}

Categories

Resources