1

How can I merge these two Coffee queries into one and indicate whether or not it's coffee or otherCoffee?

Code:

protected lazy val coffeeQuery = for {
    id <- Parameters[Long]
    coffee <- CoffeesTable if coffee.userId === id
} yield (coffee)

protected lazy val otherCoffeeQuery = for {
    id <- Parameters[Long]
    employment <- EmployeesTable if employment.employedId === id
    sup <- SuppliersTable if employment.supId === sup.id
    coffee <- CoffeesTable if (coffee.userId === id || coffee.userId === sup.userId)
} yield (otherCoffee)

Now I would like to get the list of coffees & distinguish whether it's the otherCoffee or not:

def coffeeList(userId: Long): JValue =  withSession { implicit db: Session =>
    val coffee = coffeeQuery(userId)
    val otherCoffee = otherCoffeeQuery
    val coffeeUnion = coffee union otherCoffee
    // How would I efficiently distinguish which item is "coffee" and which is "otherCoffee"?
    ("coffee" -> 
        coffeeUnion.map { c =>
           ("name" -> c.name) ~
           ("coffee_or_other_coffee" -> // is this coffee or otherCoffee?
        }
    )
}

I'm open to other ways of tackling this (doesn't have to be Unions)

goo
  • 2,230
  • 4
  • 32
  • 53

1 Answers1

2

You can map the query results into tuples containing e.g. 'coffee / 'otherCoffee (or true / false etc)

val coffee = Set(coffeeQuery(userId):_*)
val otherCoffee = Set(otherCoffeeQuery:_*)
val coffeeUnion = coffee.map(_ -> 'coffee) ++ otherCoffee.filterNot(coffee.contains(_)).map(_ -> 'otherCoffee)
// coffeeUnion is a Set[Tuple2[Coffee, Symbol]]; you can make this e.g. a Set[Tuple2[Coffee, String]] instead via
// coffee.map(_ -> "coffee") ++ otherCoffee.filterNot(coffee.contains(_)).map(_ -> "otherCoffee")
("coffee" ->
  coffeeUnion.map { tuple =>
    ("name" -> tuple._1.name) ~
    ("coffee_or_other_coffee" -> tuple._2)

You don't need to use Sets, but they'll make the filterNot faster compared to using Seqs

You can also do this just using Sets without tuples

val coffee = Set(coffeeQuery(userId):_*)
val otherCoffee = Set(otherCoffeeQuery:_*)
val coffeeUnion = coffee union otherCoffee
("coffee" ->
  coffeeUnion.map { c =>
    ("name" -> c.name) ~
    ("coffee_or_other_coffee" -> if(coffee.contains(c)) 'coffee else 'otherCoffee)
Zim-Zam O'Pootertoot
  • 17,888
  • 4
  • 41
  • 69
  • Is `tuple._2` still returning a `coffee`? i.e. `tuple._2.name`? – goo Feb 19 '14 at 03:47
  • @Jon `tuple._1` returns a `Coffee`, while `tuple._2` returns a `'coffee` or `'otherCoffee` which are of type `Symbol` http://www.tutorialspoint.com/scala/scala_tuples.htm - a tuple is sort of like an array except that it uses 1-based indexing, it's limited to 22 elements (this restriction should be lifted in Scala 2.11), and most importantly its elements can be of different type, in this case `Tuple2[Coffee, Symbol]` or `Tuple2[Coffee, String]` or `Tuple2[Coffee, Boolean]`, etc. `tuple._1` access the `Coffee` element while `tuple._2` access the `Symbol` (or `String` or `Boolean`) element – Zim-Zam O'Pootertoot Feb 19 '14 at 15:01
  • I can still do tuple._2.name & it will compile – goo Feb 19 '14 at 18:59