JavaScript - 合并两个对象数组并根据 属性 值删除重复项
JavaScript - merge two arrays of objects and de-duplicate based on property value
我想更新(替换)我数组中的对象为另一个数组中的对象。每个对象都具有相同的结构。例如
var origArr = [
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: true},
{name: 'Kasich', isRunning: true}
];
var updatingArr = [
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
];
// desired result:
NEWArr = [
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
];
我试过 concat() 和 Underscore 的 _.uniq
函数,但它总是转储较新的对象 & returns,本质上是原始数组。
有没有办法用 updatingArr
中的对象覆盖(替换)origArr
-- 在 name
属性 上匹配?
这将满足您的需求:
var origArr = [
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: true},
{name: 'Kasich', isRunning: true}
];
var updatingArr = [
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
];
for (var i = 0; i < updatingArr.length; ++i) {
var updateItem = updatingArr[i];
for (var j = 0; j < origArr.length; ++j) {
var origItem = origArr[j];
if (origItem.name == updateItem.name) {
origItem.isRunning = updateItem.isRunning;
break;
}
}
}
document.write('<pre>' + JSON.stringify(origArr, 0, 4) + '</pre>');
你可以使用 Array#map
in combination with Array#reduce
var origArr = [{ name: 'Trump', isRunning: true }, { name: 'Cruz', isRunning: true }, { name: 'Kasich', isRunning: true }],
updatingArr = [{ name: 'Cruz', isRunning: false }, { name: 'Kasich', isRunning: false }],
NEWArr = origArr.map(function (a) {
return this[a.name] || a;
}, updatingArr.reduce(function (r, a) {
r[a.name] = a;
return r;
}, Object.create(null)));
document.write('<pre>' + JSON.stringify(NEWArr, 0, 4) + '</pre>');
您可以使用按名称给出索引的散列,然后 Object.assign
进行更新。
var hash = origArr.reduce(function(hash, obj, index) {
hash[obj.name] = index;
return hash;
}, Object.create(null));
for(var obj of updatingArr) {
Object.assign(origArr[hash[obj.name]], obj);
}
你可以试试这个。
var origArr = [
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: true},
{name: 'Kasich', isRunning: true}
];
var updatingArr = [
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
];
var origLength = origArr.length;
var updatingLength = updatingArr.length;
//Traverse the original array and replace only if the second array also has the same value
for(i = origLength-1; i >= 0; i--) {
for(j = updatingLength -1; j >= 0; j--) {
if(origArr[i].name === updatingArr[j].name) {
origArr[i] = updatingArr[j];
}
}
}
console.log(origArr);
使用双循环和拼接,你可以这样做:
for(var i = 0, l = origArr.length; i < l; i++) {
for(var j = 0, ll = updatingArr.length; j < ll; j++) {
if(origArr[i].name === updatingArr[j].name) {
origArr.splice(i, 1, updatingArr[j]);
break;
}
}
}
例子here
下面是一个使用下划线的解决方案:
var result = _.map(origArr, function(orig){
return _.extend(orig, _.findWhere(updatingArr, {name: orig.name}));
});
我来这里正是为了寻找这个,看到了@Gruff Bunny 的技术,想知道 'lodash' 是否甚至比 'underscore' 更优越?
你瞧:
let result = _.unionBy(updatingArr, origArr, 'name');
此版本允许您将 selector
定义为重复的对象。
- forEach 遍历新数据
- findIndex returns 索引
>= 0
如果两个选择器相等。如果none相等,则returns-1
- 如果有重复,我们用slice把原来的替换成新的。
- 如果没有重复,我们将其推入原始数组。
const origArr = [
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: true},
{name: 'Kasich', isRunning: true}
];
const updatingArr = [
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
];
const mergeArrayOfObjects = (original, newdata, selector = 'key') => {
newdata.forEach(dat => {
const foundIndex = original.findIndex(ori => ori[selector] == dat[selector]);
if (foundIndex >= 0) original.splice(foundIndex, 1, dat);
else original.push(dat);
});
return original;
};
const result = mergeArrayOfObjects(origArr, updatingArr, "name")
console.log('RESULT -->', result)
const origArr = [
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: true},
{name: 'Kasich', isRunning: true}
];
const updatingArr = [
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
];
let hash = {};
for(let i of origArr.concat(updatingArr)) {
if(!hash[i]) {
hash[i.name] = i;
}
}
let newArr = [];
for(let i in hash) {
newArr.push(hash[i])
}
console.log(newArr);
用 ES-6 试试这个方法 Set 数据结构:
const result = [...new Set([...origArr, ...updatingArr])]
与@gevorg 的回答相同,但如果未找到匹配项,您可能还想向原始数组添加一个新对象。
let combinedEvents = origEvents;
for(let i =0; i< newEvents.length; i++){
let newEvent = newEvents[i];
for(let j =0; j< origEvents.length; j++){
let origEvent = origEvents[j];
if(newEvent.events_id == origEvent.events_id){
combinedEvents.splice(j,1, newEvent);
break;
} else if(j === origEvents.length - 1){
combinedEvents.push(newEvent);
break;
}
}
}
在 ES6 中你可以像这样使用对象 Map...
let map = new Map();
let origArr = [
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: true},
{name: 'Kasich', isRunning: true}
];
let updatingArr = [
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
];
// Concating arrays with duplicates
let NEWArr = origArr.concat(updatingArr);
// Removing duplicates items
NEWArr.forEach(item => {
if(!map.has(item.name)){
map.set(item.name, item);
}
});
Array.from(map.values());
记住:Map 对象需要唯一键,在本例中我使用了 name
.
Backbone 的 collection 非常适合这种场景。
首先,我们定义一个知道寻找 name
属性:
的模型类型
import { Model } from 'backbone';
const President = Model.extend({ idAttribute: 'name' });
然后,我们将初始数组放入使用上述模型的 collection 中:
import { Collection } from 'backbone';
const presidents = new Collection(origArr, { model: President });
现在,我们可以 运行 presidents.set
任意多次,它会将新表示与旧值合并:
presidents.set(updateArr);
presidents.get('Cruz').get('isRunning'); // false
presidents.toJSON(); // NEWArr
将所有内容放在一个 运行 可用的片段中:
const { Model, Collection } = Backbone;
const President = Model.extend({ idAttribute: 'name' });
const presidents = new Collection([
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: true},
{name: 'Kasich', isRunning: true}
], { model: President });
presidents.set([
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
]);
console.log(presidents.get('Cruz').get('isRunning'));
console.log(presidents.toJSON());
<script src="https://underscorejs.org/underscore-umd-min.js"></script>
<script src="https://backbonejs.org/backbone-min.js"></script>
虽然 Underscore 没有直接等效于 Lodash 的 unionBy
,但您仍然可以使用 concat
、groupBy
、map
在高效的单行代码中获得相同的结果和 last
:
import { groupBy, map, last } from 'underscore';
var NEWArr = map(groupBy(origArr.concat(updateArr), 'name'), last);
使用 chain
:
的等效、稍长但更简洁的符号
import { chain } from 'underscore';
var NEWArr = chain(origArr).concat(updateArr).groupBy('name').map(last).value();
事实上,您可以使用上面的配方定义您自己的 unionBy
并将其添加到 Underscore 中:
import _, { groupBy, map, first } from 'underscore';
function unionBy(...args) {
const iteratee = _.iteratee(args.pop());
const candidates = [].concat.apply(null, args);
return map(groupBy(candidates, iteratee), first);
}
_.mixin({ unionBy }); // unionBy can now be used in chaining
请注意,我们现在使用的是 first
而不是 last
,因为 Lodash 的 unionBy
的语义规定第一次出现为准。
我想更新(替换)我数组中的对象为另一个数组中的对象。每个对象都具有相同的结构。例如
var origArr = [
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: true},
{name: 'Kasich', isRunning: true}
];
var updatingArr = [
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
];
// desired result:
NEWArr = [
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
];
我试过 concat() 和 Underscore 的 _.uniq
函数,但它总是转储较新的对象 & returns,本质上是原始数组。
有没有办法用 updatingArr
中的对象覆盖(替换)origArr
-- 在 name
属性 上匹配?
这将满足您的需求:
var origArr = [
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: true},
{name: 'Kasich', isRunning: true}
];
var updatingArr = [
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
];
for (var i = 0; i < updatingArr.length; ++i) {
var updateItem = updatingArr[i];
for (var j = 0; j < origArr.length; ++j) {
var origItem = origArr[j];
if (origItem.name == updateItem.name) {
origItem.isRunning = updateItem.isRunning;
break;
}
}
}
document.write('<pre>' + JSON.stringify(origArr, 0, 4) + '</pre>');
你可以使用 Array#map
in combination with Array#reduce
var origArr = [{ name: 'Trump', isRunning: true }, { name: 'Cruz', isRunning: true }, { name: 'Kasich', isRunning: true }],
updatingArr = [{ name: 'Cruz', isRunning: false }, { name: 'Kasich', isRunning: false }],
NEWArr = origArr.map(function (a) {
return this[a.name] || a;
}, updatingArr.reduce(function (r, a) {
r[a.name] = a;
return r;
}, Object.create(null)));
document.write('<pre>' + JSON.stringify(NEWArr, 0, 4) + '</pre>');
您可以使用按名称给出索引的散列,然后 Object.assign
进行更新。
var hash = origArr.reduce(function(hash, obj, index) {
hash[obj.name] = index;
return hash;
}, Object.create(null));
for(var obj of updatingArr) {
Object.assign(origArr[hash[obj.name]], obj);
}
你可以试试这个。
var origArr = [
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: true},
{name: 'Kasich', isRunning: true}
];
var updatingArr = [
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
];
var origLength = origArr.length;
var updatingLength = updatingArr.length;
//Traverse the original array and replace only if the second array also has the same value
for(i = origLength-1; i >= 0; i--) {
for(j = updatingLength -1; j >= 0; j--) {
if(origArr[i].name === updatingArr[j].name) {
origArr[i] = updatingArr[j];
}
}
}
console.log(origArr);
使用双循环和拼接,你可以这样做:
for(var i = 0, l = origArr.length; i < l; i++) {
for(var j = 0, ll = updatingArr.length; j < ll; j++) {
if(origArr[i].name === updatingArr[j].name) {
origArr.splice(i, 1, updatingArr[j]);
break;
}
}
}
例子here
下面是一个使用下划线的解决方案:
var result = _.map(origArr, function(orig){
return _.extend(orig, _.findWhere(updatingArr, {name: orig.name}));
});
我来这里正是为了寻找这个,看到了@Gruff Bunny 的技术,想知道 'lodash' 是否甚至比 'underscore' 更优越?
你瞧:
let result = _.unionBy(updatingArr, origArr, 'name');
此版本允许您将 selector
定义为重复的对象。
- forEach 遍历新数据
- findIndex returns 索引
>= 0
如果两个选择器相等。如果none相等,则returns-1
- 如果有重复,我们用slice把原来的替换成新的。
- 如果没有重复,我们将其推入原始数组。
const origArr = [
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: true},
{name: 'Kasich', isRunning: true}
];
const updatingArr = [
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
];
const mergeArrayOfObjects = (original, newdata, selector = 'key') => {
newdata.forEach(dat => {
const foundIndex = original.findIndex(ori => ori[selector] == dat[selector]);
if (foundIndex >= 0) original.splice(foundIndex, 1, dat);
else original.push(dat);
});
return original;
};
const result = mergeArrayOfObjects(origArr, updatingArr, "name")
console.log('RESULT -->', result)
const origArr = [
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: true},
{name: 'Kasich', isRunning: true}
];
const updatingArr = [
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
];
let hash = {};
for(let i of origArr.concat(updatingArr)) {
if(!hash[i]) {
hash[i.name] = i;
}
}
let newArr = [];
for(let i in hash) {
newArr.push(hash[i])
}
console.log(newArr);
用 ES-6 试试这个方法 Set 数据结构:
const result = [...new Set([...origArr, ...updatingArr])]
与@gevorg 的回答相同,但如果未找到匹配项,您可能还想向原始数组添加一个新对象。
let combinedEvents = origEvents;
for(let i =0; i< newEvents.length; i++){
let newEvent = newEvents[i];
for(let j =0; j< origEvents.length; j++){
let origEvent = origEvents[j];
if(newEvent.events_id == origEvent.events_id){
combinedEvents.splice(j,1, newEvent);
break;
} else if(j === origEvents.length - 1){
combinedEvents.push(newEvent);
break;
}
}
}
在 ES6 中你可以像这样使用对象 Map...
let map = new Map();
let origArr = [
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: true},
{name: 'Kasich', isRunning: true}
];
let updatingArr = [
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
];
// Concating arrays with duplicates
let NEWArr = origArr.concat(updatingArr);
// Removing duplicates items
NEWArr.forEach(item => {
if(!map.has(item.name)){
map.set(item.name, item);
}
});
Array.from(map.values());
记住:Map 对象需要唯一键,在本例中我使用了 name
.
Backbone 的 collection 非常适合这种场景。
首先,我们定义一个知道寻找 name
属性:
import { Model } from 'backbone';
const President = Model.extend({ idAttribute: 'name' });
然后,我们将初始数组放入使用上述模型的 collection 中:
import { Collection } from 'backbone';
const presidents = new Collection(origArr, { model: President });
现在,我们可以 运行 presidents.set
任意多次,它会将新表示与旧值合并:
presidents.set(updateArr);
presidents.get('Cruz').get('isRunning'); // false
presidents.toJSON(); // NEWArr
将所有内容放在一个 运行 可用的片段中:
const { Model, Collection } = Backbone;
const President = Model.extend({ idAttribute: 'name' });
const presidents = new Collection([
{name: 'Trump', isRunning: true},
{name: 'Cruz', isRunning: true},
{name: 'Kasich', isRunning: true}
], { model: President });
presidents.set([
{name: 'Cruz', isRunning: false},
{name: 'Kasich', isRunning: false}
]);
console.log(presidents.get('Cruz').get('isRunning'));
console.log(presidents.toJSON());
<script src="https://underscorejs.org/underscore-umd-min.js"></script>
<script src="https://backbonejs.org/backbone-min.js"></script>
虽然 Underscore 没有直接等效于 Lodash 的 unionBy
,但您仍然可以使用 concat
、groupBy
、map
在高效的单行代码中获得相同的结果和 last
:
import { groupBy, map, last } from 'underscore';
var NEWArr = map(groupBy(origArr.concat(updateArr), 'name'), last);
使用 chain
:
import { chain } from 'underscore';
var NEWArr = chain(origArr).concat(updateArr).groupBy('name').map(last).value();
事实上,您可以使用上面的配方定义您自己的 unionBy
并将其添加到 Underscore 中:
import _, { groupBy, map, first } from 'underscore';
function unionBy(...args) {
const iteratee = _.iteratee(args.pop());
const candidates = [].concat.apply(null, args);
return map(groupBy(candidates, iteratee), first);
}
_.mixin({ unionBy }); // unionBy can now be used in chaining
请注意,我们现在使用的是 first
而不是 last
,因为 Lodash 的 unionBy
的语义规定第一次出现为准。