Currently, I have a map with the below entries:
Map<LocalDateTime, Integer> resultMap = new HashMap<LocalDateTime, Integer>();
//Put localdatetime and 30mins split indicator
for(Map.Entry<LocalDateTime, Integer> entry: resultMap.entrySet()) {
System.out.println(entry.getKey() " : " entry.getValue());
}
2022-03-29T00:30 : 1
2022-03-29T01:00 : 2
2022-03-29T01:30 : 3
2022-03-29T02:00 : 4
2022-03-29T02:30 : 5
2022-03-29T03:00 : 6
2022-03-29T03:30 : 7
.
.
.
2022-03-29T23:30 : 47
2022-03-30T00:00 : 48
How do I find if the current LocalDateTime
falls in between the times?
For example, if the current time is 2022-03-29T01:27
need to get the value 3
.
I was able to do it with an iterator. Any other efficient way to do this?
Iterator keyIterator = resultMap.entrySet().iterator();
Map.Entry<LocalDateTime, Integer> perviousEntry = new AbstractMap.SimpleEntry<LocalDateTime, Integer>(null, 0);
while(keyIterator.hasNext()) {
Map.Entry<LocalDateTime, Integer> NextEntry =
(Entry<LocalDateTime, Integer>) keyIterator.next();
if(null != perviousEntry.getKey()) {
if(LocalDateTime.now().isBefore(NextEntry.getKey()) && LocalDateTime.now().isAfter(perviousEntry.getKey())){
System.out.println(NextEntry.getKey() " Value : " NextEntry.getValue());
}
}
perviousEntry = NextEntry;
}
CodePudding user response:
You can retrieve the value in O(log n) time by utilizing a TreeMap
.
To find out which dateTime object is the closet to the given, we need to compare the two keys:
lo
- obtained withfloorKey()
(<=
to the given dateTime);hi
- obtained withceilingKey()
(>=
to the given dateTime).
To compare the lo
and hi
we can make use of the Duration
class, which models the quantity of time. Its static method between()
allows to get a Duration
object representing the interval between the two temporal objects.
Below is a complete example of how it can be achieved:
public class DataTimeStorage {
private final NavigableMap<LocalDateTime, Integer> storage = new TreeMap<>();
public int getClosest(LocalDateTime dateTime) {
LocalDateTime lo = storage.floorKey(dateTime);
LocalDateTime hi = storage.ceilingKey(dateTime);
if (lo == null && hi == null) throw new IllegalArgumentException();
if (lo == null) return storage.get(hi);
if (hi == null) return storage.get(lo);
return diffInMillis(lo, dateTime) < diffInMillis(dateTime, hi) ?
storage.get(lo) : storage.get(hi);
}
public long diffInMillis(LocalDateTime dateTime1, LocalDateTime dateTime2) {
return Duration.between(dateTime1, dateTime2).toMillis();
}
public void addItemsForDay(LocalDate day) {
int count = 0;
for (LocalDateTime i = day.atTime(0, 0);
i.toLocalDate().equals(day);
i = i.plusMinutes(30))
storage.put(i, count );
}
@Override
public String toString() {
return storage.toString();
}
}
main()
- demo
public static void main(String[] args) {
DataTimeStorage storage = new DataTimeStorage();
storage.addItemsForDay(LocalDate.of(2022,03,29));
System.out.println(storage.getClosest(
LocalDateTime.of(2022,03,29,01,27)));
}
Output
3
CodePudding user response:
First idea: If you know that all the time values in your map are either 00 min or 30 min You could try to work on a currentTimeRoundedToTheNext00or30min value that will exist in your map.
Ex: (pseudo code as i'm not familiar with LocalDateTime)
LocalDateTime currentTimeRoundedToTheNext00or30min = currentTime;
if (0 < currentTime.minutes < 30) {
currentTimeRoundedToTheNext00or30min.setMinutes(30);
} else {
currentTimeRoundedToTheNext00or30min.setMinutes(0);
currentTimeRoundedToTheNext00or30min.addHour(1);
}
int result = yourMap.get(currentTimeRoundedToTheNext00or30min);
Second idea: If the time values used as keys in your map are sorted, your need can also be seen as "finding the first key of the map that is greater than the current time". By going through the map keys, comparing the key to the currentTime, and getting out of the loop as soon as one is greater than your tested currentTime value, you can solve the issue
Third idea: Without even using the map, there is a simple way of finding the expected value based on currentTime's hour and minute values. (also pseudo code as i'm not sure of syntaxes using LocalDateTime functions)
LocalDateTime currentTime = LocalDateTime.now();
Integer result = currentTime.getHours() * 2;
if (0 < currentTime.getMinutes() <= 30) {
result = 1;
} else {
result = 2;
}
This can be seen as "counting the number of 30-minutes sections that have been started since the beginning of the day"
CodePudding user response:
To find if the current LocalDateTime falls in between the times
it is possible to use
LocalDateTime::isBefore along with Duration::between. As the first step we can find out all dates that happened before the current time and among these dates, we can find out the ones that happened within last 15 seconds (or any other meaningful time slot).
Something similar to
resultMap.entrySet().stream()
.filter( entry -> {
val now = LocalDateTime.now();
return (entry.getKey() == now) || entry.getKey().isBefore(now) &&
Duration.between(entry.getKey(),now()).toSeconds() == 15)
)
.forEach( entry -> System.out.println(entry.getKey() " : " entry.getValue());
CodePudding user response:
You’re correct that NavigableMap is a good approach:
NavigableMap<LocalDateTime, Integer> sorted = new TreeMap<>(resultMap);
Map.Entry<LocalDateTime, Integer> next = sorted.ceilingEntry(targetTime);
OptionalInt match;
if (next != null) {
match = OptionalInt.of(next.getValue());
} else {
match = OptionalInt.empty();
}
I’m not sure if this is the exact logic you want, but you can study the javadoc of ceilingEntry
, floorEntry
, higherEntry
and lowerEntry
and choose the ones that fit your need.