GroupBy in JavaScript to group JSON data and populate on optgroup
Asked Answered
A

4

11

I am a little bit lost. I am getting this JSON:

[{
    "id": "210",
    "name": "Name 1",
    "category": "Category 1"
}, {
    "id": "187",
    "name": "Name 2",
    "category": "Category 1"
}, {
    "id": "186",
    "name": "Name 3",
    "category": "Category 1"
}, {
    "id": "185",
    "name": "Name 4",
    "category": "Category 1"
}, {
    "id": "184",
    "name": "Name 5",
    "category": "Category 1"
}, {
    "id": "183",
    "name": "Name 6",
    "category": "Category 1"
}, {
    "id": "182",
    "name": "Name 7",
    "category": "Category 1"
}, {
    "id": "181",
    "name": "Name 8",
    "category": "Category 2"
}, {
    "id": "180",
    "name": "Name 9",
    "category": "Category 3"
}, {
    "id": "178",
    "name": "Name 10",
    "category": "Category 2"
}]

And I would like to put all of this in a select with options and optgroups. Actually the optgroup should be category

I would like something like this:

<select name="products" class="product" id="product">
<optgroup label="Category 1">
    <option value="210">Name 1</option>
    <option value="187">Name 2</option>
    <option value="186">Name 3</option>
    <option value="185">Name 4</option>
    ...
</optgroup>
<optgroup label="Category 2">
    <option value="181">Name 8</option>
    <option value="178">Name 10</option>
</optgroup>
<optgroup label="Category 3">
    <option value="180">Name 9</option>
</optgroup>

Today I have only made this because I'm struggling too much:

$(document).ready(function () {
    $.getJSON("5.php", {
        val: $(this).val()
    }, function (data) {
        $.each(data, function (i, item) {
            $("<option/>").attr("value", item.id).append(item.name).appendTo("optgroup");
        });
    });
});

As you can see no optgroup :) Is there a way to do this? I can also modify my JSON if it can make it easier.

Thanks for any help.

Asset answered 20/12, 2011 at 16:40 Comment(0)
V
16

Assuming the optgroups already exist, change this...

.appendTo("optgroup")

to this...

.appendTo("optgroup[label='" + item.category + "']");

http://jsfiddle.net/FG9Lg/


If they don't exist, you need to create them, though I'd suggest a restructuring of your JSON response to have each item nested under the proper category.

Like this...

{
    "Category 1":[
        {"id": "210","name": "Name 1"},
        {"id": "187","name": "Name 2"},
        {"id": "186","name": "Name 3"},
        {"id": "185","name": "Name 4"},
        {"id": "184","name": "Name 5"},
        {"id": "183","name": "Name 6"},
        {"id": "182","name": "Name 7"}
    ],
    "Category 2":[
        {"id": "181","name": "Name 8"},
        {"id": "178","name": "Name 10"}
    ],
    "Category 3": [
        {"id": "180","name": "Name 9"}
    ]
}

So you could then do this:

var product = $('#product');

$.each(data, function (key, cat) {
    var group = $('<optgroup>',{label:key});

    $.each(cat,function(i,item) {
        $("<option/>",{value:item.id,text:item.name})
            .appendTo(group);
    });

    group.appendTo( product );
});

http://jsfiddle.net/FG9Lg/1/

Verdugo answered 20/12, 2011 at 16:41 Comment(7)
epic username is all I have to say.Barm
@Barm Thanks. It was either that or "LOOK AT ME! LOOK AT ME!".Illfated
Thanks a lot!! I have modified my JSON and done like you said! It works perfectly!Asset
Alternatively, could have used a map/reduce on the original set to get the list of groups, as a seeder... just the same, good suggestion.Valeriavalerian
@Tracker1: Looking back on my second solution, I see that it's flawed. I should have the categories structured as an Array of Objects as well since the order of enumeration won't be guaranteed. Feel free to add more solutions like the map/reduce if you feel like it.Illfated
Just great. Saved lots of time. Thanks a lotApis
+1 this approach worked for me, however it only worked after doing a refresh on the multiselect - $("#product").multiselect('refresh'); so this might save someone else half a day of head scratchingShae
T
16

If I were you, I would use a small library called Underscore to group the data which is returned in a easier representation.

See this code below and you may also see this live demo:

var groupData = _.groupBy(data, function (obj) {
    return obj.category;
});

var optGroups = [];
for (var key in groupData) {
    if (groupData.hasOwnProperty(key)) {
        var optGroup = $("<optgroup></optgroup>");
        optGroup.attr("label", key);
        var currentGroup = groupData[key];
        for (var i = 0; i < currentGroup.length; i++) {
            $("<option />").attr("value", currentGroup[i].id).html(currentGroup[i].name).appendTo(optGroup);
        }
        optGroups.push(optGroup);
    }
}

for(var i = 0; i < optGroups.length; i++) {
    $("#products").append(optGroups[i]);
}

If you hesitate to use Underscore library, you may consider this groupBy function:

var groupBy = function(array, predicate) {
    var grouped = {};
    for(var i = 0; i < array.length; i++) {
        var groupKey = predicate(array[i]);
        if (typeof(grouped[groupKey]) === "undefined")
            grouped[groupKey] = [];
        grouped[groupKey].push(array[i]);
    }

    return grouped;
}

USAGE:

var groupData = groupBy(data, function (obj) {
    return obj.category;
});
Theomancy answered 20/12, 2011 at 17:49 Comment(0)
S
2

I know this thread is very old, but I was needing something similar and I came up with this. It automatically adds optgroups when needed and populate them with options. Plus, it works both when you have optgroups and when you don't.

http://jsfiddle.net/mzj0nuet/

var select = $('#product');

$.each(data, function (key, cat) {
    var option = "<option value='"+cat.id+"'>"+cat.name+"</option>";

    // If we ave a category property, add this item to an optgroup
    if (cat.hasOwnProperty("category")) {
        var group = cat.category;

        // If this optgroup is not already present, add it
        if (select.find("optgroup[label='" + group + "']").length === 0) {
            select.append("<optgroup label='" + group + "' />");
        }

        select.find("optgroup[label='" + group + "']").append(option);

    // No category, no optgroup. Add this as simple option
    } else {
        select.append(option);
    }        
});
Samathasamau answered 16/4, 2015 at 9:34 Comment(0)
C
0

In case you would like to keep you JSON format as is, the following would answer your question:

//Loop through the json, get distinct category names, and append them as optgroup to the select dropdown
var categories = [];
$.each(data, function(index, item) {
    if ($.inArray(item.category, categories) == -1) {
        categories.push(item.category);
        var optgroupId = "cat-" + item.category.replace(/\s/g, "");
        $('#id_of_select_dropdown').append('<optgroup id ="'+optgroupId+'"label="'+item.category+'">');
    }
});
// append the options to their corresponding optgroups
$.each(data.response, function(index, item) {
    var optgroupId = "cat-" + item.category.replace(/\s/g, "");
    $('#'+optgroupId).append('<option>' + item.name + '</option>');
});

Hope this helps!

Capital answered 4/9, 2013 at 17:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.