How to group some objects by certain field in Java? - java

I've been trying to group of facets by translated value, but what I always get is only one last object on the List (no dataset). Here is what I tried:
HashMap<String, List<Facet>> map = new HashMap<>();
for (Facet facet : getFacets()) {
map.put(facet.getTranslatedValue(), new ArrayList<com.schneider.gss.model.Facet>());
map.get(facet.getTranslatedValue()).add(facet);
}
Can you suggest anything?

Change your for loop as below
for (Facet facet : getFacets()) {
if(map.get(facet.getTranslatedValue()) == null) {
map.put(facet.getTranslatedValue(), new ArrayList<com.schneider.gss.model.Facet>());
}
map.get(facet.getTranslatedValue()).add(facet);
}

You're overwriting your list each time you get an identical translated value with a new ArrayList. Instead, you should check if it exists:
HashMap<String, List<Facet>> map = new HashMap<>();
for (Facet facet : getFacets()) {
//get the list
ArrayList<com.schneider.gss.model.Facet> list = map.get(facet.getTranslatedValue());
//if list doesn't exist, create it
if(list == null) {
map.put(facet.getTranslatedValue(), new ArrayList<com.schneider.gss.model.Facet>());
}
//then add to list
map.get(facet.getTranslatedValue()).add(facet);
}

in Guava there is class Multimap (or ArrayListMultimap) which does exactly what you need

Related

How can I add a string one at a time to a HashMap<Integer, List<String>>?

This function loops through a dictionary (allWords) and uses the
getKey function to generate a key. wordListMap is a HashMap> so I need to loop through and put the key and and a List. If there is not a list I put one if there is I just need to append the next dictionary word. This is where I need help. I just can't figure out the syntax to simply append the next word to the list that is already there. Any Help would be appreciated.
public static void constructWordListMap() {
wordListMap = new HashMap<>();
for (String w : allWords) {
int key = getKey(w);
if (isValidWord(w) && !wordListMap.containsKey(key)) {
List list = new ArrayList();
list.add(w);
wordListMap.put(key, list);
} else if (isValidWord(w) && wordListMap.containsKey(key)) {
wordListMap.put(key, wordListMap.get(key).add(w));
}
}
}
map.get(key).add(value)
Simple as that.
So I've gathered that you want to, given HashMap<Integer, List<String>>, you'd like to:
create a List object
add String objects to said List
add that List object as a value to be paired with a previously generated key (type Integer)
To do so, you'd want to first generate the key
Integer myKey = getKey(w);
Then, you'd enter a loop and add to a List object
List<String> myList = new List<String>;
for(int i = 0; i < intendedListLength; i++) {
String myEntry = //wherever you get your string from
myList.add(myEntry);
}
Lastly, you'd add the List to the HashMap
myHash.put(myKey, myList);
Leave any questions in the comments.
else if (isValidWord(w) && wordListMap.containsKey(key)) {
wordListMap.put(key, wordListMap.get(key).add(w));
}
If you want to add a new value to your list, you need to retrieve that list first. In the code above, you are putting the return value of add into the table (which is a boolean), and that is not what you want.
Instead, you will want to do as Paul said:
else if (isValidWord(w) && wordListMap.containsKey(key)) {
wordListMap.get(key).add(w);
}
The reason this works is because you already added an ArrayList to the table earlier. Here, you are getting that ArrayList, and adding a new value to it.

Iterate through the list and grouping based on dates

