Вопрос

I'm in the process of trying to build a for comprehension in Scala, but am running into some issues when I try to use a more complex filter.

I'm aware of basic for comprehension filtering:

for (x <- 1 until 20 if x>3) yield {
  x
}

However, I want to build a for comprehension which has a much more complex filtering statement. For example, here's an illustrative version of what I'm trying to do:

for (
    element <- elementList
    val otherElement = databaseCall.getMatching(element.id)
    if element.name==otherElement.name
) yield {
  element
}

Basically, if you want to do a filter with some more complex requirements, this gets awkward, because the for comprehensions don't allow declaring vals in the filter statement, and you'd otherwise have to fit that all one one line.

An alternative would be to not use the filter mechanism at all, and just yield either Some(element) or None, and end up with an Option[elementType] list instead. However, I don't want to have Optional types in this case.

Imperatively, I'd simply create a mutable list, and only append to the list when my condition is met, but I want to see how to do this in more of a declarative manner (not all of the way declarative yet, but I'm still learning!).

Any suggestions of good declarative ways to do this would be hugely helpful.

Это было полезно?

Решение

Is using the filter method of List not the simplest solution in this case?:

elementList.filter(el => databaseCall.getMatching(el.id).name == el.name)

Другие советы

Looks like you're doing something which is a map with side effects. These side effects can lead to exceptions, which in turn can lead to a result list you'd probably like to avoid. So use a closure around what you're attempting to do in the scope of what you want to do:

 def outerScope(elementList: List[YourType]): List[YourType] =
   def dbAction(element: YourType): Option[YourType] = try{
     val other = databaseCall getMatching element.id //assuming can't return null
     if(other.name == element.name) Some(element) else None
   }
   catch{
     case ex: Exception => None //don't forget to log it
   }

   for{
     elem <- elementList
     value <- dbAction(elem)
   } yield value
 }

As you've figured out, making the for comprehension contain and do everything sometimes leads to rather verbose or confusing statement. For comprehensions should be readable and intuitive in use. Separate out the conditions so that you can compose over the results you desire, even if it means all the logic isn't going to be in the comprehension.

Side note: If the DB query can return a null then a better method is to just wrap the call in an Option.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top