Merge duplicate objects in array of objects

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);

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);

Try with below approach, it works find and it is short

 var data = [
    {
        label: "Book1",
        data: "US edition"
    },
    {
        label: "Book1",
        data: "UK edition"
    },
    {
        label: "Book2",
        data: "CAN edition"
    }
];

const result = Array.from(new Set(data.map(s => s.label)))
    .map(lab => {
      return {
        label: lab,
        data: data.filter(s => s.label === lab).map(edition => edition.data)
      }
    })

console.log(result);

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>