0

My goal is having a string like "name: smoothie, ingredients: milk, orange, category: vegan" (there can be a lot option: value or option: value, value, value... pairs) to produce an array like the following

[
  {
     option: 'name',
     value: ['smoothie']
  },
  {
     option: 'ingredients',
     value: ['milk', 'orange']
  },
  {
     option: 'category',
     value: ['vegan']
  }
]

I thought something like the following would work but it produces the following error and i can't understand why. The problem is in the line where i try to add a value to the existing options[last_ok_index].value array

{% set options[last_ok_index].value = options[last_ok_index].value|merge( [ x[0] ] ) %}

Unexpected token "punctuation" of value "[" ("end of statement block" expected).

{% set product = "name: smoothie, ingredients: milk, orange" %}
{% set options = [] %}
{% set last_ok_index = 0 %}
{% for item in product|split(',') %}
    {% set x = item|split(': ') %}
    {% if x|length == 2 %}
        {% set options = options|merge( [ { option: x[0], value: [x[1]] } ] ) %}
        {% set last_ok_index = loop.index - 1 %}
    {% else %}
        {% set options[last_ok_index].value = options[last_ok_index].value|merge( [ x[0] ] ) %}
    {% endif%}
{% endfor %}

{# print result #}
{% for item in options %}
    {{item.option }}
    {% for inner_item in item.value %}
        {{"-" ~ inner_item}}
    {% endfor %}
{% endfor %}
Professor Chaos
  • 447
  • 5
  • 11
  • 2
    Why are you doing this in a template instead of a controller or straight PHP? – aynber Apr 07 '21 at 13:38
  • i'm not supposed to touch any of the controllers code...and i believe it is possible in twig somehow – Professor Chaos Apr 07 '21 at 13:39
  • What have you tried to debug the problem? Why not reduce the code step by step to spot the problem? – Nico Haase Apr 07 '21 at 13:49
  • the problem is in line {% set options[last_ok_index].value = options[last_ok_index].value|merge( [ x[0] ] ) %} so it is when i try to add a value in the existing options[last_ok_index].value array – Professor Chaos Apr 07 '21 at 13:51
  • 3
    Have you considered writing a TwigExtension that handles parsing the string and returning the array? That way you can use PHP without needing to modify any existing code, e.g. in controllers. You could take this as a template: https://stackoverflow.com/a/14504988/1166880 – dbrumann Apr 07 '21 at 13:56
  • The problem already starts at your first `split`, your expected output is `smoothie` and `ingredients`, while the actual result will be `smoothie`, `ingredients`, `orange` – DarkBee Apr 07 '21 at 14:53
  • @DarkBee run it and remove the problematic line to check...If i could add to the array the value i'm trying to add it would be ok – Professor Chaos Apr 07 '21 at 15:12
  • I know that as well, merging a multidimensional array in `twig` is not as easy as you've tried in your snippet, you'd need to merge every part of the array as seen [here](https://stackoverflow.com/questions/57461972/dynamic-multidimensional-array-in-twig). Anyway in your case it's not required as seen in my answer below – DarkBee Apr 07 '21 at 15:18
  • Why not write a TwigExtension that you could properly test and debug with common PHP tools? This would be way easier than relying on such hacks – Nico Haase Apr 07 '21 at 15:24
  • @NicoHaase no clue, some people just want to do everything inside `twig` – DarkBee Apr 07 '21 at 15:26

2 Answers2

1

You should go with the suggestion in the comments by @dbrumann and use a TwigExtension.
However if you want to solve this in pure twig, then you are overcomplicating things.

First things first, the problem already starts at your first split, your expected output is smoothie and ingredients, while the actual result will be smoothie, ingredients, orange. You can fix this by passiung asecond argument to the split filter, which will limit the output.

Split uses the PHP function explode in the background. More on what the second parameters does you can find in the documentation here

Now as I said you can simply your snippet by creating the "item" in two parts rather than one part

{% set product = "name: smoothie, ingredients: milk, orange" %}

{% set items = [] %}

{% for item in product|split(',', 2) %}
    {% set tmp = item|split(':') %}
        
    {% set option = tmp[0] %}
    {% set values = [] %}
        
    {% for value in tmp[1]|split(',') %}
       {% set values = values|merge([ value, ]) %}
    {% endfor %}    
    
    {% set items = items|merge([ {'option': option, 'values': values,}, ]) %}
{% endfor %}

demo


As you've changed the initial input of the original question. The problem still starts with the split filter. I'd suggest you use another delimeter for your values, e.g. ;

{% set product = 'name: smoothie 3, ingredients: milk ; orange; pineapple, category: bar' %}
{% set products = [] %}

{% for string in products_raw %}
    {% set product = [] %}
       
    {% for item in string|split(',') %}
        {% set tmp = item|split(':') %}
        
        {% set option = tmp[0] %}
        {% set values = [] %}
        
        {% for value in tmp[1]|split(';') %}
            {% set values = values|merge([ value, ]) %}
        {% endfor %}    
    
        {% set product = product|merge([ {'option': option, 'values': values,}, ]) %}
    {% endfor %}
    
    {% set products = products|merge([ product, ]) %}
{% endfor %}

{% for product in products %}
    {% for item in product %}
    - Option: {{ item.option }}
    - Values:
    {% for value in item.values %}
        - {{value }}
    {% endfor %}
{% endfor %}    
----------------------------------
{% endfor %}

demo

DarkBee
  • 16,592
  • 6
  • 46
  • 58
  • This does not work for the following string "name: smoothie, ingredients: milk, orange, cookie, category: vegan"...maybe i should specify that there could be numerous option-values pairs inside the string – Professor Chaos Apr 07 '21 at 15:17
  • That is because you've changed the input of your question. The problem still lays in the initial problem i've mentioned – DarkBee Apr 07 '21 at 15:19
  • the pattern of the input is option: value, option: value, value, value, option: value, value.... I thought it was obvious my bad.... – Professor Chaos Apr 07 '21 at 15:20
  • Well you need to change the pattern of your input or it will never work. See my edit – DarkBee Apr 07 '21 at 15:24
  • Found a way to make it work without changing the pattern although i do understand that i should just communicate with someone who has the privilege to change the pattern. Thank you a lot for your answers – Professor Chaos Apr 07 '21 at 16:00
0

Thank you a lot for all the tips, i changed the logic a bit and it works now. I will search about twig extensions as you proposed since it is for sure too much code in twig for something like that.

{% set product = "name: smoothie, ingredients: milk, orange, sugar, tags: healthy, popular, category: milk" %}
{% set options = [] %}
{% set last_option = null %}
{% set last_value = null %}
{% for item in product|split(',') %}
    {% set x = item|split(':') %}
    {% if x|length == 2 %}
        {% if last_value|length > 0 %}
            {% set options = options|merge( [ {option: last_option, value: last_value} ] ) %}
        {% endif %}
        {% set last_option = x[0] %}
        {% set last_value = [x[1]] %}
    {% else %}
        {% set last_value = last_value|merge([x[0]]) %}
    {% endif%}
    {% if loop.last %}
        {% if last_value|length > 0 %}
            {% set options = options|merge( [ {option: last_option, value: last_value} ] ) %}
        {% endif %}
    {% endif %}
{% endfor %}

{# print result #}
{% for item in options %}
    {{ item.option }}
    {% for inner_item in item.value %}
        {{ "-" ~ inner_item }}
    {% endfor %}
{% endfor %}
Professor Chaos
  • 447
  • 5
  • 11