1

Recently I found myself writing either DB queries or simple Seq method chains which I would like to be dynamic based on user input, for instance:

def between(from: Option[DateTime], to: Option[DateTime]): Seq[MyObject] = { db.all() // let's say this returns Seq[MyObject] /* Here I want to either restrict the upper/lower bounds with from/to if they exist or take all the values otherwise */ } 

If I were lazy I'd just go with:

def between(from: Option[DateTime], to: Option[DateTime]): Seq[MyObject] = { if(from.isEmpty && to.isEmpty) db.all() else if(from.isEmpty) db.all().filter(_.date <= to.get) else if(to.isEmpty) db.all().filter(_.date >= from.get) else db.all().filter(_.date >= from.get && _.date <= to.get) } 

Obviously filter is just an example, I have the same problem with for instance take when I want to take n elements or all based on Option.

But what if I have more Options passed? What would be the idiomatic way of doing this in Scala?

I can go with pattern matching, but that's not extremely different to if/else?

I could make several vals and do from.map(...).getOrElse() but that introduces temporary vals and again doesn't look that much better than if/else.

Are there any other tricks I could use?

4
  • Just a question before trying to answer : is there really a meaning to between(None, Some(...) or between(None, None) ? I don't think it has any obvious semantic and I don't know what I should expect to be returned as a user, so I wonder if this method makes sense at all Commented Jul 17, 2015 at 3:01
  • @Dici well yes as I tried showing in the example, between(None, None) will return all the objects, between(None, Some(to)) will only put an upper bound, so all the objects that are older then to. Commented Jul 17, 2015 at 3:08
  • Yes that's the way you implemented it, but is it what anybody would expect ie is this a good API ? Commented Jul 17, 2015 at 3:09
  • @Dici yes it's much easier to use for my UI guys since they don't have to do any checks themselves Commented Jul 17, 2015 at 3:26

1 Answer 1

2

I don't think this is a good design to start with, because it violates the very meaning of the word between.

I would just have separate methods for each case, with clear semantics :

def between(from: DateTime, DateTime) def after(from: DateTime) def before(to: DateTime) def anytime() 

Otherwise, if you really want to keep it this way :

def between(from: Option[Date], to: Option[Date]) = { val datePredicate = (test: (Date,Date) => Boolean, date: Option[Date]) => (o: MyObject) => date.map(test(o.date, _)).getOrElse(true) val fromPredicate = datePredicate(_ >= _, from) val toPredicate = datePredicate(_ <= _, to) db.all().filter(x => fromPredicate(x) && toPredicate(x)) } 

It is more general and extendable, but very obscure.

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

5 Comments

this actually kind of misses the point. First of all ok I can change the name (just came up with something for the sake of an example), secondly I like to have a single endpoint it's actually easier for my UI guys - they don't have to do any checks whether the user input something or not. How are those 4 methods different to if/else? I need to do the cheks somewhere. Last but not least it was supposed to be a generic question, what if I want to have different filtering options? Something where I pass X conditions wrapped in option? I would have to create a method for every possibility.
the other solution I like much more thanks will have a look at it ;-)
suggested name : "inInterval(lowerBound, upperBound)". Having one of the bound (or even both) missing sounds normal.
@DidierDupont using the concept of interval is a nice suggestion, in that case I would even create a small class to represent it
@MateuszDymczyk I changed my answer, which was not quite correct. It should fix it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.