1

I've been trying to convert this object to an unordered list, but I need to consolidate it in categories.

var snippets = [
  {title: 'snippet 1', content: 'hello there 1', category: 'javascript'},
  {title: 'snippet 2 ', content: 'hello there 2', category: 'wordpress'},
  {title: 'snippet 3', content: 'hello there 3', category: 'javascript'},
  {title: 'snippet 4', content: 'hello there 4', category: 'general'},
  {title: 'snippet 5', content: 'hello there 5', category: 'general'}
];

The expected output is:

<ul>
  <li>javascript 
    <ul>
      <li>snippet 1</li>
      <li>snippet 2</li>
    </ul>
  </li>
    <li>wordpress 
    <ul>
      <li>snippet 1</li>
      <li>snippet 2</li>
    </ul>
  </li>
</ul>

I've been trying to do it with $.each, but for the file of me I can't get my head around it. I'm willing to use UnderscoreJS too.

$('body').append('<ul class="categories"></ul>');

$.each(snippets, function(i, v) {
    var data = '<li class="category">'+ v.category +' ';
      data += '<ul class="item"> <li> ' + v.title + ' </li> </ul>';
      data += '</li>';

    $(data).appendTo('.categories');
});

Output not expected:

<ul class="categories">
  <li class="category">javascript 
    <ul class="item"> 
      <li> snippet 1 </li> 
    </ul></li>
  <li class="category">wordpress 
    <ul class="item"> 
      <li> snippet 2  </li> 
    </ul>
  </li>
  <li class="category">javascript 
    <ul class="item"> 
      <li> snippet 3 </li> 
    </ul>
  </li>
  <li class="category">general 
    <ul class="item"> 
      <li> snippet 4 
      </li> 
    </ul>
  </li>
  <li class="category">general 
    <ul class="item"> 
      <li> snippet 5 
      </li> 
    </ul>
  </li>
</ul>

Can anyone give me a hint?

Ivor
  • 590
  • 6
  • 21
  • 1
    JSON is a *textual notation* for data exchange. [(More.)](http://stackoverflow.com/a/2904181/157247) If you're dealing with JavaScript source code, and not dealing with a *string*, you're not dealing with JSON. – T.J. Crowder Mar 11 '17 at 16:28
  • @T.J.Crowder The title has been edited, the fact is I'm actually dealing with JSON because I'm getting the data from WP REST API. The object literal `snippets` is outputted after I've parsed it. – Ivor Mar 11 '17 at 16:33
  • Sure -- but at that point, again, you're not dealing with JSON anymore. :-) – T.J. Crowder Mar 11 '17 at 16:40

3 Answers3

3

Description inline as comments:

var snippets = [
  {title: 'snippet 1', content: 'hello there 1', category: 'javascript'},
  {title: 'snippet 2 ', content: 'hello there 2', category: 'wordpress'},
  {title: 'snippet 3', content: 'hello there 3', category: 'javascript'},
  {title: 'snippet 4', content: 'hello there 4', category: 'general'},
  {title: 'snippet 5', content: 'hello there 5', category: 'general'}
];

// Create the UL that will contain all the categories
var ul = $("<ul>").addClass("categories");
// Use an object as a map of category to sublist
var lists = Object.create(null);
// Loop through the snippets
snippets.forEach(function(snippet) {
  // Get the sublist for this category
  var list = lists[snippet.category];
  if (!list) {
    // Don't have one yet, create it
    list = lists[snippet.category] = $("<ul>");
    // Create the item that will contain it in the main list
    var item = $("<li>").addClass("category").text(snippet.category);
    // Add the sublist to the item, and the item to the main list
    item.append(list);
    ul.append(item);
  }
  // Add this entry to the sublist
  list.append($("<li>").addClass("item").text(snippet.title));
});
// Done building, add the main list to the doc
ul.appendTo(document.body);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
2

Another way to do it:

var snippets = [
  {title: 'snippet 1', content: 'hello there 1', category: 'javascript'},
  {title: 'snippet 2 ', content: 'hello there 2', category: 'wordpress'},
  {title: 'snippet 3', content: 'hello there 3', category: 'javascript'},
  {title: 'snippet 4', content: 'hello there 4', category: 'general'},
  {title: 'snippet 5', content: 'hello there 5', category: 'general'}
];

var categories = _.uniq(_.pluck(snippets, 'category'));

var categoriesUl = '<ul>';
categories.forEach(function(category) {
  categoriesUl += '<li>' + category + '</category>';
  var listUnderCategory = [];
  snippets.forEach(function(item) {
    if (item.category == category) {
      listUnderCategory.push(item.title);
    }
  });
  if (listUnderCategory.length) {
    categoriesUl += '<ul>';
    categoriesUl += listUnderCategory.map(function(val) {
      return '<li>' + val + '</li>'
    }).join('');
    categoriesUl += '</ul>';
  }
})
categoriesUl += '</ul>';

$('#output').append(categoriesUl);
<div id='output'>
</div>
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Mosd
  • 1,640
  • 19
  • 22
  • 1
    Thanks! If you're using Underscore you can also do `var categories = _.uniq( _.pluck(snippets, 'category') );` instead of `.map` – Ivor Mar 11 '17 at 17:59
  • @Ivor, yeah...making things more concise – Mosd Mar 11 '17 at 18:31
1

You can create an extra object with the desired values as shown below. Now each category will be in separate array with category as key.

item = [];

$.each(snippets, function(i, v) {
      if (item[v.category] == undefined) { // If it is a new key then intialize it
        item[v.category] = [];
        item[v.category].push(v.title);
      } else {
        item[v.category].push(v.title); // If already existing then push value
      }
});

console.log(item); // Loop this object to get the values and generate HTML
var html = '<ul class="categories">';
for (var key in item) {
    var obj = item[key];
    html += '<li class="category">' + key +'<ul class="item">';
    for (var i = 0; i < obj.length; i++) {
        html += '<li>'+obj[i]+'</li>';
    } 
    html += '</ul></li>';
    console.log(obj);
}
html += "</ul>";
$('body').append('<ul class="categories"></ul>');
console.log(html);
$(html).appendTo('.categories');

js fiddle

Ayush
  • 741
  • 9
  • 19
  • This doesn't answer the question of how to put all this into the DOM as an unordered list of unordered lists. – T.J. Crowder Mar 11 '17 at 16:43
  • @T.J.Crowder Ohhh sorry for the mistake. I thought Ivor just needed an idea to get the data. Let me update. Thanks for pointing that out. :) – Ayush Mar 11 '17 at 16:46
  • Also note that you're using an array (`item`) like a non-array object, not like an array. Should just make it an object, ideally one without a prototype (see my answer for an example). – T.J. Crowder Mar 11 '17 at 16:52
  • @T.J.Crowder I have updated my code and thank you for you valuable advice. I saw your answer. I didn't know that we can use objects like this. I will make sure that I use it in my future code. ty. :) – Ayush Mar 11 '17 at 17:00