0

I have a JSP that I am displaying a list of items on. The list of items will be placed into vertical columns. Each row should have (length of list)/(total number of rows), rounding up if there is a remainder. Right now we are doing two columns, but we want the freedom to change the number of columns.

My work doesn't like scriptlets, so what is the best way to divide a list into equal columns using JSTL?

Alonso Dominguez
  • 7,750
  • 1
  • 27
  • 37
Joe
  • 7,922
  • 18
  • 54
  • 83
  • Not sure i understand your problem, can you elaborate, maybe with some example data and how you roughly want it laid out? – Chris White Mar 24 '12 at 00:53
  • It's hard to understand/visualize what you actually are trying to achieve. I think it's the best if you show how the resulting HTML should look like. Then it's easier to propose how the JSP/JSTL code should look like to generate exactly that HTML. – BalusC Mar 24 '12 at 15:10
  • So if the number of items in the list turns out to be 7, I want:
    item 1
    item 2
    item 3
    item 4
    item 5
    item 6
    item 7
    – Joe Mar 25 '12 at 02:04
  • Doesn't my answer solve the problem? – Eugene Retunsky Mar 25 '12 at 16:06
  • @EvgenyRetyunsky, it was a good contribution, but Alsonso's answer is what I was getting at. – Joe Mar 25 '12 at 23:41
  • @Joe - with all respect, Alonoso's is more complicated while it supposed to do the same thing. – Eugene Retunsky Mar 26 '12 at 02:04
  • I voted your answer up, but accepted the other. Your answer doesn't account for not know the length of the list ahead of time. – Joe Mar 26 '12 at 18:04

3 Answers3

3

You can do something like this:

        <c:forEach items="${elements}" step="5" varStatus="i">
            <tr>
                <c:forEach begin="0" end="4" varStatus="j">
                <c:set var="index" value="${i.index * 5 + j.index}"/> 
                    <td>
                       ${index < fn:length(elements) ? elements[index] : ""}
                    </td>
                </c:forEach>
            </tr>
        </c:forEach>

This example is for "5" elements in a row. You can change it to any other value.

Eugene Retunsky
  • 13,009
  • 4
  • 52
  • 55
  • How about if you know that the outer loop should have 5. But you need to calculate the number of items in the inner loop? I am having trouble with that because I am trouble doing division in jstl and rounding up. Also, can the last loop stop when it runs out of elements. – Joe Mar 25 '12 at 02:00
  • A simple trick: ${(fn:length(elements) + 4) / 5} It will round up. – Eugene Retunsky Mar 25 '12 at 02:18
  • you can not make a loop to stop unless you enclose it in a tag for the "ArrayIndexOutOfBoundsException" as there is not any "" tag (or something similar) – Alonso Dominguez Mar 25 '12 at 02:46
  • BTW, you this code is working and doesn't crash. ${elements[i.index * 5 + j.index]} prints an empty string if it exceeds the length of the list. – Eugene Retunsky Mar 25 '12 at 02:53
  • from my point of view it's buggy, it's relying on the implementation of the JSTL standard to be smart enough to ignore an index that's gone out of bounds instead being neat enough to avoid such situation. – Alonso Dominguez Mar 25 '12 at 03:07
  • Alonso, if you want to be neat then: ${index < fn:length(elements) ? elements[index] : ""} - is a simple solution for this. – Eugene Retunsky Mar 25 '12 at 05:27
  • sorry @Evgeny, you got me wrong, I just wanted to give enough clean code to allow any kind of nested content needed to show "the item" and avoiding possible bugs, the final decision is up to the OP. Ah!, and I'm not in a competition to get reputation points... – Alonso Dominguez Mar 26 '12 at 17:28
2

I will give you a similar answer to the one from Evgeny but a bit more accurate. But I need to make the following assumptions:

  • Each of your rows should have a rounded value of (number of items / number of columns) instead of (length of list / total number of items) since in that statement your columns will always have 1 item.
  • Your JSP context has following attributes:
    • values: a collection or array containing all the elements
    • numRows: An integer value defining the number of rows to distribute your list

First of all, since you can not use any scriptlet, you will need the following taglib declarations:

<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>  
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>

Now, to build your grid of items you will write something similar to this:

<table>
   <c:forEach begin="0" end="${numRows - 1}" varStatus="i">
      <c:set var="rowStart" value="${i.index * numColumns}" />
      <tr>
          <fmt:formatNumber var="numColumns" value="${fn:length(values) / numRows}"
             maxFractionDigits="0" />
          <c:forEach begin="0" end="${numColumns - 1}" varStatus="j">
             <c:set var="index" value="${rowStart + j.index}"/>
             <td>
                <c:choose>
                   <c:when test="${index lt fn:length(values)}">
                      <%-- Replace following code with the one needed to display your item --%>
                      <c:out value="${values[index]}" />
                   </c:when>
                   <c:otherwise>&nbsp;</c:otherwise>
                </c:choose>
             </td>
         </c:forEach>
      </tr>
   </c:forEach>
</table>

In this example I am first rounding the value of the division to calculate the number of rows to render (see answer How do I round a number in JSTL?). Then the iteration through items to generate rows and columns start almost exactly as Evgeny did. The main difference resides in that you will need to perform an index check before accessing your list and in case your index is out of bounds, just render a "non breaking space" so your table will be "HTML correct".

Community
  • 1
  • 1
Alonso Dominguez
  • 7,750
  • 1
  • 27
  • 37
0

In c:if tag change the value of 4 to any number(how many columns you want)

 <tr>
    <c:forEach items="${list}" var="item" varStatus="mycount">
    <td>${item}</td>
    <c:if test="${mycount.count % 4 eq 0}">
    <c:out value="</tr><tr>" escapeXml="false"></c:out>
    </c:if>
    </c:forEach>
 </tr>
bhagat
  • 29
  • 5