Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add between, betweenBy functions to Seq #158

Open
VladKopanev opened this issue Nov 29, 2021 · 2 comments
Open

Add between, betweenBy functions to Seq #158

VladKopanev opened this issue Nov 29, 2021 · 2 comments

Comments

@VladKopanev
Copy link

It would be nice to have a function similar to SQL BETWEEN operator that can filter elements from a Seq that are in some range. This is not an uncommon task when working with statistics, time series, location etc.
We can implement it as an extension for Seq, roughly like this:

extension [T: Ordering](seq: Seq[T])
  def between(a: T, b: T): Seq[T] = seq.filter(e => Ordering[T].gt(e, a) && Ordering[T].lt(e, b))

and then use it like so:

Seq(1, 2, 3, 4, 5, 6 ,7, 7, 9, 10).between(4, 7) //will result in Seq(5, 6)

We could also make an extension that would allow filtering sequences of arbitrary product types that don't have an ordering, but have some fields that we can use for comparison, similar to sortBy function, for example:

extension [T](seq: Seq[T])
  def betweenBy[U: Ordering](a: U, b: U)(f: T => U): Seq[T] = seq.filter { e => 
    Ordering[U].gt(f(e), a) && Ordering[U].lt(f(e), b) 
}

locationDataList.betweenBy(hourAgoInstant, Instant.now)(_.timestamp)

IMHO this is much more intuitive, less prone to errors, and nicer to read than using a simple filter function.

P.S. naming is debatable here because I also can suggest using between on numeric types to check if they are in some range, e.g. 1.between(-1, 5) // returns true that would be useful addition too, but might cause confusion if paired with between on Sequences.

@julienrf
Copy link
Collaborator

Hi, thank you for writing this up! It seems to me that the gain would be relatively low, though.

If I understand correctly, the current way of achieving the same is:

xs.filter(x => x > 0 && x < 42)
xs.filter(x => x.foo.bar > 0 && x.foo.bar < 42)

And with your proposal, that would look like the following:

xs.between(0, 42)
xs.betweenBy(0, 42)(_.foo.bar)

Also, here we assume that between uses strict bounds, but often you want some of the bounds to be non-strict. You have this flexibility with filter: xs.filter(x => x >= 0 && x < 42).

@VladKopanev
Copy link
Author

Hi @julienrf, yes, the function is very simple, it will not add anything that you couldn't do on your own with filter, but I would argue that it's more descriptive for the code reader. It can be just, you know, that one nice little thing that library has here for you if you want it there and could maybe lift the level of satisfaction overall, but that's IMO. For non-strict bounds, we can either have a different version of it like betweenInclusive or just let the user resort to filter if they are hitting some corner cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants