If fruits
is the list [\'apples\', \'oranges\', \'pears\']
,
is there a quick way using django template tags to produce \"apples, oranges, and p
All of the answers here fail one or more of the following:
and
for the last item.Here's my entry into this canon. First, the tests:
class TestTextFilters(TestCase):
def test_oxford_zero_items(self):
self.assertEqual(oxford_comma([]), '')
def test_oxford_one_item(self):
self.assertEqual(oxford_comma(['a']), 'a')
def test_oxford_two_items(self):
self.assertEqual(oxford_comma(['a', 'b']), 'a and b')
def test_oxford_three_items(self):
self.assertEqual(oxford_comma(['a', 'b', 'c']), 'a, b, and c')
And now the code. Yes, it gets a bit messy, but you'll see that it doesn't use negative indexing:
from django.utils.encoding import force_text
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe
@register.filter(is_safe=True, needs_autoescape=True)
def oxford_comma(l, autoescape=True):
"""Join together items in a list, separating them with commas or ', and'"""
l = map(force_text, l)
if autoescape:
l = map(conditional_escape, l)
num_items = len(l)
if num_items == 0:
s = ''
elif num_items == 1:
s = l[0]
elif num_items == 2:
s = l[0] + ' and ' + l[1]
elif num_items > 2:
for i, item in enumerate(l):
if i == 0:
# First item
s = item
elif i == (num_items - 1):
# Last item.
s += ', and ' + item
else:
# Items in the middle
s += ', ' + item
return mark_safe(s)
You can use this in a django template with:
{% load my_filters %}
{{ items|oxford_comma }}
I would simply use ', '.join(['apples', 'oranges', 'pears'])
before sending it to the template as a context data.
UPDATE:
data = ['apples', 'oranges', 'pears']
print(', '.join(data[0:-1]) + ' and ' + data[-1])
You will get apples, oranges and pears
output.
First choice: use the existing join template tag.
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#join
Here's their example
{{ value|join:" // " }}
Second choice: do it in the view.
fruits_text = ", ".join( fruits )
Provide fruits_text
to the template for rendering.
If you want a '.' on the end of Michael Matthew Toomim's answer, then use:
{% if not forloop.last %}{% ifequal forloop.revcounter 2 %} and {% else %}, {% endifequal %}{% else %}{% endif %}{% if forloop.last %}.{% endif %}
I would suggest a custom django templating filter rather than a custom tag -- filter is handier and simpler (where appropriate, like here). {{ fruits | joinby:", " }}
looks like what I'd want to have for the purpose... with a custom joinby
filter:
def joinby(value, arg):
return arg.join(value)
which as you see is simplicity itself!
On the Django template this all you need to do for establishing a comma after each fruit. The comma will stop once its reached the last fruit.
{% if not forloop.last %}, {% endif %}