Given some JSON data as array of items:
[
{
"category": 1,
"product": "Sugar"
},
{
"category": 1,
"product": "Oil"
},
{
"category": 2,
"product": "Spice"
},
{
"category": 3,
"product": "Salt"
},
{
"category": 3,
"product": "Eggs"
},
{
"category": 5,
"product": "Bread"
},
{
"category": 5,
"product": "Butter"
},
{
"category": 7,
"product": "Milk"
}
]
How can I render it using Mustache.js, so that the items are grouped by category Id into separate lists, also adding an empty placeholder for missing Id's (4 and 6 in this case), like:
<ul id="category-1">
<li>Sugar</li>
<li>Oil</li>
</ul>
<ul id="category-2">
<li>Spice</li>
</ul>
<ul id="category-3">
<li>Salt</li>
<li>Eggs</li>
</ul>
<ul id="category-4">
</ul>
<ul id="category-5">
<li>Bread</li>
<li>Butter</li>
</ul>
<ul id="category-6">
</ul>
<ul id="category-7">
<li>Milk</li>
</ul>
Figured out a solution that does involve data transformation to suit Mustache template format, but performs fewer iterations than cjl750's answer below (which does filter
and map
for each item):
// fill data array with empty/no products for all categories (max 7 in this case)
for (let i = 1; i < 8; i++)
data.push({ category: i });
// group by categories and add products
const grouped = Object.values(data.reduce((r, i) => {
r[i.category] = r[i.category] || { category: i.category, products: [] };
if (i.product)
r[i.category].products.push({ product: i.product });
return r;
}, {}));
It will transform the data array to a grouped object array, like:
[
{
"category": 1,
"products": [
{ "product": "Sugar" },
{ "product": "Oil" }
]
},
:
:
]
Now the template can simply be:
<template id="template">
{{#.}}
<ul class="category-{{category}}">
{{#products}}
<li>{{product}}</li>
{{/products}}
</ul>
{{/.}}
</template>