1

Question

I want to concatenate variables in Jinja by separating them with exactly one space (' ') where some of the variables might be undefined (not known to the template which ones).

import jinja2

template = jinja2.Template('{{ title }} {{ first }} {{ middle }} {{ last }}')
result = template.render(first='John', last='Doe')  # result == ' John  Doe'

I've been searching for a template string alternative that returns 'John Doe' instead of ' John Doe' in this case.

First attempt

Use

{{ [title, first, middle, last]|reject("undefined")|join(" ") }}

as template (which actually works), however

  • it's confusing
  • once I want to use a macro on one of the arguments (that e.g. puts middle in brackets if it is not undefined) and therefore return an empty string instead of Undefined this stops working.

2nd attempt

Replace multiple spaces with one space

{% filter replace("  ", " ") -%}
{{ title }} {{ first }} {{ middle }} {{ last }}
{%- endfilter %}

which actually only would work in all cases with a regular expression search/replace like

{% filter regex_replace(" +", " ") -%}
{{ title }} {{ first }} {{ middle }} {{ last }}
{%- endfilter %}

but regex_replace is not built-in (but could be provided by a custom regular expression filter).

Still I would not consider this to be optimal as double spaces in the content of variables would be replaced as well.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
phispi
  • 604
  • 7
  • 15

2 Answers2

1

you dont use the power of jinja2/python, use the if else and test if a variable is defined:

import jinja2

template = "{{ title ~ ' ' if title is defined else '' }}"
template += "{{first ~ ' ' if first is defined else '' }}"
template += "{{ middle ~ ' ' if middle is defined else '' }}"
template += "{{ last if last is defined else '' }}"

template = jinja2.Template(template)
result = template.render(first='John', last='Doe')
print("----------")
print(result)
print("----------")

result:

----------
John Doe
----------

if you want to use regex, use :

import re
  :
  :
template = jinja2.Template("{{ title }} {{ first }} {{ middle }} {{ last }}")
result = template.render(first='John', last='Doe')
result = re.sub(" +", " ", result.strip())
  • strip() deletes any leading and trailing whitespaces including tabs (\t)

or a mixed of both solutions to avoid to suppress spaces not wanted

import jinja2

template = "{{ title ~ ' ' if title is defined else '' }}"
template += "{{first ~ ' ' if first is defined else '' }}"
template += "{{ middle ~ ' ' if middle is defined else '' }}"
template += "{{ last if last is defined else '' }}"

template = jinja2.Template(template)
result = template.render(first='John', last='Doe').strip()
Frenchy
  • 16,386
  • 3
  • 16
  • 39
  • Thank you very much for your proposal! Your suggestion with if/else works but might leave you with an extra space at the end (no problem in my use case, and would be possible to get rid of by surrounding your code with `{% filter trim %}...{% endfilter %}`. The second suggestion is a good idea, it would not require me to install a custom filter but would potentially strip multiple spaces at different locations which in my use case (wikitext) would destroy the formatting. – phispi Jun 12 '22 at 12:55
  • Thanks! If `first` is defined and `last` is not, you would get an end space, or am I mistaken? – phispi Jun 13 '22 at 06:53
  • oh yes in this case...you have right – Frenchy Jun 13 '22 at 06:57
  • and the trim filter is necessary in this case – Frenchy Jun 13 '22 at 07:05
  • 1
    or use the template of the first solution and do a simple result.strip() to delete all leading and trailing spaces – Frenchy Jun 13 '22 at 07:11
0

A variant of the accepted answer is to use a joiner in the Jinja template:

{%- set space = joiner(" ") %}
{%- if title %}{{ space() }}{{ title }}{% endif %}
{%- if first %}{{ space() }}{{ first }}{% endif %}
{%- if middle %}{{ space() }}{{ middle }}{% endif %}
{%- if last %}{{ space() }}{{ last }}{% endif %}

This works without any extensions, custom filters or post-processing but is relatively verbose.

phispi
  • 604
  • 7
  • 15