4

I do result paging often (given a page number and a page size calculate start, end and total pages) and I ported this little function from Java to help:

def page(page: Int, pageSize: Int, totalItems: Int) = {
    val from = ((page - 1) * pageSize) + 1
    var to = from + pageSize - 1
    if (to > totalItems) to = totalItems
    var totalPages: Int = totalItems / pageSize
    if (totalItems % pageSize > 0) totalPages += 1
    (from, to, totalPages)
}

And on the receiving side:

val (from, to, totalPages) = page(page, pageSize, totalItems)

Although it works, I'm sure there are more readable and functional ways to do the same thing in Scala. What would be a more scala-like approach?

In particular, I'm trying to find a nicer way of saying:

var to = from + pageSize - 1
if (to > totalItems) to = totalItems

In Java I could do something like:

from + pageSize - 1 + (to > totalItems) ? 1 : 0;
Ike
  • 763
  • 1
  • 7
  • 17

2 Answers2

9

Half of the problem is identifying the pattern:

def pageCalc(page: Int, pageSize: Int, totalItems: Int) = {
    val pages = 1 to totalItems by pageSize
    val from = pages(page - 1)
    val to = from + pageSize - 1 min totalItems
    val totalPages = pages.size
    (from, to, totalPages)
}

Though, really, maybe you could just use a Range directly instead?

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • As a side note, it is impressive how many edits I made because of simple mistakes that led me to make the code much more complex before I simplified it to almost the same thing I started with. – Daniel C. Sobral Dec 17 '10 at 01:19
  • Thanks Daniel. I didn't know about 'by' in Range. Interesting application here. We're avoiding some arithmetic operations by creating a clever collection. I guess it's a matter of taste. Not sure about performance implications and scalability considerations for large numbers of totalItems but I don't think those would be relevant for most practical uses of this anyway. – Ike Dec 17 '10 at 04:40
  • @Ike Actually, all arithmetic operations are hidden by `Range`. When I do `pages(page - 1)`, it just makes pretty much the same computations you and Rex did -- and, so, have pretty much the same performance. In fact, `Range` is probably the most optimized collection in Scala. Anyway, the main advantage is that you don't need to "decode" the arithmetic to understand the code. The second main advantage the decreased chance of bugs. – Daniel C. Sobral Dec 17 '10 at 10:55
2

The easiest improvement is just to use functions instead of vars (and avoid shadowing the method name with the argument name, so it's clearer whether you are calling a recursive function or not):

def pageCalc(page: Int, pageSize: Int, totalItems: Int) = {
  val from = ((page - 1) * pageSize) + 1
  val to = totalItems min (from + pageSize - 1)
  val totalPages = (totalItems / pageSize) + (if (totalItems % pageSize > 0) 1 else 0)
  (from, to, totalPages)
}

The key changes are just using the min function instead of a separate var, and letting the if statement return 0 or 1 rather than having it update a var.

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • Thanks Rex. I agree with the function name change. I just threw something together quickly to make sure it compiled. I think the 'min' is a clear improvement and the 'if' solves the use of the var. It would have been nicer if there was something even more terse like ? : in Java. – Ike Dec 16 '10 at 22:07
  • @Ike - There isn't a ternary operator built in, but you can define your own pretty easily. See, for example, http://stackoverflow.com/questions/2705920 – Rex Kerr Dec 17 '10 at 02:47
  • 1
    I see that. Very interesting post. It does seem like an overly complex solution for "relatively little" gain (not even considering the performance implications raised in the comments). I think your proposed changes above strike the right balance. Thanks again. – Ike Dec 17 '10 at 04:18