Подтвердить что ты не робот

Как получить разницу между двумя массивами объектов в JavaScript

У меня есть два набора результатов, как это:

// Result 1
[
    { value="0", display="Jamsheer" },
    { value="1", display="Muhammed" },
    { value="2", display="Ravi" },
    { value="3", display="Ajmal" },
    { value="4", display="Ryan" }
]

// Result 2
[
    { value="0", display="Jamsheer" },
    { value="1", display="Muhammed" },
    { value="2", display="Ravi" },
    { value="3", display="Ajmal" },
]

Конечный результат, который мне нужен, - это разница между этими массивами - конечный результат должен быть таким:

[{ value="4", display="Ryan" }]

Возможно ли сделать что-то подобное в JavaScript?

4b9b3361

Ответ 1

Используя только родной JS, что-то вроде этого будет работать:

a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"},  { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}]
b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}]

function comparer(otherArray){
  return function(current){
    return otherArray.filter(function(other){
      return other.value == current.value && other.display == current.display
    }).length == 0;
  }
}

var onlyInA = a.filter(comparer(b));
var onlyInB = b.filter(comparer(a));

result = onlyInA.concat(onlyInB);

console.log(result);

Ответ 2

Вы можете использовать Array.prototype.filter() в сочетании с Array.prototype.some().

Вот пример (если ваши массивы хранятся в переменных result1 и result2):

//Find values that are in result1 but not in result2
var uniqueResultOne = result1.filter(function(obj) {
    return !result2.some(function(obj2) {
        return obj.value == obj2.value;
    });
});

//Find values that are in result2 but not in result1
var uniqueResultTwo = result2.filter(function(obj) {
    return !result1.some(function(obj2) {
        return obj.value == obj2.value;
    });
});

//Combine the two arrays of unique entries
var result = uniqueResultOne.concat(uniqueResultTwo);

Ответ 3

Я использую немного более универсальный подход, хотя по идеям похож на подходы @Cerbrus и @Kasper Moerch. Я создаю функцию, которая принимает предикат, чтобы определить, равны ли два объекта (здесь мы игнорируем свойство $$hashKey, но это может быть что угодно), и возвращаю функцию, которая вычисляет симметричную разницу двух списков на основе этого предиката:

a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"},  { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}]
b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}]

var makeSymmDiffFunc = (function() {
    var contains = function(pred, a, list) {
        var idx = -1, len = list.length;
        while (++idx < len) {if (pred(a, list[idx])) {return true;}}
        return false;
    };
    var complement = function(pred, a, b) {
        return a.filter(function(elem) {return !contains(pred, elem, b);});
    };
    return function(pred) {
        return function(a, b) {
            return complement(pred, a, b).concat(complement(pred, b, a));
        };
    };
}());

var myDiff = makeSymmDiffFunc(function(x, y) {
    return x.value === y.value && x.display === y.display;
});

var result = myDiff(a, b); //=>  {value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan"}

У него есть одно незначительное преимущество перед подходом Cerebrus (как и у подхода Каспера Мёрча) в том, что он ускользает рано; если он находит совпадение, он не проверяет остальную часть списка. Если бы у меня была удобная функция curry, я бы сделал это немного по-другому, но это прекрасно работает.

объяснение

Комментарий попросил более подробное объяснение для начинающих. Здесь попытка.

Мы передаем следующую функцию makeSymmDiffFunc:

function(x, y) {
    return x.value === y.value && x.display === y.display;
}

Эта функция - то, как мы решаем, что два объекта равны. Как и все функции, которые возвращают true или false, ее можно назвать "функцией предиката", но это только терминология. Суть в том, что makeSymmDiffFunc настроен с помощью функции, которая принимает два объекта и возвращает true если мы считаем их равными, и false если мы этого не делаем.

Используя это, makeSymmDiffFunc (читаем "сделать функцию симметричной разности") возвращает нам новую функцию:

        return function(a, b) {
            return complement(pred, a, b).concat(complement(pred, b, a));
        };

Это функция, которую мы на самом деле будем использовать. Мы передаем ему два списка, и он находит элементы в первом, а не во втором, затем элементы во втором, не в первом, и объединяет эти два списка.

Оглядываясь на это снова, я определенно мог бы взять реплику из вашего кода и немного упростить основную функцию, используя some:

var makeSymmDiffFunc = (function() {
    var complement = function(pred, a, b) {
        return a.filter(function(x) {
            return !b.some(function(y) {return pred(x, y);});
        });
    };
    return function(pred) {
        return function(a, b) {
            return complement(pred, a, b).concat(complement(pred, b, a));
        };
    };
}());

complement использует предикат и возвращает элементы его первого списка, а не второго. Это проще, чем мой первый проход с отдельным contains функцию.

Наконец, основная функция обернута в выражение немедленно вызванной функции ( IIFE), чтобы сохранить внутреннюю функцию complement вне глобальной области видимости.


Обновление через несколько лет

Теперь, когда ES2015 стал довольно распространенным, я бы предложил ту же технику с гораздо меньшим количеством шаблонов:

const diffBy = (pred) => (a, b) => a.filter(x => !b.some(y => pred(x, y)))
const makeSymmDiffFunc = (pred) => (a, b) => diffBy(pred)(a, b).concat(diffBy(pred)(b, a))

const myDiff = makeSymmDiffFunc((x, y) => x.value === y.value && x.display === y.display)

const result = myDiff(a, b)
//=>  {value="a63a6f77-c637-454e-abf2-dfb9b543af6c", display="Ryan"}

Ответ 4

