1
\$\begingroup\$

I wonder if there is a more "functional" way of implementing something like this:

@months.zipWithIndex.foreach { case (month, index) =>
    if (index%3 == 0) {
        <tr>
    }
    <td>@month</td>
    if (index%3 == 0) {
        </tr>
    }
}

Can these two if-statements be removed by using something other than foreach?

\$\endgroup\$
  • \$\begingroup\$ There are many ways, of which my answer is one example, but the key is to structure your code around rows rather than months. Unless the list of months is empty, you will always be generating at least one row and so at least one pair of <tr></tr> tags, so there should be nothing conditional about them. \$\endgroup\$ – itsbruce Oct 8 '13 at 13:45
  • 1
    \$\begingroup\$ Is there not an error in your code? It would generate <td>Jan</td><td>Feb</td><tr><td>Mar</td></tr> and not <tr><td>Jan</td><td>Feb</td><td>Mar</td></tr>. I've assumed the latter is what you actually intended. \$\endgroup\$ – itsbruce Oct 9 '13 at 15:08
3
\$\begingroup\$

I am not sure which framework you are using, but this is how you could do it using Scala´s native XML library:

 <div>
 { for (row <- months grouped 3) yield (<tr> { for (month <- row) yield (<td> {month} </td>) } </tr>) }
 </div>

Rendered slightly more prettily, the code looks like this:

for (row <- months grouped 3) yield {
  <tr> 
  { for (month <- row) yield (<td> {month} </td>) }
  </tr>
}

Which hopefully makes the structure clear.

If your framework works with yield, all you need to do is add @ in the appropriate places. If it does not, you might have to do something like this:

for (row <- @months grouped 3) {
  <tr>
  for (month <- @row) {
    <td> @month </td>
  }
  </tr>
}

Your code is structured only around months, so has to use conditional logic to create your rows. My code is structured around rows (and within that around months), so needs no conditionals. My code also only has the value for the number of months in one place, which is less error prone and makes it easier to change the number of months in a row.

You don't have to use a list comprehension, though. The important thing is simply to slice the list up into groups of 3 (or n, where n is the number of months you want in each row) and iterate over those.

\$\endgroup\$
0
\$\begingroup\$

I don't believe there's any kind of functional looping structure that will allow you to obtain this behavior without the 'if' statements. The fact is, you have to include something conditionally, and so you have to check that condition somehow. This is a bit more idiomatic way to do it, I believe, and I think it's prettier:

@months.zipWithIndex.foreach { 
    case (month, index) if (index % 3 == 0) => <tr><td>@month</td></tr>
    case (month, _) => <td>@month</td>
}

I'm guessing you're using Play or something like that based on the @months. I've never used Play much so I'm not sure what the return type needs to be for a function like this but it might be possible to do something along the lines of

@months.zipWithIndex.foreach { case (month, index) => 
    @str = <td>@month</td>
    if(index % 3 == 0) @str = "<tr>" + @str + "<\tr>"
    @str
}

but I don't personally think that's helping anything. Hope that was helpful.

\$\endgroup\$
  • \$\begingroup\$ Of course you can remove the if statements. As long as @months is not empty, this code is going to create at least one row, each of which will be bounded by <tr> and </tr>, so there should be no need for if at all. \$\endgroup\$ – itsbruce Oct 8 '13 at 13:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.