Merge duplicate objects in array of objects

后端 未结 3 1124
心在旅途
心在旅途 2020-11-29 06:03

I have below array of objects,

var data = [
    {
        label: \"Book1\",
        data: \"US edition\"
    },
    {
        label: \"Book1\",
        data:         


        
3条回答
  •  长情又很酷
    2020-11-29 06:37

    I would probably loop through with filter, keeping track of a map of objects I'd seen before, along these lines (edited to reflect your agreeing that yes, it makes sense to make (entry).data always an array):

    var seen = {};
    data = data.filter(function(entry) {
        var previous;
    
        // Have we seen this label before?
        if (seen.hasOwnProperty(entry.label)) {
            // Yes, grab it and add this data to it
            previous = seen[entry.label];
            previous.data.push(entry.data);
    
            // Don't keep this entry, we've merged it into the previous one
            return false;
        }
    
        // entry.data probably isn't an array; make it one for consistency
        if (!Array.isArray(entry.data)) {
            entry.data = [entry.data];
        }
    
        // Remember that we've seen it
        seen[entry.label] = entry;
    
        // Keep this one, we'll merge any others that match into it
        return true;
    });
    

    In an ES6 environment, I'd use seen = new Map() rather than seen = {}.

    Note: Array.isArray was defined by ES5, so some quite older browsers like IE8 won't have it. It can easily be shimmed/polyfilled, though:

    if (!Array.isArray) {
        Array.isArray = (function() {
            var toString = Object.prototype.toString;
            return function(a) {
                return toString.call(a) === "[object Array]";
            };
        })();
    }
    

    Side note: I'd probably also always make entry.data an array, even if I didn't see two values for it, because consistent data structures are easier to deal with. I didn't do that above because your end result showed data being just a string when there was only one matching entry. (We've done that above now.)

    Live example (ES5 version):

    var data = [
        {
            label: "Book1",
            data: "US edition"
        },
        {
            label: "Book1",
            data: "UK edition"
        },
        {
            label: "Book2",
            data: "CAN edition"
        }
    ];
    snippet.log("Before:");
    snippet.log(JSON.stringify(data, null, 2), "pre");
    var seen = {};
    data = data.filter(function(entry) {
        var previous;
    
        // Have we seen this label before?
        if (seen.hasOwnProperty(entry.label)) {
            // Yes, grab it and add this data to it
            previous = seen[entry.label];
            previous.data.push(entry.data);
    
            // Don't keep this entry, we've merged it into the previous one
            return false;
        }
    
        // entry.data probably isn't an array; make it one for consistency
        if (!Array.isArray(entry.data)) {
            entry.data = [entry.data];
        }
    
        // Remember that we've seen it
        seen[entry.label] = entry;
    
        // Keep this one, we'll merge any others that match into it
        return true;
    });
    snippet.log("After:");
    snippet.log(JSON.stringify(data, null, 2), "pre");
    
    

提交回复
热议问题