I am trying to achieve, where I am looking for ways to group the list based on the dates. I have a lost of attribute as below:
List<Attribute> attributes
where
Attribute.java is as below
public class Attribute
{
Integer getValue();
List<String> getString();
Date getDate();
}
I am looking for a way such that , while iterating through the list of Attribute , I could create a List of elements (current date) and map of elements based on the dates (in the past) having the same IntegerValue.
My codes goes as below:
List<Attribute> currentElement = new ArrayList<Attribute>();
Map<Integer, List<Attribute>> historicalElement = new HashMap<Integer, List<Attribute>>();
//iterating the entire list
for(final Attribute attribute : attributes)
{
if(attribute.getDate() == currentDate)
{
currentElement.add(attribute);
}
if(attribute.getDate() < currentDate)
{
historicalElement.put(attribute.getValue(), attribute)
}
}
The statement
historicalElement.put(attribute.getValue(), attribute)
wont work beacuse
The method put(Integer, List<Attribute>) in the type Map<Integer,List<Attribute>> is not applicable for the arguments (Integer, Attribute).
Is there any way that we can achieve that map , rather than typing casting to List.
Thanks !!!
Casting to list won't help at all. You'll only get a ClassCastException. Simplest way might be this:
if(attribute.getDate() < currentDate)
{
List<Attribute> list = historicalElement.get(attribute.getValue());
if(list == null){
list = new ArrayList<>();
historicalElement.put(attribute.getValue() , list);
}
list.add(attribute);
}
edit: Paul's answer is better here.
Looks like a job for guava multimap where you can do:
Map<Integer, List<Attribute>> historicalElement = Multimaps.newListMultimap();
for(final Attribute attribute : attributes) {
historicalElement.put(attribute.getValue(), attribute)
}
should do it.
Well, except that you want to group by the date as well? That's slightly trickier.
Firstly you need to fix your date comparison. You don't compare dates using == operator.
Now while adding a new entry in map, you've to first check the existing key. If not there then create a new ArrayList with new value:
if(attribute.getDate().compareTo(currentDate) < 0) {
if (historicalElement.containsKey(attribute.getValue())) {
historicalElement.get(attribute.getValue()).add(attribute);
} else {
historicalElement.put(attribute.getValue(),
new ArrayList<Attribute>(Arrays.asList(attribute)));
}
}
If you're using Java 8, you can directly use Map#merge() method to avoid that extra testing:
if(attribute.getDate().compareTo(currentDate) < 0) {
historicalElement.merge(
attribute.getValue(),
new ArrayList<Attribute>(Arrays.asList(attribute)),
ArrayList::addAll);
}
You can also use Stream API and lambda here:
List<Attribute> currentElement = attributes.stream()
.filter(a -> a.getDate().compareTo(currentDate) == 0)
.collect(Collectors.toList());
Map<Integer, List<Attribute>> historicalElement = attributes.stream()
.filter(a -> a.getDate().compareTo(currentDate) < 0)
.collect(Collectors.groupingBy(Attribute::getValue));
Aren’t you trying to put a single Attribute in a Map where it requires a List of Attributes, here:
//iterating the entire list
for(final Attribute attribute : attributes)
{
if(attribute.getDate() == currentDate)
{
currentElement.add(attribute);
}
if(attribute.getDate() < currentDate)
{
historicalElement.put(attribute.getValue(), attribute) // HERE
}
}
If you want it to be single attribute, this should change:
From: Map<Integer, List<Attribute>> historicalElement = new HashMap<Integer, List<Attribute>>();
To: Map<Integer, List<Attribute>> historicalElement = new HashMap<Integer, Attribute>();

prepare a multi valued country map

I want to prepare a HashMap in such way that
Key : Country Code
Value : List of returned orderEntries
the following process data method process every 5 orderEntry which can be from any country.
let me make it more clear. I have list of orderEntries that come from different countries now I want to put these entries into map based on country key. Like if 20 entries coming from US then US will be the key and 20 Entries would be the values. But problem is that I don't want to create a list for each county inside map.
public void processSegmentData(final List resultSet)
{
for (final Object orderEntry : resultSet)
{
if (orderEntry instanceof OrderEntryModel)
{
String countryCode = null;
final OrderModel order = ((OrderEntryModel) orderEntry).getOrder();
if (order.getDeliveryAddress() != null)
{
countryCode = order.getDeliveryAddress().getCountry().getIsocode();
}
orderEntriesMap.put(Config.getParameter(countryCode+".return.pid"), orderEntries);
}
}
}
so you are after a hashmap which contains a linked list Something along the lines of:
public HashMap<String, LinkedList<OrderEntryModel>> processSegmentData(final List resultSet) {
HashMap<String, LinkedList<OrderEntryModel>> orderEntriesMap = new HashMap<String, LinkedList<OrderEntryModel>>();
for (final Object orderEntry : resultSet) {
if (orderEntry instanceof OrderEntryModel) {
String countryCode = null;
final OrderModel order = ((OrderEntryModel) orderEntry).getOrder();
if (order.getDeliveryAddress() != null) {
countryCode = order.getDeliveryAddress().getCountry().getIsocode();
}
if (!orderEntriesMap.containsKey(countryCode)) {
orderEntriesMap.put(countryCode, new LinkedList<OrderEntryModel>());
}
orderEntriesMap.get(countryCode).add((OrderEntryModel) orderEntry);
}
}
return orderEntriesMap;
}
would be an example based on the source code you provided guessing object names.
But problem is that I don't want to create a list for each county
inside map.
I understand your problem but map store unique key, you can not store same country code.
you have to use Map<String, List<String>>() that will hold your country code as key and then put your values inside List<String>.
after doing this if you have any problem edit your question will help you to resolve that.
Just Create a Map<String,List<String>>. and follow the following approach
Map<String,List<String>> countryMap = new HashMap<String, List<String>>();
for (final String orderEntry : orders){
if(countryMap.containsKey(orderEntry.getCountry())){
countryMap.get(orderEntry.getCountry()).add(orderEntry);
}else{
//create a new list and add orderEntry
countryMap.put(orderEntry.getCountry(),orderEntry);
}
}
You need to modify this according to your stuff
You could use Guava's Multimap to simplify things. A Multimap allows you to store multiple entries against a single key, e.g.:
Multimap<String, OrderEntry> orderEntriesMultimap = HashMultimap.create();
for (final Object orderEntry : resultSet) {
// omitted...
orderEntriesMultimap.put(Config.getParameter(countryCode+".return.pid"), orderEntry);
}
You can then retrieve all the associated values by key:
Collection<OrderEntryModel> entries = orderEntriesMultimap.get(key);

