JavaScript에서 두 객체 배열의 차이를 얻는 방법


100

다음과 같은 두 가지 결과 집합이 있습니다.

// 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에서 이와 같은 일을 할 수 있습니까?


따라서 배열 모두 에서 발생하지 않는 모든 요소의 배열을 값 및 디스플레이로 필터링하고 싶습니까?
Cerbrus 2014

두 배열의 차이를 원합니다. 값은 배열 중 하나에 표시됩니다.
BKM 2014


2
방화범 콘솔에 로그인했을 때 표시되는 배열처럼 보입니다 ...
Mike C

2
죄송하지만, JSON 객체는 당신을 위해 = 변경해야 ... 잘못된 :
월터 Zalazar

답변:


136

네이티브 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);


1
이것은 작동하고 아마도 최선의 직접적인 대답 일 수 있지만, 술어와 두 개의 목록을 받아들이고 술어를 적절하게 적용하여 두 목록의 대칭 적 차이를 반환하는 것으로 바꾸는 것이 좋을 것입니다. (모든 m * n 비교를 두 번 실행하는 것보다 더 효율적인 경우 추가 점수!)
Scott Sauyet 2014

@ScottSauyet : 저는 "술어" 라는 용어에 익숙하지 않습니다 (영어 원어민 아님). 그게 return a.value ===...당신의 대답입니까? (그런데 멋진 솔루션, +1)를 사용하는 것 외에는 Array.prototype.some()더 효율적이고 짧은 방법을 찾을 수 없습니다.
Cerbrus 2014

2
@Cerbrus : 네. 술어는 부울 ( true또는 false값) 을 반환하는 함수입니다 .이 경우 사용자가 동등성 검사를 함수로 통과하도록 요구하여 나머지 코드에서 동등성 테스트 개념을 분리하면 다음과 같이 할 수 있습니다. 간단한 일반 알고리즘.
Scott Sauyet 2014

@ScottSauyet : 나를 다시 답을 찾기위한 시간이 조금 걸렸지 만, 지금은 꽤 좋습니다 : D
Cerbrus

ES6 const comparer = (otherArray) => (현재) => otherArray.filter ((기타) => other.value == current.value && other.display == current.display) .length == 0;
Shnigi

68

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

40

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


3
제발 설명 해주세요!
Bruno Brito

15

@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의 접근법 (Kasper Moerch의 접근법과 마찬가지로)보다 한 가지 사소한 이점이 있습니다. 일치하는 항목을 찾으면 나머지 목록을 확인하지 않습니다. 내가 있었다면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"}

1
코드에 더 많은 설명을 추가 할 수 있습니까? JavaScript의 초보자가 술어 접근 방식이 어떻게 작동하는지 이해할 수 있을지 모르겠습니다.
kaspermoerch 2014

1
@KasperMoerch : 긴 설명이 추가되었습니다. 도움이 되었기를 바랍니다. (또한이 코드가 가져야하는 심각한 정리를 인식하게했습니다.)
Scott Sauyet 2014

8
import differenceBy from 'lodash/differenceBy'

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

이렇게하면 키 value를 사용하여 비교하는 두 객체 배열 간의 차이가 반환 됩니다. 다른 키는 무시되므로 동일한 값을 가진 두 항목은 반환되지 않습니다.

이것은 lodash 의 일부입니다 .


위의 json 객체가 잘못되었습니다. :이 방법 변경 =하려고 할 때
월터 Zalazar

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

ES5

var 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"}];
var 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 valuesA = a.reduce(function(a,c){a[c.value] = c.value; return a; }, {});
var valuesB = b.reduce(function(a,c){a[c.value] = c.value; return a; }, {});
var result = a.filter(function(c){ return !valuesB[c.value]}).concat(b.filter(function(c){ return !valuesA[c.value]}));
console.log(result);


5

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

2

필터와 일부를 사용하여이 솔루션을 찾았습니다.

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


1

여기에있는 대부분의 답변은 다소 복잡하지만이 논리는 매우 간단하지 않습니까?

  1. 어떤 배열이 더 긴지 확인하고 첫 번째 매개 변수로 제공 (길이가 같으면 매개 변수 순서는 중요하지 않음)
  2. array1을 반복합니다.
  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) 복잡성.


복잡성 포함을 위해 1 개 추가
delavago1999

1

b에서 diff a를, a에서 diff b를 수행 한 다음 두 결과를 병합 할 수 있습니다.

let a = [
    { value: "0", display: "Jamsheer" },
    { value: "1", display: "Muhammed" },
    { value: "2", display: "Ravi" },
    { value: "3", display: "Ajmal" },
    { value: "4", display: "Ryan" }
]

let b = [
    { value: "0", display: "Jamsheer" },
    { value: "1", display: "Muhammed" },
    { value: "2", display: "Ravi" },
    { value: "3", display: "Ajmal" }
]

// b diff a
let resultA = b.filter(elm => !a.map(elm => JSON.stringify(elm)).includes(JSON.stringify(elm)));

// a diff b
let resultB = a.filter(elm => !b.map(elm => JSON.stringify(elm)).includes(JSON.stringify(elm)));  

