Home > Mobile >  Iteration over dictionary
Iteration over dictionary

Time:05-06

I have setup a Flask app trying to fetch data from the classic Drinks API.

Here is a shortened sample of the output given by this API

{
  "drinks": [
    {
      "idDrink": "13196",
      "strDrink": "Long vodka",
      "strCategory": "Ordinary Drink",
      "strGlass": "Highball glass",
      "strInstructions": "Shake a tall glass with ice cubes and Angostura, coating the inside of the glass. Por the vodka onto this, add 1 slice of lime and squeeze juice out of remainder, mix with tonic, stir and voila you have a Long Vodka",
      "strIngredient1": "Vodka",
      "strIngredient2": "Lime",
      "strIngredient3": "Angostura bitters",
      "strIngredient4": "Tonic water",
      "strIngredient5": "Ice",
      "strIngredient6": null,
      "strIngredient7": null,
      "strIngredient8": null,
      "strIngredient9": null,
      "strIngredient10": null,
      "strIngredient11": null,
      "strIngredient12": null,
      "strIngredient13": null,
      "strIngredient14": null,
      "strIngredient15": null
    },
    {
      "idDrink": "16967",
      "strDrink": "Vodka Fizz",
      "strCategory": "Other/Unknown",
      "strGlass": "White wine glass",
      "strInstructions": "Blend all ingredients, save nutmeg. Pour into large white wine glass and sprinkle nutmeg on top.",
      "strIngredient1": "Vodka",
      "strIngredient2": "Half-and-half",
      "strIngredient3": "Limeade",
      "strIngredient4": "Ice",
      "strIngredient5": "Nutmeg",
      "strIngredient6": null,
      "strIngredient7": null,
      "strIngredient8": null,
      "strIngredient9": null,
      "strIngredient10": null,
      "strIngredient11": null,
      "strIngredient12": null,
      "strIngredient13": null,
      "strIngredient14": null,
      "strIngredient15": null
    }
  ]
}

How do you iterate over a value that has the same name but ends with something else?
For example, they have: strIngredient1-15.

Not sure, but maybe regex is better or match/endswith and then use {{ variable }} to present it.

I understand it can be done in many ways but I can't figure out how to proceed, I just need some push in the right direction.

I was trying to loop over it but I am getting the error

NoneType is not iterable

On the line

{% if 'strIngredient' in inner_dict[ingr] == null %}

My full code is

<tbody>
    {% for outer_dict in data_output %}
        {% for inner_dict in data_output[outer_dict] %}
    <tr>
        <td>{{ inner_dict['idDrink'] }}</td>
        <td>{{ inner_dict['strDrink'] }}</td>
        <td>{{ inner_dict['strCategory'] }}</td>
        <td>{{ inner_dict['strGlass'] }}</td>
        <td>{{ inner_dict['strInstructions'] }}</td>
            {% for ingr in inner_dict %}
                {% if 'strIngredient' in inner_dict[ingr] == null %}
            <td>{{ inner_dict[ingr] }}</td>
    </tr>
                {% endif %}
            {% endfor %}
        {% endfor %}
    {% endfor %}
</tbody>

CodePudding user response:

Since you have to compare the attributes of the dictionary to strIngredient, the idea should be to use the for construction to loop over a dictionary:

As variables in templates retain their object properties, it is possible to iterate over containers like dict:

<dl>
{% for key, value in my_dict.items() %}
   <dt>{{ key|e }}</dt>
   <dd>{{ value|e }}</dd>
{% endfor %}
</dl>

With this, you have both the key and value of the dictionary, and it is way easier to compare your string to the key given by the for itself.

Mind that:

  • there is also a for ... if ... construct in Jinja that can simplify your actual use case
  • you can use the startswith method of a Python string in your particular use case: drink_attribute.startswith('strIngredient')
  • you also have to check if the ingredient is not None since you have some null values for ingredients in the input JSON
  • it is always useful to give meaningful name in your code (e.g. drinks — plural since it is a list of drink — instead of data_output, drink instead of outer_dict, ...).

All this together gives this loop for the ingredients:

{%- for drink_attribute, ingredient in drink.items() 
    if drink_attribute.startswith('strIngredient') and ingredient 
%}
  <td>{{ ingredient }}</td>
{%- endfor %}

And this is the loop to create a table with all the drinks:

<table>
  <tbodby>
    {% for drink in drinks %}
    <tr>
      <td>{{ drink.idDrink }}</td>
      <td>{{ drink.strDrink }}</td>
      <td>{{ drink.strCategory }}</td>
      <td>{{ drink.strGlass }}</td>
      <td>{{ drink.strInstructions }}</td>
      
      {%- for drink_attribute, ingredient in drink.items() 
            if drink_attribute.startswith('strIngredient') and ingredient 
      %}
        <td>{{ ingredient }}</td>
      {%- endfor %}
    </tr>
    {% endfor %}
  <tbody>
<table>

This will render in (some spacing and style added for readability):

table {
  border-collapse: collapse;
}
td {
  border: 1px solid;
}
<table>
  <tbodby>
    
    <tr>
      <td>13196</td>
      <td>Long vodka</td>
      <td>Ordinary Drink</td>
      <td>Highball glass</td>
      <td>
          Shake a tall glass with ice cubes and Angostura, 
          coating the inside of the glass. 
          Por the vodka onto this, 
          add 1 slice of lime and squeeze juice out of remainder,
          mix with tonic, 
          stir and voila you have a Long Vodka
      </td>
        <td>Angostura bitters</td>
        <td>Vodka</td>
        <td>Tonic water</td>
        <td>Ice</td>
        <td>Lime</td>
    </tr>
    
    <tr>
      <td>16967</td>
      <td>Vodka Fizz</td>
      <td>Other/Unknown</td>
      <td>White wine glass</td>
      <td>
          Blend all ingredients, save nutmeg. 
          Pour into large white wine glass 
          and sprinkle nutmeg on top.
      </td>
        <td>Limeade</td>
        <td>Vodka</td>
        <td>Ice</td>
        <td>Nutmeg</td>
        <td>Half-and-half</td>
    </tr>
    
  <tbody>
<table>

  • Related