Something like this should get you started - the code is commented with explanations (see it in action):
// convert array of objects into a Map grouped and ordered by supplied key function group(items, key) { // get unique values for grouping key const unique = [ ...new Set(items.map(item => item[key])) ]; // will be ascending by default unique.sort(); // sorting all of the results by title field const sortFn = (a, b) => a.title > b.title; const sortItems = (val) => { // filters the result set to items sharing the current group field value let sorted = items.filter(item => item[key] === val); // sort by title sorted.sort(sortFn); return sorted; } // reduce to a Map (which preserves insertion order and maintains the group key sorting) return unique.reduce((map, cur) => map.set(cur, sortItems(cur)), new Map()); } // testing it out data = [{ title: 'foo', category: 'book', year: 2016, }, { title: 'bar', category: 'book', year: 2016, }, { title: 'blah', category: 'paper', year: 2010, }, { title: 'idk', category: 'paper', year: 2015, }] group(data, 'category'); // Map {"book" => [Object, Object], "paper" => [Object, Object]} group(data, 'year'); // Map {2010 => [Object], 2015 => [Object], 2016 => [Object, Object]}
For displaying, you can use for...of with destructuring:
for (let [category, items] of group(data, 'category')) { console.log(` <div class="item"> <h3>${category}</h3> <ol>${items.map(item => `<li>${item.title}</li>`).join('')}</ol> </div> `); }
Note that even though I used ES6 throughout, this can easily be refactored to be more backwards compatible.