How to call multi table insert in myBatis mapper?

I am using myBatis 3.2.x and have run into a scenario where I need to do multiple table inserts in one database trip,
I was wondering if I can create a master INSERT sql mapper file which would call these multi table inserts and save me network trips
I am consuming JSON objects from a EMS server and my Turn around time is a bit higher then required.
All suggestions and hints are welcome.
Thanks
VR
Use Collections.sort() to sort and use a simple for cycle to catch doubles, e.g.:
Collections.sort(myList);
A previous = null;
for (A elem: myList) {
if (elem.compareTo(previous) == 0) {
System.err.println("Duplicate: "+elem);
}
previous = elem;
}
Assuming that the Comparable is consistent with the equals implementation, you can use a Set. You can add each element to a Set using Set.add(..) and use the return value of add to determine if the value was already present in the Set and create either a Set or a List to return.
Note: If you need each duplicate returned only once, you can change the return list to a set.
List<A> duplicates(List<A> myList) {
Set<A> s = new HashSet<A>();
List<A> duplicates = new ArrayList<A>(); // change to using a Set if you want to report each duplicate item only once.
for (A item: myList) {
if (!s.add(item)) {
duplicates.add(item);
}
}
return duplicates;
}
An improved version using sorting (to report duplicate elements only once, I assume there are no null values in the list):
Collections.sort(myList);
A previous = null, elem = null;
for (java.util.Iterator<A> it = myList.iterator; it.hasNext(); elem = it.next()) {
if (elem.compareTo(previous) == 0) {
System.err.println("Duplicate: "+elem);
while (it.hasNext() && (elem = it.next()).compareTo(previous)) {
//loop through other values
}
}
previous = elem;
}
A version using SortedSet (probably this is faster a bit): and corrected the same
SortedSet<A> set = new TreeSet<>(), duplicates = new TreeSet<>();
for (A a: myList) {
if (!set.add(a)) {
duplicates.add(a);
}
}
return duplicates;//or for (A a: duplicates) System.println("Duplicate: " + a);

Add new line to list if key already exist

I have to build hashMap that contain key object and list of instances that related to this key.
during the build of the map I want to ask if the key object(vocKey) already exist don't create new instance
for Voc key just add new line to the list of vocData ,how I can do that ?
private HashMap<vocKey,List<vocData>> vocabulary = new HashMap<vocKey,List<vocData>>();
See HashMap.get()
List<vocData> data = vocabulary.get(key);
if (data == null) {
vocabulary.put(...);
} else {
data.add(...);
}
This is just a guide. Give it a try yourself
1) First get the value using key.
2) if a value exist add new line to value list
3) if value does not exist create a new instance and add under new key.
List<vocData> data = vocabulary.get(key);
if (data == null) {
data = new ArrayList();
vocabulary.put(key, data);
}
data.add(...);

Categories

Resources