2

Lets say I have some data called 'people' in an array past into a twig template like this:

firstname | surname | colour
Fred        Smith     Blue
James       Holmes    Red
Sarah       Fisher    Blue
Chrstine    Jenkins   Yellow
Sid         Wells     Red
Cory        Simpson   Blue
Laura       Jones     Yellow

With this data i need to group them by the 'colour' column. by wrapping a div around the users based on there colour. e.g

<div class="colour_container">
Fred Smith - Blue<br>
Sarah Fisher - Blue<br>
Cory Simpson - Blue<br>
</div>

<div class="colour_container">
James Holmes - Red<br>
Sid Wells - Red<br>
</div> 

<div class="colour_container">
Christine Jenkins - Yellow<br>
Laura Jones - Yellow<br>
</div>

now if I use a twig loop, it puts the div around each name rather than grouping them by colour. Whats the easiest way to get the above output? Ive tried all sorts of things in the loop but I am struggling.

{% for p in people %}
   <div class="colour_container">
       {{ p.firstname }} {{ p.surname }} - {{ p.colour }}
   </div>
{% endfor %}

I need it to somehow loop through unique colour values first then loop through the names that belong to that colour.

dreftymac
  • 31,404
  • 26
  • 119
  • 182
azzy81
  • 2,261
  • 2
  • 26
  • 37

7 Answers7

6

I've had similar problem recently. I have made extension and published it as open source. It introduces lambda expressions and |group_by filter.

https://github.com/dpolac/twig-lambda

With that you can simply write:

{% for colour, group in people|group_by(=> _.colour) %}
   <div class="colour_container">
       {% for person in group %}
           {{ person.firstname }} {{ person.surname }} - {{ person.colour }}
       {% endfor %}
   </div>
{% endfor %}
Damian Polac
  • 911
  • 9
  • 20
3

First, create the color list according to your products array:

$colors = array();
foreach ($people as $p)
{
  if (!in_array($p['colour'], $colors))
  {
    $colors[] = $p['colour'];
  }
}

// ...

$twig->render("view.html.twig", array(
    'colors' => $colors,
    'products' => $products
));

Then, iterate throught your colors, and display products that match the current color:

{% for color in colors %}
   <div class="colour_container">
   {% for p in people %}
       {% if p.colour == color %}
         {{ p.firstname }} {{ p.surname }} - {{ p.colour }}<br/>
       {% endif %}
   {% endfor %}
   </div>
{% endfor %}

This should give you the expected result.

Alain Tiemblo
  • 36,099
  • 17
  • 121
  • 153
2

I'm not recommending you do this as this feels a bit labour intensive to be all done in the template, but it's quite good fun coming up with twig solutions so I've done one anyway to answer your question.

{# create array of handled people so you don't have to loop through the all the people for each colour #}
{% set handledPeople = [] %}
{% for person in people if person not in handledPeople %}
    <div class="colour_container">
        {% for p in people if p.colour == person.colour and p not in handledPeople %}
            <p>{{ p.firstname }} {{ p.surname }} - {{ p.colour }}</p>
            {% set handledPeople = handledPeople|merge([p]) %}
        {% endfor %}
    </div>
{% endfor %}
Mark
  • 1,754
  • 1
  • 12
  • 14
  • Cheers for your answer Mark. I came up with a similar solution but was hoping there was a much easier solution. I did find a good way of doing it. solution posted above =D thanks again =) – azzy81 May 12 '14 at 19:55
  • This solution is insane! Had no idea that it is possible to combine for with if. And it's still extreme readable. YMMD! – Matthias Kleine Mar 23 '17 at 10:59
2

It wasn't all that bad to do really. I did want to avoid running the for loop multiple times but hey ho. I wasn't aware you could put conditions apart of a for loop (http://twig.sensiolabs.org/doc/tags/for.html). Because of this I did the following:

<div class="colour_container">
{% for p in people if p.colour == "Blue" %}
   {{ p.firstname }} {{ p.surname }} - {{ p.colour }}
{% endfor %}
</div>

<div class="colour_container">
{% for p in people if p.colour == "Red" %}
   {{ p.firstname }} {{ p.surname }} - {{ p.colour }}
{% endfor %}
</div>

<div class="colour_container">
{% for p in people if p.colour == "Green" %}
   {{ p.firstname }} {{ p.surname }} - {{ p.colour }}
{% endfor %}
</div>

Because I know the full range of colours I just had to repeat the block for each colour and voila it works a treat =D

azzy81
  • 2,261
  • 2
  • 26
  • 37
0

You can use batch filter.

{% set items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] %}

<table>
{% for row in items|batch(3, 'No item') %}
    <tr>
        {% for column in row %}
            <td>{{ column }}</td>
        {% endfor %}
    </tr>
{% endfor %}
</table>
Oleg
  • 121
  • 1
  • 2
0

You can achieve this with Twig using a combination of the column, keys and filter filters.

Although undocumented, Twig's column filter actually accepts a second key parameter. Using this you can create an array with unique colour keys, and with keys convert it into an array.

{% set colours = people | column('colour', 'colour') | keys %}

You can then loop colours, and for each iteration filter people by each colour.

{% for person in people | filter(o => o.colour == colour) %} 
    ... 
{% endfor %}

Putting it all together

{% for colour in people | column('colour', 'colour') | keys %}
    <div class="colour_container">
    {% for person in people | filter(o => o.colour == colour) %}
        {{ person.firstname }} {{ person.surname }} - {{ person.colour }}
    {% endfor %}
    <div>
{% endfor %}
nick
  • 3,544
  • 1
  • 26
  • 22
-2

hello is this really what you want :

{% set firstname=['Fred','James','Sarah','Chrstine','Sid','Cory','Laura'] %}
{% set surname=['Smith','Holmes','Fisher','Jenkins','Wells','Simpson','Jones'] %}
{% set colour=['Blue','Red','Blue','Yellow','Red','Blue','Yellow'] %}
<div class="colour_container">
{{ firstname[0]~' '~surname[0]~' '~colour[0] }}
<br>
{{ firstname[2]~' '~surname[2]~' '~colour[2] }}
<br>
{{ firstname[5]~' '~surname[5]~' '~colour[5] }}
<br>
</div>
<div class="colour_container">
{{ firstname[1]~' '~surname[1]~' '~colour[1] }}<br>
{{ firstname[4]~' '~surname[4]~' '~colour[4] }}
<br>
</div>
<div class="colour_container">
{{ firstname[3]~' '~surname[3]~' '~colour[3] }}<br>
{{ firstname[6]~' '~surname[6]~' '~colour[6] }}
<br>
</div>