I have a List of items with a (java.util.)Date property, and I want to create a DataSeriesItem for each day beginning from the oldest date up to now. It is for a chart series with a timeline.
The creation of that DataSeriesItem will look like this:DataSeriesItem seriesItem = new DataSeriesItem(Date, occurrenceCount);
Where the occurrenceCount
is the count of Items where their Date property matches that day. The first parameter can also be of type java.time.Instant
I have managed to find a way that works, but I am certain that my approach is very bad and could possibly be done with one stream, maybe two. However, I am a beginner in streams and could not do it with my knowledge.
Is this possible with stream? How would it probably look like approximately?
I'm not asking you to actually do my whole implementation anew, but only point me to the correct streamfunctions and mappings that you would use, and for bonus points an example of it.
Here is my ugly solution:
List<?> items = myItems;
Collection<Date> foundDates = new HashSet<>();
for (Object item : items) {
foundDates.add((Date)getPropertyValueFromItem(item, configurator.getDateProperty()));
}
//====== This is the part I am asking about ======//
Map<Instant, Integer> foundInstants = new HashMap<>();
foundDates.stream().sorted(Date::compareTo).forEach(date -> {
Calendar c = Calendar.getInstance();
c.clear(); // clear nanoseconds, or else equals won't work!
c.set(date.getYear()+1900, date.getMonth(), date.getDate(), 0, 0, 0);
if(!foundInstants.containsKey(c.toInstant())){
foundInstants.put(c.toInstant(), 1);
} else {
// increment count of that entry
Integer value = foundInstants.get(c.toInstant());
foundInstants.remove(c.toInstant());
foundInstants.put(c.toInstant(), ++value);
}
});
//====== Leaving this code here for context ======//
// Could this maybe simplyfied too by using streams ?
// find oldest date
Date dateIndex = foundDates.stream().min(Date::compareTo).get();
Date now = new Date();
// starting from oldest date, add a seriesItem for each day until now
// if dateOccurrences contains the current/iterated date, use it's value, else 0
while(dateIndex.before(now)){
Calendar c = Calendar.getInstance();
c.clear();// clear nanoseconds, or else equals won't work!
c.set(dateIndex.getYear()+1900, dateIndex.getMonth(), dateIndex.getDate(), 0, 0, 0);
if(foundInstants.containsKey(c.toInstant())){
ExtendedDataSeriesItem seriesItem = new ExtendedDataSeriesItem(c.toInstant(), foundInstants.get(c.toInstant()));
seriesItem.setSeriesType("singleDataPoint");
series.add(seriesItem);
} else {
ExtendedDataSeriesItem seriesItem = new ExtendedDataSeriesItem(c.toInstant(), 0);
seriesItem.setSeriesType("singleDataPoint");
series.add(seriesItem);
}
c.add(Calendar.DATE, 1); // adding a day is complicated. Calendar gets it right. Date does not. This is why I don't use Date here
dateIndex = c.getTime();
}
You can use groupingBy()
and then use the downstream collector counting()
.
Map<Date, Long> occurrances = dateList.stream().collect(
groupingBy(d -> yourTransformation(d), counting()));
It should be easy enough to create your DataSeriesItem
objects from that map.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With