2

I need to obtain a collection that should be filled with datetime intervals between some input and LocalDatetime.now.

Example:

If I would to build a interval based on every 30 minutes, it should be something like 2017-06-12 00:30, 2017-06-12 01:00, 2017-06-12 01:30, 2017-06-12 02:00..and go on.

I've wrote a code that does my requirement but it looks like Java using Scala syntax - not a functional and cleaner way.

  val now = LocalDateTime.now
  val dates = if (interval.toInt > 0) {
    var tempDate = LocalDate.now.atStartOfDay()
    val allDates = scala.collection.mutable.ListBuffer[String]()
    while (tempDate.isBefore(now)) {
      allDates += tempDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
      tempDate = tempDate.plusMinutes(interval.toInt)
    }
    allDates
  } else {
    var initialDay = LocalDate.now.minusDays(30)
    val allDates = scala.collection.mutable.ListBuffer[String]()
    while (initialDay.isBefore(LocalDate.now)) {
      allDates += initialDay.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
      initialDay = initialDay.plusDays(1)
    }
    allDates
  }

How can I refactor this code to looks like functional style or at least remove that mutable variable?

1 Answer 1

5

You should use Scala's Stream type to build a lazy stream from the start date in half-hour increments, and then lazily take only as many dates from that which are earlier than the stop date. E.g.:

def halfHoursFrom(start: LocalDateTime): Seq[LocalDateTime] =
  Stream
    .iterate(start)(_.plusMinutes(30))
    .takeWhile(_.isBefore(LocalDateTime.now))

You will find the documentation for the Stream type in the standard library reference. The two notable methods we're using here are:

  • Stream.iterate (in the Stream companion object): builds an infinite stream lazily from a starting value and an increment function
  • Stream#takeWhile (in the Stream class): takes only as many elements from the given stream as match the given predicate (function which tests each element and returns true/false).

Note that I'm upcasting the return type to Seq[LocalDateTime] to hide the implementation details from the caller.

Sign up to request clarification or add additional context in comments.

2 Comments

This is a good answer. I'll just add one tip that tripped me up when I started using Scala: Stream is lazily-evaluated, but it does cache all elements it has computed, so it can use up memory. So this pattern is not always applicable when replacing a while loop + mutable var as it can lead to worse memory usage if not collecting results in every iteration. If you don't want to save intermediate results from every iteration Iterator.iterate is an equivalent that doesn't use memory in that way.
@JoeK good point, it's good to carefully note the exact requirements. In this case the request was for a collection of dates as the return value, but after giving it some thought the requestor may find that they can make do with an iterator over the date sequence.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.