替换数组中的对象
Replacing objects in array
我有这个 javascript 对象:
var arr1 = [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}]
var arr2 = [{id:'124',name:'ttt'},
{id:'45',name:'yyy'}]
我需要将 arr1 中的对象替换为 arr2 中具有相同 id 的项目。
所以这是我想要得到的结果:
var arr1 = [{id:'124',name:'ttt'},
{id:'589',name:'www'},
{id:'45',name:'yyy'},
{id:'567',name:'rrr'}]
如何使用 javascript 实现它?
您可以使用 Array#map
with Array#find
.
arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
var arr1 = [{
id: '124',
name: 'qqq'
}, {
id: '589',
name: 'www'
}, {
id: '45',
name: 'eee'
}, {
id: '567',
name: 'rrr'
}];
var arr2 = [{
id: '124',
name: 'ttt'
}, {
id: '45',
name: 'yyy'
}];
var res = arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
console.log(res);
此处,如果在 arr2
中找到 id
,arr2.find(o => o.id === obj.id)
将 return 元素,即来自 arr2
的对象。如果不是,则 arr1
中的相同元素即 obj
是 returned.
由于您使用的是 Lodash,因此您可以使用 _.map
和 _.find
来确保支持主要浏览器。
最后我会选择这样的东西:
function mergeById(arr) {
return {
with: function(arr2) {
return _.map(arr, item => {
return _.find(arr2, obj => obj.id === item.id) || item
})
}
}
}
var result = mergeById([{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}])
.with([{id:'124',name:'ttt'}, {id:'45',name:'yyy'}])
console.log(result);
<script src="https://raw.githubusercontent.com/lodash/lodash/4.13.1/dist/lodash.js"></script>
如果您不关心数组的顺序,那么您可能希望通过 id
使用 differenceBy() and then simply use concat() 获得 arr1
和 arr2
之间的差异追加所有更新的对象。
var result = _(arr1).differenceBy(arr2, 'id').concat(arr2).value();
var arr1 = [{
id: '124',
name: 'qqq'
}, {
id: '589',
name: 'www'
}, {
id: '45',
name: 'eee'
}, {
id: '567',
name: 'rrr'
}]
var arr2 = [{
id: '124',
name: 'ttt'
}, {
id: '45',
name: 'yyy'
}];
var result = _(arr1).differenceBy(arr2, 'id').concat(arr2).value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.js"></script>
我提交这个答案只是因为人们对浏览器和维护对象的顺序表示担忧。我认识到这不是实现目标的最有效方法。
话虽如此,为了便于阅读,我将问题分解为两个函数。
// The following function is used for each itertion in the function updateObjectsInArr
const newObjInInitialArr = function(initialArr, newObject) {
let id = newObject.id;
let newArr = [];
for (let i = 0; i < initialArr.length; i++) {
if (id === initialArr[i].id) {
newArr.push(newObject);
} else {
newArr.push(initialArr[i]);
}
}
return newArr;
};
const updateObjectsInArr = function(initialArr, newArr) {
let finalUpdatedArr = initialArr;
for (let i = 0; i < newArr.length; i++) {
finalUpdatedArr = newObjInInitialArr(finalUpdatedArr, newArr[i]);
}
return finalUpdatedArr
}
const revisedArr = updateObjectsInArr(arr1, arr2);
多亏了 ES6,我们可以用简单的方法做到这一点 -> 例如在 util.js 模块 ;))).
合并 2 个实体数组
export const mergeArrays = (arr1, arr2) =>
arr1 && arr1.map(obj => arr2 && arr2.find(p => p.id === obj.id) || obj);
gets 2 array and merges it.. Arr1 is main array which is priority is
high on merge process
合并具有相同类型实体的数组
export const mergeArrayWithObject = (arr, obj) => arr && arr.map(t => t.id === obj.id ? obj : t);
it merges the same kind of array of type with some kind of type for
示例:人物数组 ->
[{id:1, name:"Bir"},{id:2, name: "Iki"},{id:3, name:"Uc"}]
second param Person {id:3, name: "Name changed"}
结果是
[{id:1, name:"Bir"},{id:2, name: "Iki"},{id:3, name:"Name changed"}]
function getMatch(elem) {
function action(ele, val) {
if(ele === val){
elem = arr2[i];
}
}
for (var i = 0; i < arr2.length; i++) {
action(elem.id, Object.values(arr2[i])[0]);
}
return elem;
}
var modified = arr1.map(getMatch);
Object.assign(target, source)
怎么了?
数组在Javascript中仍然是类型对象,所以只要找到匹配的键,使用赋值仍然应该重新分配运算符解析的任何匹配键,对吗?
我接受了这个,因为它对我来说很有意义。为读者添加评论!
masterData = [{id: 1, name: "aaaaaaaaaaa"},
{id: 2, name: "Bill"},
{id: 3, name: "ccccccccc"}];
updatedData = [{id: 3, name: "Cat"},
{id: 1, name: "Apple"}];
updatedData.forEach(updatedObj=> {
// For every updatedData object (dataObj), find the array index in masterData where the IDs match.
let indexInMasterData = masterData.map(masterDataObj => masterDataObj.id).indexOf(updatedObj.id); // First make an array of IDs, to use indexOf().
// If there is a matching ID (and thus an index), replace the existing object in masterData with the updatedData's object.
if (indexInMasterData !== undefined) masterData.splice(indexInMasterData, 1, updatedObj);
});
/* masterData becomes [{id: 1, name: "Apple"},
{id: 2, name: "Bill"},
{id: 3, name: "Cat"}]; as you want.`*/
这是我在 TypeScript 中的做法:
const index = this.array.indexOf(this.objectToReplace);
this.array[index] = newObject;
考虑到接受的答案对于大型阵列 O(nm) 可能效率低下,我通常更喜欢这种方法,O(2n + 2m):
function mergeArrays(arr1 = [], arr2 = []){
//Creates an object map of id to object in arr1
const arr1Map = arr1.reduce((acc, o) => {
acc[o.id] = o;
return acc;
}, {});
//Updates the object with corresponding id in arr1Map from arr2,
//creates a new object if none exists (upsert)
arr2.forEach(o => {
arr1Map[o.id] = o;
});
//Return the merged values in arr1Map as an array
return Object.values(arr1Map);
}
单元测试:
it('Merges two arrays using id as the key', () => {
var arr1 = [{id:'124',name:'qqq'}, {id:'589',name:'www'}, {id:'45',name:'eee'}, {id:'567',name:'rrr'}];
var arr2 = [{id:'124',name:'ttt'}, {id:'45',name:'yyy'}];
const actual = mergeArrays(arr1, arr2);
const expected = [{id:'124',name:'ttt'}, {id:'589',name:'www'}, {id:'45',name:'yyy'}, {id:'567',name:'rrr'}];
expect(actual.sort((a, b) => (a.id < b.id)? -1: 1)).toEqual(expected.sort((a, b) => (a.id < b.id)? -1: 1));
})
这里有一个更透明的方法。我发现单行代码更难阅读,更难调试。
export class List {
static replace = (object, list) => {
let newList = [];
list.forEach(function (item) {
if (item.id === object.id) {
newList.push(object);
} else {
newList.push(item);
}
});
return newList;
}
}
关于时间与 space 总是会有很好的争论,但是这些天我发现使用 space 更适合长期 运行.. 除了数学让我们看看使用哈希图、字典或关联数组来解决问题的一种实用方法,无论您喜欢标记简单的数据结构..
var marr2 = new Map(arr2.map(e => [e.id, e]));
arr1.map(obj => marr2.has(obj.id) ? marr2.get(obj.id) : obj);
我喜欢这种方法,因为尽管您可能会与数字较少的数组争论,但您是在浪费space,因为像@Tushar 方法这样的内联方法的性能与这种方法非常接近。然而,我 运行 进行了一些测试,图表显示了这两种方法在 n 0 - 1000 之间的性能表现。你可以根据你的情况决定哪种方法最适合你,但根据我的经验,用户并不太在意关于小 space 但他们确实关心小速度。
这是我的性能测试 运行 数据来源
var n = 1000;
var graph = new Array();
for( var x = 0; x < n; x++){
var arr1s = [...Array(x).keys()];
var arr2s = arr1s.filter( e => Math.random() > .5);
var arr1 = arr1s.map(e => {return {id: e, name: 'bill'}});
var arr2 = arr2s.map(e => {return {id: e, name: 'larry'}});
// Map 1
performance.mark('p1s');
var marr2 = new Map(arr2.map(e => [e.id, e]));
arr1.map(obj => marr2.has(obj.id) ? marr2.get(obj.id) : obj);
performance.mark('p1e');
// Map 2
performance.mark('p2s');
arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
performance.mark('p2e');
graph.push({ x: x, r1: performance.measure('HashMap Method', 'p1s', 'p1e').duration, r2: performance.measure('Inner Find', 'p2s','p2e').duration});
}
使用 array.map 的公认答案是正确的,但您必须记住将其分配给另一个变量,因为 array.map 不会更改原始数组,它实际上会创建一个新数组。
//newArr contains the mapped array from arr2 to arr1.
//arr1 still contains original value
var newArr = arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
// here find all the items that are not it the arr1
const temp = arr1.filter(obj1 => !arr2.some(obj2 => obj1.id === obj2.id))
// then just concat it
arr1 = [...temp, ...arr2]
我喜欢用 foreach()
遍历 arr2
并使用 findIndex()
检查 arr1
中是否出现:
var arr1 = [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}]
var arr2 = [{id:'124',name:'ttt'},
{id:'45',name:'yyy'}]
arr2.forEach(element => {
const itemIndex = arr1.findIndex(o => o.id === element.id);
if(itemIndex > -1) {
arr1[itemIndex] = element;
} else {
arr1 = arr1.push(element);
}
});
console.log(arr1)
Array.prototype.update = function(...args) {
return this.map(x=>args.find((c)=>{return c.id===x.id}) || x)
}
const result =
[
{id:'1',name:'test1'},
{id:'2',name:'test2'},
{id:'3',name:'test3'},
{id:'4',name:'test4'}
]
.update({id:'1',name:'test1.1'}, {id:'3',name:'test3.3'})
console.log(result)
我想提出另一个解决方案:
const objectToReplace = this.array.find(arrayItem => arrayItem.id === requiredItem.id);
Object.assign(objectToReplace, newObject);
我有这个 javascript 对象:
var arr1 = [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}]
var arr2 = [{id:'124',name:'ttt'},
{id:'45',name:'yyy'}]
我需要将 arr1 中的对象替换为 arr2 中具有相同 id 的项目。
所以这是我想要得到的结果:
var arr1 = [{id:'124',name:'ttt'},
{id:'589',name:'www'},
{id:'45',name:'yyy'},
{id:'567',name:'rrr'}]
如何使用 javascript 实现它?
您可以使用 Array#map
with Array#find
.
arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
var arr1 = [{
id: '124',
name: 'qqq'
}, {
id: '589',
name: 'www'
}, {
id: '45',
name: 'eee'
}, {
id: '567',
name: 'rrr'
}];
var arr2 = [{
id: '124',
name: 'ttt'
}, {
id: '45',
name: 'yyy'
}];
var res = arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
console.log(res);
此处,如果在 arr2
中找到 id
,arr2.find(o => o.id === obj.id)
将 return 元素,即来自 arr2
的对象。如果不是,则 arr1
中的相同元素即 obj
是 returned.
由于您使用的是 Lodash,因此您可以使用 _.map
和 _.find
来确保支持主要浏览器。
最后我会选择这样的东西:
function mergeById(arr) {
return {
with: function(arr2) {
return _.map(arr, item => {
return _.find(arr2, obj => obj.id === item.id) || item
})
}
}
}
var result = mergeById([{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}])
.with([{id:'124',name:'ttt'}, {id:'45',name:'yyy'}])
console.log(result);
<script src="https://raw.githubusercontent.com/lodash/lodash/4.13.1/dist/lodash.js"></script>
如果您不关心数组的顺序,那么您可能希望通过 id
使用 differenceBy() and then simply use concat() 获得 arr1
和 arr2
之间的差异追加所有更新的对象。
var result = _(arr1).differenceBy(arr2, 'id').concat(arr2).value();
var arr1 = [{
id: '124',
name: 'qqq'
}, {
id: '589',
name: 'www'
}, {
id: '45',
name: 'eee'
}, {
id: '567',
name: 'rrr'
}]
var arr2 = [{
id: '124',
name: 'ttt'
}, {
id: '45',
name: 'yyy'
}];
var result = _(arr1).differenceBy(arr2, 'id').concat(arr2).value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.js"></script>
我提交这个答案只是因为人们对浏览器和维护对象的顺序表示担忧。我认识到这不是实现目标的最有效方法。
话虽如此,为了便于阅读,我将问题分解为两个函数。
// The following function is used for each itertion in the function updateObjectsInArr
const newObjInInitialArr = function(initialArr, newObject) {
let id = newObject.id;
let newArr = [];
for (let i = 0; i < initialArr.length; i++) {
if (id === initialArr[i].id) {
newArr.push(newObject);
} else {
newArr.push(initialArr[i]);
}
}
return newArr;
};
const updateObjectsInArr = function(initialArr, newArr) {
let finalUpdatedArr = initialArr;
for (let i = 0; i < newArr.length; i++) {
finalUpdatedArr = newObjInInitialArr(finalUpdatedArr, newArr[i]);
}
return finalUpdatedArr
}
const revisedArr = updateObjectsInArr(arr1, arr2);
多亏了 ES6,我们可以用简单的方法做到这一点 -> 例如在 util.js 模块 ;))).
合并 2 个实体数组
export const mergeArrays = (arr1, arr2) => arr1 && arr1.map(obj => arr2 && arr2.find(p => p.id === obj.id) || obj);
gets 2 array and merges it.. Arr1 is main array which is priority is high on merge process
合并具有相同类型实体的数组
export const mergeArrayWithObject = (arr, obj) => arr && arr.map(t => t.id === obj.id ? obj : t);
it merges the same kind of array of type with some kind of type for
示例:人物数组 ->
[{id:1, name:"Bir"},{id:2, name: "Iki"},{id:3, name:"Uc"}]
second param Person {id:3, name: "Name changed"}
结果是
[{id:1, name:"Bir"},{id:2, name: "Iki"},{id:3, name:"Name changed"}]
function getMatch(elem) {
function action(ele, val) {
if(ele === val){
elem = arr2[i];
}
}
for (var i = 0; i < arr2.length; i++) {
action(elem.id, Object.values(arr2[i])[0]);
}
return elem;
}
var modified = arr1.map(getMatch);
Object.assign(target, source)
怎么了?
数组在Javascript中仍然是类型对象,所以只要找到匹配的键,使用赋值仍然应该重新分配运算符解析的任何匹配键,对吗?
我接受了这个,因为它对我来说很有意义。为读者添加评论!
masterData = [{id: 1, name: "aaaaaaaaaaa"},
{id: 2, name: "Bill"},
{id: 3, name: "ccccccccc"}];
updatedData = [{id: 3, name: "Cat"},
{id: 1, name: "Apple"}];
updatedData.forEach(updatedObj=> {
// For every updatedData object (dataObj), find the array index in masterData where the IDs match.
let indexInMasterData = masterData.map(masterDataObj => masterDataObj.id).indexOf(updatedObj.id); // First make an array of IDs, to use indexOf().
// If there is a matching ID (and thus an index), replace the existing object in masterData with the updatedData's object.
if (indexInMasterData !== undefined) masterData.splice(indexInMasterData, 1, updatedObj);
});
/* masterData becomes [{id: 1, name: "Apple"},
{id: 2, name: "Bill"},
{id: 3, name: "Cat"}]; as you want.`*/
这是我在 TypeScript 中的做法:
const index = this.array.indexOf(this.objectToReplace);
this.array[index] = newObject;
考虑到接受的答案对于大型阵列 O(nm) 可能效率低下,我通常更喜欢这种方法,O(2n + 2m):
function mergeArrays(arr1 = [], arr2 = []){
//Creates an object map of id to object in arr1
const arr1Map = arr1.reduce((acc, o) => {
acc[o.id] = o;
return acc;
}, {});
//Updates the object with corresponding id in arr1Map from arr2,
//creates a new object if none exists (upsert)
arr2.forEach(o => {
arr1Map[o.id] = o;
});
//Return the merged values in arr1Map as an array
return Object.values(arr1Map);
}
单元测试:
it('Merges two arrays using id as the key', () => {
var arr1 = [{id:'124',name:'qqq'}, {id:'589',name:'www'}, {id:'45',name:'eee'}, {id:'567',name:'rrr'}];
var arr2 = [{id:'124',name:'ttt'}, {id:'45',name:'yyy'}];
const actual = mergeArrays(arr1, arr2);
const expected = [{id:'124',name:'ttt'}, {id:'589',name:'www'}, {id:'45',name:'yyy'}, {id:'567',name:'rrr'}];
expect(actual.sort((a, b) => (a.id < b.id)? -1: 1)).toEqual(expected.sort((a, b) => (a.id < b.id)? -1: 1));
})
这里有一个更透明的方法。我发现单行代码更难阅读,更难调试。
export class List {
static replace = (object, list) => {
let newList = [];
list.forEach(function (item) {
if (item.id === object.id) {
newList.push(object);
} else {
newList.push(item);
}
});
return newList;
}
}
关于时间与 space 总是会有很好的争论,但是这些天我发现使用 space 更适合长期 运行.. 除了数学让我们看看使用哈希图、字典或关联数组来解决问题的一种实用方法,无论您喜欢标记简单的数据结构..
var marr2 = new Map(arr2.map(e => [e.id, e]));
arr1.map(obj => marr2.has(obj.id) ? marr2.get(obj.id) : obj);
我喜欢这种方法,因为尽管您可能会与数字较少的数组争论,但您是在浪费space,因为像@Tushar 方法这样的内联方法的性能与这种方法非常接近。然而,我 运行 进行了一些测试,图表显示了这两种方法在 n 0 - 1000 之间的性能表现。你可以根据你的情况决定哪种方法最适合你,但根据我的经验,用户并不太在意关于小 space 但他们确实关心小速度。
这是我的性能测试 运行 数据来源
var n = 1000;
var graph = new Array();
for( var x = 0; x < n; x++){
var arr1s = [...Array(x).keys()];
var arr2s = arr1s.filter( e => Math.random() > .5);
var arr1 = arr1s.map(e => {return {id: e, name: 'bill'}});
var arr2 = arr2s.map(e => {return {id: e, name: 'larry'}});
// Map 1
performance.mark('p1s');
var marr2 = new Map(arr2.map(e => [e.id, e]));
arr1.map(obj => marr2.has(obj.id) ? marr2.get(obj.id) : obj);
performance.mark('p1e');
// Map 2
performance.mark('p2s');
arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
performance.mark('p2e');
graph.push({ x: x, r1: performance.measure('HashMap Method', 'p1s', 'p1e').duration, r2: performance.measure('Inner Find', 'p2s','p2e').duration});
}
使用 array.map 的公认答案是正确的,但您必须记住将其分配给另一个变量,因为 array.map 不会更改原始数组,它实际上会创建一个新数组。
//newArr contains the mapped array from arr2 to arr1.
//arr1 still contains original value
var newArr = arr1.map(obj => arr2.find(o => o.id === obj.id) || obj);
// here find all the items that are not it the arr1
const temp = arr1.filter(obj1 => !arr2.some(obj2 => obj1.id === obj2.id))
// then just concat it
arr1 = [...temp, ...arr2]
我喜欢用 foreach()
遍历 arr2
并使用 findIndex()
检查 arr1
中是否出现:
var arr1 = [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}]
var arr2 = [{id:'124',name:'ttt'},
{id:'45',name:'yyy'}]
arr2.forEach(element => {
const itemIndex = arr1.findIndex(o => o.id === element.id);
if(itemIndex > -1) {
arr1[itemIndex] = element;
} else {
arr1 = arr1.push(element);
}
});
console.log(arr1)
Array.prototype.update = function(...args) {
return this.map(x=>args.find((c)=>{return c.id===x.id}) || x)
}
const result =
[
{id:'1',name:'test1'},
{id:'2',name:'test2'},
{id:'3',name:'test3'},
{id:'4',name:'test4'}
]
.update({id:'1',name:'test1.1'}, {id:'3',name:'test3.3'})
console.log(result)
我想提出另一个解决方案:
const objectToReplace = this.array.find(arrayItem => arrayItem.id === requiredItem.id);
Object.assign(objectToReplace, newObject);