Я думаю, что решение @Cerbrus занято. Я реализовал одно и то же решение, но извлек повторный код в свою собственную функцию (DRY).

 function filterByDifference(array1, array2, compareField) {
  var onlyInA = differenceInFirstArray(array1, array2, compareField);
  var onlyInb = differenceInFirstArray(array2, array1, compareField);
  return onlyInA.concat(onlyInb);
}

function differenceInFirstArray(array1, array2, compareField) {
  return array1.filter(function (current) {
    return array2.filter(function (current_b) {
        return current_b[compareField] === current[compareField];
      }).length == 0;
  });
}

Ответ 5

import differenceBy from 'lodash/differenceBy'

const myDifferences = differenceBy(Result1, Result2, 'value')

Это вернет разницу между двумя массивами объектов, используя ключ value для их сравнения. Обратите внимание, что две вещи с одинаковым значением не будут возвращены, так как остальные клавиши игнорируются.

Это часть lodash.

Ответ 6

Вы можете создать объект с ключами в качестве уникального значения, соответствующего каждому объекту в массиве, а затем отфильтровать каждый массив на основе наличия ключа в другом объекте. Это снижает сложность операции.

ES6

let a = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal"},  { value:"a63a6f77-c637-454e-abf2-dfb9b543af6c", display:"Ryan"}];
let b = [{ value:"4a55eff3-1e0d-4a81-9105-3ddd7521d642", display:"Jamsheer", $$hashKey:"008"}, { value:"644838b3-604d-4899-8b78-09e4799f586f", display:"Muhammed", $$hashKey:"009"}, { value:"b6ee537a-375c-45bd-b9d4-4dd84a75041d", display:"Ravi", $$hashKey:"00A"}, { value:"e97339e1-939d-47ab-974c-1b68c9cfb536", display:"Ajmal", $$hashKey:"00B"}];

let valuesA = a.reduce((a,{value}) => Object.assign(a, {[value]:value}), {});
let valuesB = b.reduce((a,{value}) => Object.assign(a, {[value]:value}), {});
let result = [...a.filter(({value}) => !valuesB[value]), ...b.filter(({value}) => !valuesA[value])];
console.log(result);

Ответ 7

Я нашел это решение, используя фильтр и некоторые.

resultFilter = (firstArray, secondArray) => {
  return firstArray.filter(firstArrayItem =>
    !secondArray.some(
      secondArrayItem => firstArrayItem._user === secondArrayItem._user
    )
  );
};

Ответ 8

Для тех, кто любит однострочные решения в ES6, что-то вроде этого:

const arrayOne = [ 
  { value: "4a55eff3-1e0d-4a81-9105-3ddd7521d642", display: "Jamsheer" },
  { value: "644838b3-604d-4899-8b78-09e4799f586f", display: "Muhammed" },
  { value: "b6ee537a-375c-45bd-b9d4-4dd84a75041d", display: "Ravi" },
  { value: "e97339e1-939d-47ab-974c-1b68c9cfb536", display: "Ajmal" },
  { value: "a63a6f77-c637-454e-abf2-dfb9b543af6c", display: "Ryan" },
];
          
const arrayTwo = [
  { value: "4a55eff3-1e0d-4a81-9105-3ddd7521d642", display: "Jamsheer"},
  { value: "644838b3-604d-4899-8b78-09e4799f586f", display: "Muhammed"},
  { value: "b6ee537a-375c-45bd-b9d4-4dd84a75041d", display: "Ravi"},
  { value: "e97339e1-939d-47ab-974c-1b68c9cfb536", display: "Ajmal"},
];

const results = arrayOne.filter(({ value: id1 }) => !arrayTwo.some(({ value: id2 }) => id2 === id1));

console.log(results);

Ответ 9

Я сделал обобщенный diff, который сравнивает 2 объекта любого типа и может запускать обработчик модификации gist.github.com/bortunac "diff.js" например:

old_obj={a:1,b:2,c:[1,2]}
now_obj={a:2 , c:[1,3,5],d:55}

поэтому свойство a изменяется, b удаляется, c изменен, d добавляется

var handler=function(type,pointer){
console.log(type,pointer,this.old.point(pointer)," | ",this.now.point(pointer)); 

}

теперь использовать как

df=new diff();
df.analize(now_obj,old_obj);
df.react(handler);

консоль покажет

mdf ["a"]  1 | 2 
mdf ["c", "1"]  2 | 3 
add ["c", "2"]  undefined | 5 
add ["d"]  undefined | 55 
del ["b"]  2 | undefined 

Ответ 10

Большинство ответов здесь довольно сложны, но разве логика не так проста?

  1. проверьте, какой массив длиннее, и укажите его в качестве первого параметра (если длина равна, порядок параметров не имеет значения)
  2. Итерировать по массиву1.
  3. Для текущего элемента итерации array1 проверьте, присутствует ли он в array2
  4. Если его нет, то чем
  5. Нажмите на массив разностей
const getArraysDifference = (longerArray, array2) => {
  const difference = [];

  longerArray.forEach(el1 => {      /*1*/
    el1IsPresentInArr2 = array2.some(el2 => el2.value === el1.value); /*2*/

    if (!el1IsPresentInArr2) { /*3*/
      difference.push(el1);    /*4*/
    }
  });

  return difference;
}

O (n ^ 2) сложность.

Ответ 11

               a.forEach(function( _obj ) {
                    b = b.filter(function(element) {
                        if (_obj.value == element.value && _obj.display ==element.display){
                            return element;
                        }
                    });
                });
                var difference = b;

Ответ 12

Если вы хотите использовать внешние библиотеки, вы можете использовать _.difference в underscore.js для достижения этого. _.difference возвращает значения из массива, отсутствующие в других массивах.

_.difference([1,2,3,4,5][1,4,10])

==>[2,3,5]