Объединение повторяющихся объектов в массив объектов - программирование

Объединение повторяющихся объектов в массив объектов

У меня есть массив объектов,

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

Я хочу объединить дублирующие объекты на основе атрибута 'label' так что конечный вывод будет выглядеть ниже,

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

Может кто-нибудь помочь мне определить подход?

4b9b3361

Ответ 1

Я бы, вероятно, перешел через filter, отслеживая карту объектов, которые я видел раньше, по этим строкам (отредактированные, чтобы отразить ваше согласие, что да, имеет смысл сделать (entry).data всегда массив)

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

В среде ES6 я использовал бы seen = new Map(), а не seen = {}.

Примечание: Array.isArray было определено ES5, поэтому некоторые довольно старые браузеры, такие как IE8, не будут иметь этого. Его можно легко закрепить/полилизовать, но:

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

Боковое примечание: я бы, вероятно, также всегда делал entry.data массив, даже если бы я не видел для него двух значений, поскольку согласованные структуры данных легче справляются с, Я не делал этого выше, потому что ваш конечный результат показал, что data является просто строкой, когда была только одна соответствующая запись. (Мы уже сделали это выше.)

Пример в реальном времени (версия ES5):

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>

Ответ 2

Этот код тестируется в последней версии firefox. Чтобы работать с другими браузерами, измените Array.isArray на библиотеку как lodash или что вы предпочитаете.

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