// show merge 
console.log([...resultA, ...resultB]);


0

나는 어떤 종류의 2 개 객체를 비교하는 일반화 된 diff를 만들었고, 다음을 사용하여 수정 핸들러 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 

0

가장 일반적이고 간단한 방법 :

findObject(listOfObjects, objectToSearch) {
    let found = false, matchingKeys = 0;
    for(let object of listOfObjects) {
        found = false;
        matchingKeys = 0;
        for(let key of Object.keys(object)) {
            if(object[key]==objectToSearch[key]) matchingKeys++;
        }
        if(matchingKeys==Object.keys(object).length) {
            found = true;
            break;
        }
    }
    return found;
}

get_removed_list_of_objects(old_array, new_array) {
    // console.log('old:',old_array);
    // console.log('new:',new_array);
    let foundList = [];
    for(let object of old_array) {
        if(!this.findObject(new_array, object)) foundList.push(object);
    }
    return foundList;
}

get_added_list_of_objects(old_array, new_array) {
    let foundList = [];
    for(let object of new_array) {
        if(!this.findObject(old_array, object)) foundList.push(object);
    }
    return foundList;
}

0

나는 큰 배열에 관해서는 맵 객체를 선호합니다.

// create tow arrays
array1 = Array.from({length: 400},() => ({value:Math.floor(Math.random() * 4000)}))
array2 = Array.from({length: 400},() => ({value:Math.floor(Math.random() * 4000)}))

// calc diff with some function
console.time('diff with some');
results = array2.filter(({ value: id1 }) => array1.some(({ value: id2 }) => id2 === id1));
console.log('diff results ',results.length)
console.timeEnd('diff with some');

// calc diff with map object
console.time('diff with map');
array1Map = {};
for(const item1 of array1){
    array1Map[item1.value] = true;
}
results = array2.filter(({ value: id2 }) => array1Map[id2]);
console.log('map results ',results.length)
console.timeEnd('diff with map');


0

JavaScript에는 O (1) 삽입 및 조회 시간을 제공하는 Maps가 있습니다. 따라서 이것은 O (n)에서 풀 수 있습니다 (다른 모든 답변처럼 O (n²)이 아닙니다). 이를 위해 각 객체에 대해 고유 한 기본 (문자열 / 숫자) 키를 생성해야합니다. 하나는 할 수 JSON.stringify있지만 요소의 순서가 평등에 영향을 줄 수 있으므로 오류가 발생하기 쉽습니다.

 JSON.stringify({ a: 1, b: 2 }) !== JSON.stringify({ b: 2, a: 1 })

따라서 값에 표시되지 않는 구분 기호를 사용하여 문자열을 수동으로 작성합니다.

const toHash = value => value.value + "@" + value.display;

그런 다음지도가 생성됩니다. 요소가 이미 맵에 있으면 제거되고 그렇지 않으면 추가됩니다. 따라서 홀수 시간 (한 번만 의미)에 포함 된 요소 만 남습니다. 이는 요소가 각 배열에서 고유 한 경우에만 작동합니다.

const entries = new Map();

for(const el of [...firstArray, ...secondArray]) {
  const key = toHash(el);
  if(entries.has(key)) {
    entries.delete(key);
  } else {
    entries.set(key, el);
  }
}

const result = [...entries.values()];


0

다른 배열의 값과 일치하지 않는 한 배열의 첫 번째 항목을 선택하는 방법을 검색하는 동안이 질문을 발견하고 결국 array.find () 및 array.filter ()와 같이 정렬했습니다. 이

var carList= ['mercedes', 'lamborghini', 'bmw', 'honda', 'chrysler'];
var declinedOptions = ['mercedes', 'lamborghini'];

const nextOption = carList.find(car=>{
    const duplicate = declinedOptions.filter(declined=> {
      return declined === car
    })
    console.log('duplicate:',duplicate) //should list out each declined option
    if(duplicate.length === 0){//if theres no duplicate, thats the nextOption
      return car
    }
})

console.log('nextOption:', nextOption);
//expected outputs
//duplicate: mercedes
//duplicate: lamborghini
//duplicate: []
//nextOption: bmw

차선책을 확인하기 전에 업데이트 된 목록을 계속 가져와야한다면 충분히 잘 작동 할 것입니다. :)


-2

외부 라이브러리를 사용하려는 경우 underscore.js에서 _.difference를 사용하여이를 수행 할 수 있습니다. _.difference는 다른 배열에없는 배열의 값을 반환합니다.

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

==>[2,3,5]

11
이것은 원시 값이있는 배열에서만 작동합니다. 이 질문과 같이 배열에 객체 목록이 포함되어 있으면 객체 자체 대신 참조를 비교하려고 할 때 작동하지 않습니다. 이는 거의 항상 모든 것이 다르다는 것을 의미합니다.
Ross Peoples 2015

1
고마워요 로스 당신이 저를 두통에서 구해 줬어요. 밑줄을 긋기 위해 버그를보고하려고했습니다.
Etienne
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.