Merge duplicate objects in array of objects

后端 未结 3 1099
心在旅途
心在旅途 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:24

    This code is tested on latest version of firefox. To work on other browsers change Array.isArray for a library as lodash or whatever you prefer.

    var data = [
        {
            label: "Book1",
            data: "US edition"
        },
        {
            label: "Book1",
            data: "UK edition"
        },
        {
            label: "Book2",
            data: "CAN edition"
        }
    ],
    i = 0,
    j = data.length - 1,
    current;
    
    for (;i < data.length; i++) {
      current = data[i];
      for (;j > i; j--) {
         if (current.label === data[j].label) {
           if (Array.isArray(current.data)) {
             current.data = current.data.concat([data[j].data]);
           } else {
             current.data = [].concat([data[j].data, current.data]);
           }
           data.splice(j, 1);
         }
    
      }
    } 
    
    console.log(data);
    
    0 讨论(0)
  • 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");
    <!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
    <script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

    0 讨论(0)
  • 2020-11-29 06:44

    I came across the same situation and I was hoping to use the Set instead of mine here

    const data = [
      {
        label: "Book1",
        data: "US edition"
      },
      {
        label: "Book1",
        data: "UK edition"
      },
      {
        label: "Book2",
        data: "CAN edition"
      },
      {
        label: "Book3",
        data: "CAN edition"
      },
      {
        label: "Book3",
        data: "CANII edition"
      }
    ];
    
    const filteredArr = data.reduce((acc, current) => {
      const x = acc.find(item => item.label === current.label);
      if (!x) {
        const newCurr = {
          label: current.label,
          data: [current.data]
        }
        return acc.concat([newCurr]);
      } else {
        const currData = x.data.filter(d => d === current.data);
        if (!currData.length) {
          const newData = x.data.push(current.data);
          const newCurr = {
            label: current.label,
            data: newData
          }
          return acc;
        } else {
          return acc;
        }
        
      }
    }, []);
    
    console.log(filteredArr);

    0 讨论(0)
提交回复
热议问题