如何在JavaScript中获取两个对象数组之间的差异

时间:2014-02-24 12:34:02

标签: javascript arrays object

我有两个这样的结果集:

// 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中执行此类操作?

19 个答案:

答案 0 :(得分:103)

只使用本机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 :(得分:50)

您可以将Array.prototype.filter()Array.prototype.some()结合使用。

以下是一个示例(假设您的数组存储在变量result1result2中):

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

答案 2 :(得分:14)

我采用了更为通用的方法,尽管与 @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;
}

这个函数是我们决定两个对象是否相等的方法。与返回truefalse的所有函数一样,它可以称为“谓词函数”,但这只是术语。重点是makeSymmDiffFunc配置了一个接受两个对象的函数,如果我们认为它们相等则返回true,如果我们不相等则返回false

使用它,makeSymmDiffFunc(读取“make symmetric difference function”)返回一个新函数:

        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函数简单。

最后,main函数包含在一个立即调用的函数表达式( 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"}

答案 3 :(得分: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;
  });
}

答案 4 :(得分:4)

import differenceBy from 'lodash/differenceBy'

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

这将返回两个对象数组之间的差异,使用键value来比较它们。请注意,不会返回具有相同值的两个内容,因为其他键将被忽略。

这是lodash的一部分。

答案 5 :(得分:4)

您可以使用键创建一个对象,该键作为数组中每个对象的对应唯一值,然后根据其他对象中键的存在来过滤每个数组。降低了操作的复杂性。

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

答案 6 :(得分:4)

您可以对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]);

答案 7 :(得分:2)

对于那些喜欢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);

答案 8 :(得分:1)

我使用过滤器和一些过滤器找到了这个解决方案。

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

答案 9 :(得分:1)

对于大型数组,我更喜欢使用map对象。

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

答案 10 :(得分:1)

JavaScript具有Maps,可提供O(1)插入和查找时间。因此,这可以在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()];

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

const secondArray = [
    { value: "0", display: "Jamsheer" },
    { value: "1", display: "Muhammed" },
    { value: "2", display: "Ravi" },
    { value: "3", display: "Ajmal" },
];

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

console.log(result);

答案 11 :(得分:0)

最简单的方法可能是将 filtersome 一起使用 请参考以下链接 DifferenceInTwoArrayOfObjectInSimpleWay

答案 12 :(得分:0)

let obj1 =[
                 { id: 1, submenu_name: 'login' },
                 { id: 2, submenu_name: 'Profile',}, 
                 { id: 3, submenu_name: 'password',  },  
                 { id: 4, submenu_name: 'reset',}
               ] ;
 let obj2 =[
                 { id: 2}, 
                 { id: 3 },
               ] ;
               
// Need Similar obj 
const result1 = obj1.filter(function(o1){
 return obj2.some(function(o2){
    return o1.id == o2.id;          // id is unnique both array object
  });
});
 console.log(result1);



// Need differnt obj 
 const result2 = obj1.filter(function(o1){
 return !obj2.some(function(o2){    //  for diffrent we use NOT (!) befor obj2 here
    return o1.id == o2.id;          // id is unnique both array object
  });
});
 console.log(result2);

答案 13 :(得分:0)

另外,说两个key value不同的对象数组

// Array Object 1
const arrayObjOne = [
    { userId: "1", display: "Jamsheer" },
    { userId: "2", display: "Muhammed" },
    { userId: "3", display: "Ravi" },
    { userId: "4", display: "Ajmal" },
    { userId: "5", display: "Ryan" }
]

// Array Object 2
const arrayObjTwo =[
    { empId: "1", display: "Jamsheer", designation:"Jr. Officer" },
    { empId: "2", display: "Muhammed", designation:"Jr. Officer" },
    { empId: "3", display: "Ravi", designation:"Sr. Officer" },
    { empId: "4", display: "Ajmal", designation:"Ast. Manager" },
]

您可以在filteres5中使用native js减去两个数组对象。

//Find data that are in arrayObjOne but not in arrayObjTwo
var uniqueResultArrayObjOne = arrayObjOne.filter(function(objOne) {
    return !arrayObjTwo.some(function(objTwo) {
        return objOne.userId == objTwo.empId;
    });
});

ES6中,您可以将箭头功能与ES6中的Object destructuring一起使用。

const ResultArrayObjOne = arrayObjOne.filter(({ userId: userId }) => !arrayObjTwo.some(({ empId: empId }) => empId === userId));

console.log(ResultArrayObjOne);

答案 14 :(得分: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

如果您需要在交叉检查下一个最佳选项之前继续获取更新的列表,则应该可以使用它:)

答案 15 :(得分: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;
}

答案 16 :(得分:0)

这里的大多数答案都相当复杂,但是背后的逻辑不是很简单吗?

  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)复杂度。

答案 17 :(得分:0)

我做了一个广义差异,可以比较任何类型的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 

答案 18 :(得分:-3)

如果您愿意使用外部库,可以在underscore.js中使用_.difference来实现此目的。 _.difference返回数组中不存在于其他数组中的值。

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

==>[2,3,5]
相关问题