2

Hoping for some guidance.

Consider this snippet:

val q = (for {
    (d, o) <- dx innerJoin ox on (_.user === _.id)
   } yield(d,o))

  "div" #>  q.map { case (x, y) =>
  {
       ".dF1 *" #> x.name &
       ".dF2 *" #> y.id
  }
}

in this query, I have two tables, where table "ox" is a list of people, and "dx" is a list of items associated with those people. As it is written, it works good, but I end up creating one row for each item that a person has. So assume three users, first two have two items, and last one has 1, i get five rows:

<div class="dF1">[user1]</div><div class="dF2">[item1]</div>
<div class="dF1">[user1]</div><div class="dF2">[item2]</div>
<div class="dF1">[user2]</div><div class="dF2">[item1]</div>
<div class="dF1">[user2[</div><div class="dF2">[item2]</div>
<div class="dF1">[user3]</div><div class="dF2">[item3]</div>

What I'd like to do is create a single row for each user, and inside of the dF2 field create multiple divs, one for each item. The layout would then be:

<div class="dF1">[user1]</div><div class="dF2">[item1] [item2]</div>
<div class="dF1">[user2]</div><div class="dF2">[item1] [item2]</div>
<div class="dF1">[user3]</div><div class="dF2">[item1]</div>

How can I do this? Do I need to use an intermediary collection?

Will I Am
  • 2,614
  • 3
  • 35
  • 61
  • You don't say explicitly, but from a previous question it looks like you're using Slick. Any reason you're not using groupBy on the query before executing it? – waffle paradox Oct 04 '13 at 11:39
  • I do use slick - I will revisit the group-by approach, previously I gave up a little on it because it was creating an odd/unoptimal looking query which may not be fixed after redoing my schema, and because I had trouble processing the result of a groupBy. – Will I Am Oct 04 '13 at 16:44

1 Answers1

4

You have two options here, you can either use the database to return a collection grouped by a field or you can use Scala to group your collection. If you use the database, you will be limited to grouping by a field, like x.name, but your execution will likely be more efficient. If you group with Scala you'll be able to group by the entire object x, but you will end up doing more processing and object creation. You'd need to decide which is best for you.

Now, that said - let's assume we're using Scala. You would do something like this:

"div" #>  q.groupBy(_._1).map { case (x, y) =>
  {
       ".dF1 *" #> x.name &
       ".dF2 *" #> y.map{ case (x2, y2) => y2.id}.mkString(" ") 
  }
}

Instead of working directly with the List containing D and O from your query, the groupBy will create a list of type: Map[D, List[(D, O)]]. In the example above, I am just combining the id field of your o into a single string to output which looked like the example you requested.

Instead of a single string, you could also have your second map return a CssSel to do further transformations. This example which will look for <div class="dF2SubDiv"></div> nested in the div that has the class dF2:

"div" #>  q.groupBy(_._1).map { case (x, y) =>
  {
       ".dF1 *" #> x.name &
       ".dF2" #> y.map{ case (x2, y2) => 
           ".dF2SubDiv *" #>  y2.id
        }
  }
}

If you would rather use Slick to do the groupBy, you could find more info on it here and here.

Community
  • 1
  • 1
jcern
  • 7,798
  • 4
  • 39
  • 47
  • Thank you. Going to give groupBy on more try with a newly optimized schema and see how it works out. I do agree the groupBy approach would be ideal. – Will I Am Oct 04 '13 at 16:48
  • Just one observation, the reason why I think was not working before for me is because I grouped the sql way, e.g. groupBy(_._1.fieldname) instead of groupBy(_._1). – Will I Am Oct 04 '13 at 17:34
  • A couple of questions: I had to add a .list to my yield(d,o) again, otherwise I'd get a "cannot find implicit value for parameter computer / CanBind" and mkString would be undefined. Once I added the '.list' it was fine. Also, can I use your first approach to insert DIVs, or your second approach when I don't know the number of DIVs at design time? – Will I Am Oct 04 '13 at 17:36
  • The right hand side of the `#>` takes a `NodeSeq`, or any expression for which there is an implicit conversion to one. So, if you added a blank `span` tag inside of `.dF2`, you could do something like: `".dF2 *" #> y.map{ case (x2, y2) => "span" #>
    { y2.id }
    }`. If you just wanted to insert it into the `div` with no modification, it's a little trickier - but this would accomplish it for you: `".dF2 *" #> { ns:NodeSeq => y.map{ case (x2, y2) =>
    { y2 }
    }}`.
    – jcern Oct 04 '13 at 17:56
  • @WillIAm , what you wrote without the .list is a query object. It doesn't actually hit the db and get the data you want until you execute the query with .list, .firstOption, etc. Check out the docs for the Invoker trait. – waffle paradox Oct 04 '13 at 20:43
  • thanks both, for the help and also for the details explanation. – Will I Am Oct 04 '13 at 21:45