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>();
Related
I have a list of custom object,
public class Assignmentsdata {
String assignmentId;
String teacherId;
String groupName;
String sectionId;
String levelId;
String startTime;
}
ArrayList<Assignmentsdata> list = new ArrayList<>();
lets say there are 20 elements in that list.
Now I want to get the output which is a hashmap of startTime as a key and the Value would be a new HashMap of GroupID and a list of Assignments of those that had the same groupName.
OutPut Example
HashMap<startTime,HasMap<groupName,List> hashMap = new HashMap();
a little more insight about the problem: First I want to categorise based on startTime(Month) then i want to categorise based on groupName, Thanks in advance.
I have successfully categorised based on group name and created a map through below code:
for( int i = 0; i<assignmentsdataArrayList.size();i++ ){
if (hashMap.size()>0){
hashMap.get(assignmentsdataArrayList.get(i).getGroupName()).add(assignmentsdataArrayList.get(i));
}else {
hashMap.put(assignmentsdataArrayList.get(i).getGroupName(),new ArrayList<Assignmentsdata>());
hashMap.get(assignmentsdataArrayList.get(i).getGroupName()).add(assignmentsdataArrayList.get(i));
}
}
After that I am lost on how to categorise this hashmap based on the startDate and create a hashmap that would look like the above hashmap in the output heading.
your code may throw a NullPointerException at the first if branch
if (hashMap.size()>0)
{hashMap.get(assignmentsdataArrayList.get(i).getGroupName()).add(assignmentsdataArrayList.get(i));
}
the map.size()>0 doesnt means the Value of GroupName has put a new ArrayList already.
the anwser of using loop should like this
Map<String, Map<String, List<Assignmentsdata>>> map = new HashMap<>();
for (Assignmentsdata assignmentsdata : list) {
if (!map.containsKey(assignmentsdata.getStartTime())) {
map.put(assignmentsdata.getStartTime(), new HashMap<>());
}
Map<String, List<Assignmentsdata>> startTimeMap = map.get(assignmentsdata.startTime);
if (!startTimeMap.containsKey(assignmentsdata.getGroupName())) {
startTimeMap.put(assignmentsdata.getGroupName(), new ArrayList<>());
}
startTimeMap.get(assignmentsdata.groupName).add(assignmentsdata);
}
or you could use the java stream().collect(Collectors.groupingBy()) api to get the result easily
Map<String, Map<String, List<Assignmentsdata>>> result = list.stream()
.collect(Collectors.groupingBy(Assignmentsdata::getStartTime,Collectors.groupingBy(Assignmentsdata::getGroupName)));
I am answering my own question as I solved it if anyone has a better answer please passed your answer aswell, ill accept another answer suitable and efficient answer.
for( int i = 0; i<assignmentsdataArrayList.size();i++ ){
if (hashMap.size()>0){
if (hashMap.get(assignmentsdataArrayList.get(i).getGroupName())==null){
hashMap.put(assignmentsdataArrayList.get(i).getGroupName(),new ArrayList<Assignmentsdata>());
hashMap.get(assignmentsdataArrayList.get(i).getGroupName()).add(assignmentsdataArrayList.get(i));
}else{
hashMap.get(assignmentsdataArrayList.get(i).getGroupName()).add(assignmentsdataArrayList.get(i));
}
}else {
hashMap.put(assignmentsdataArrayList.get(i).getGroupName(),new ArrayList<Assignmentsdata>());
hashMap.get(assignmentsdataArrayList.get(i).getGroupName()).add(assignmentsdataArrayList.get(i));
}
}
// above part is already in the question. the second part i looped through the hashMap then the list once again, and checking if list and map entry have same group name, then made the startdate key that indexed element from the list.
HashMap<String, Map.Entry<String, ArrayList<Assignmentsdata>>> hashMapHashMap = new HashMap<>();
for (var entry : hashMap.entrySet()){
for( int j = 0; j<assignmentsdataArrayList.size();j++ ){
if (assignmentsdataArrayList.get(j).getGroupName()==entry.getKey()){
hashMapHashMap.put(assignmentsdataArrayList.get(j).getStartTime(),entry);
}
}
hashMapHashMap.put(entry.getValue().get())
}
I have a list of custom objects:
List<CustomObject> customObjects;
from which I would like to extract all the objects that have the have the earliest datetime value set.
So the class would look something like this:
class CustomObject {
LocalDateTime date;
public LocalDateTime getDateTime() {
return date;
}
}
and I'm able to successfully find the object in the list with the earliest date with a custom comparator function like this:
private static LocalDateTime getDate(CustomObject customObject) {
return customObject.getDateTime();
}
CustomObject customObjectMin = customObjects.stream().
min(Comparator.comparing(MyUtilClass::getDate));
However, it is possible to have multiple custom objects with the same date, but it looks like there is no way to get multiple occurrences in that scenario with the min. Is there an easy solution to finding all the objects in the list with the earliest date set ? Something like this:
List<CustomObject> customObjectsMin = customObjects.stream().
minWithAllOccurences(Comparator.comparing(MyUtilClass::getDate));
You can do two selections.
one to find the min date
one to find those with that date
e.g.
LocalDate min = customObjects.stream()
.map(CustomObject::getDateTime)
.min(Comparator.naturalOrder());
List<CustomObject> objs = customObjects.stream()
.filter(c -> min.equals(c.getDateTime()))
.collect(Collectors.toList());
Or you can use Collectors.groupingBy into a TreeMap and take the first entry.
Other than Peter Lawrey's excellent answer, I would like to point out that it is possible to do this with a single stream while avoiding the memory costs of collecting every element into a TreeMap. How? One way would be to use reduce(), as follows:
List<SampleJava> customObjectsMin = customObjects.stream()
.reduce(new ArrayList<>(), // identity
(List<SampleJava> list, SampleJava item) -> { // accumulate
if(list.isEmpty() || getDate(item).compareTo(getDate(list.get(0))) < 0) {
return new ArrayList<>(Arrays.asList(item));
} else if(getDate(item).equals(getDate(list.get(0)))) {
list.add(item);
}
return list;
},
(list1, list2) -> { // combine
if(list1.isEmpty()) return list2;
if(list2.isEmpty()) return list1;
int cmp = getDate(list1.get(0)).compareTo(getDate(list2.get(0)));
if(cmp < 0) return list1;
if(cmp > 0) return list2;
list1.addAll(list2);
return list1;
});
I have a list of objects(say Office object) which has properties like say id, name, location, strength. Now, suppose, the field location has values from a certain set (say 3 possible values, New York, California, Utah). Now, I want to re-arrange this list on the location property in a way that the Office object with location property as New York comes first in the list, followed by ones in California followed by Utah. So, basically it's not an ascending or descending order but a criteria set by me for that property. what would be the most efficient way to implement this in Java?
I first thought of using Comprator but I do not want to simply sort in an ascending/descending order but on the criteria I specify as mentioned above. Not sure, if i could use COmparator for this as well and if yes how. And, if not, then what is the other most efficient way?
Simple, can't exclude and ideally need to contain all locations:
public static void rearrangeSort(List<Office> source, String... locations) {
List<String> locs = Arrays.asList(locations);
Collections.sort(source, (o1, o2) -> locs.indexOf(o1.getLocation()) - locs.indexOf(o2.getLocation()));
}
More complex:
public static List<Office> rearrange(List<Office> source, String... locations) {
Map<String, List<Office>> map = new HashMap<>();
for (Office office : source) {
List<Office> lst = map.get(office.getLocation());
if (lst == null) {
lst = new ArrayList<>();
map.put(office.getLocation(), lst);
}
lst.add(office);
}
List<Office> resultList = new ArrayList<>();
for (String loc : locations) {
List<Office> partial = map.get(loc);
if (partial != null) {
resultList.addAll(partial);
}
}
return resultList;
}
Previous variant with lambdas:
public static List<Office> rearrange2(List<Office> source, String... locations) {
Map<String, List<Office>> map = source.stream().collect(Collectors.toMap(
Office::getLocation,
o -> {List<Office> lst = new ArrayList<>(); lst.add(o); return lst;},
(offices, offices2) -> {offices.addAll(offices2); return offices;}));
return Arrays.asList(locations).stream()
.filter(map::containsKey)
.map(map::get)
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
Method :
public void itemAmountCollection() {
Map<String, List<Integer>> orderItemDetails = new LinkedHashMap<String, List<Integer>>();
ArrayList<Integer> itemsAmount = new ArrayList<Integer>();
WebElement orderItemTable = driver.findElement(By
.xpath("//*[#id='tblInfo']/tbody"));
List<WebElement> noOfItems = orderItemTable.findElements(By
.tagName("tr"));
for (int i = 1; i <= noOfItems.size(); i++) {
String itemAmount = driver.findElement(
By.xpath("//*[#id='tblInfo']/tbody/tr[" + i
+ "]/td[8]")).getText();
itemsAmount.add(Integer.parseInt(itemAmount));
orderItemDetails.put("amount", itemsAmount);
}
}
with above method we collected all the item amount with Map Collections and Output for the above method is (345,7905,345)
how can we add all the values in an particular Key (amount)
Expected Output :
8595 (i.e 345+7905+345)
I don't really get what you mean, but I'm amusing that you're trying to add all values in a List. To do this:
int result = 0;
for(int i : itemsAmount)
{
result+=1;
}
System.out.println(result);//This should print 8595.
In general Map<Key,List<Value>> structures end up needing code that looks as follows:
public addValue(Key key, Value value) {
if (!map.containsKey(key)) {
map.put(key, new ArrayList<>());
}
map.get(key).add(value);
}
In your case you should replace orderItemDetails.put with similar code.
Alternatively you could use a true Multimap from a third party library such as guava.
Summing the values would simply be:
map.get(key).stream().sum();
Assuming that the values are List which makes the stream an IntStream.
I have an object as Riziv with three variables as id, cnk and product. Then I search in a databank for this object and add it to a ArrayList as ArrayList<Riziv> list.
Now I should checkout if all object in his array are the same cnk then return true otherwise I should return all objects which are not the same cnk with error message.
public class Riziv{ String id, cnk, product; }
ArrayList<Riziv> list = getArrayListFromDatabank(id);
public void getDuplicatedWhichHasTheSameCnk(){
}
}
Using standard JVM structures (MultiMap is provided by guava), you can do that:
public List<Riviz> getDuplicates(final List<Riviz> l)
{
final HashMap<String, List<Riviz>> m = new HashMap<String, List<Riviz>>();
final List<Riviz> ret = new ArrayList<Riviz>();
String cnk;
for (final Riviz r: l) {
cnk = r.getCnk();
if (!m.contains(cnk))
m.add(cnk, new ArrayList<Riviz>());
m.get(cnk).add(r);
}
List<Riviz> tmp;
for (final Map.Entry<String, List<Riviz>> entry: m.entrySet()) {
tmp = entry.getValue();
if (tmp.size() == 1) // no dups
continue;
ret.addAll(tmp);
}
return ret;
}
ret will contain the duplicates. You can change that function to return a Map<String, Riviz> instead, and filter out entries where the list size is only one. You'll then get a map with the conflicting cnks as keys and a list of dups as values.
I am not clear exactly what you want however I suspect you want something like this.
MultiMap<Key, Riziv> multiMap =
List<Riziv> list =
for(Riziv r: list)
multiMap.put(r.getCnk(), r);
for(Key cnk: multiMap.keySet()) {
Collection<Riziv> sameCnk = multiMap.get(cnk);
// check size and compare entries
}
The multi-map will have the list of Riziv objects for each Cnk.
One way to do it is write a comparator to sort the list by cnk String and then compare each consecutive cnk String to the next, if you find a duplicate, they will be right next to eachother.
1.) Sort the list using a comparator by sorting on the cnk variable.
2.) Compare each element in the list to the next for duplicates.
There's probably many other ways to solve this, this is just the first that came to mind.
I did not test this so you have been forewarned lol:
ArrayList<Riziv> rizArray = new ArrayList<Riziv>();
//Sort the array by the CNK variable.
Collections.sort(rizArray, new Comparator<Riziv>(){
#Override
public int compare(Riziv arg0, Riziv arg1) {
//Return the comparison of the Strings.
//Use .compareToIgnoreCase if you want to ignore upper/lower case.
return arg0.getCnk().compareTo(arg1.getCnk());
}
});
//List should be in alphabetical order at this point.
List<Riziv> duplicates = new ArrayList<Riziv>();
Riziv rizPrevious = null;
for(Riziv riz: rizArray){
if(rizPrevious == null){
rizPrevious = riz;
continue;
}
if(riz.getCnk().compareTo(rizPrevious.getCnk()) == 0){
duplicates.add(riz);
}
rizPrevious = riz;
}