简体   繁体   English

替换数组中的对象

[英]Replacing objects in array

I have this javascript object:我有这个 javascript object:

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'}]

I need to replace objects in arr1 with items from arr2 with same id .我需要用arr2中具有相同id的项目替换arr1中的对象。

So here is the result I want to get:所以这是我想要得到的结果:

var arr1 = [{id:'124',name:'ttt'}, 
           {id:'589',name:'www'}, 
           {id:'45',name:'yyy'},
           {id:'567',name:'rrr'}]

How can I implement it using javascript?如何使用 javascript 实现它?

You can use Array#map with Array#find .您可以将Array#mapArray#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);

Here, arr2.find(o => o.id === obj.id) will return the element ie object from arr2 if the id is found in the arr2 .在这里, arr2.find(o => o.id === obj.id)如果在arr2找到了id则会从arr2返回元素即对象。 If not, then the same element in arr1 ie obj is returned.如果不是,则返回arr1的相同元素,即obj

What's wrong with Object.assign(target, source) ? Object.assign(target, source)什么问题?

在此处输入图片说明

Arrays are still type object in Javascript, so using assign should still reassign any matching keys parsed by the operator as long as matching keys are found, right?数组在 Javascript 中仍然是类型对象,因此只要找到匹配的键,使用assign 仍然应该重新分配运算符解析的任何匹配键,对吗?

Since you're using Lodash you could use _.map and _.find to make sure major browsers are supported.由于您使用的是 Lodash,因此您可以使用_.map_.find来确保支持主要浏览器。

In the end I would go with something like:最后,我会选择类似的东西:

 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>

I'd like to suggest another solution:我想建议另一种解决方案:

const objectToReplace = this.array.find(arrayItem => arrayItem.id === requiredItem.id);
Object.assign(objectToReplace, newObject);

Thanks to ES6 we can made it with easy way -> for example on util.js module ;))).感谢 ES6,我们可以用简单的方法实现它 -> 例如在 util.js 模块上 ;)))。

  1. Merge 2 array of entity合并 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获取 2 个数组并合并它.. Arr1 是主数组,它在合并过程中优先级高

  1. Merge array with same type of entity合并具有相同类型实体的数组

    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它将同一种类型的数组与某种类型合并

example: array of person ->示例:人员数组 ->

[{id:1, name:"Bir"},{id:2, name: "Iki"},{id:3, name:"Uc"}]   
second param Person {id:3, name: "Name changed"}   

result is结果是

[{id:1, name:"Bir"},{id:2, name: "Iki"},{id:3, name:"Name changed"}]

Considering that the accepted answer is probably inefficient for large arrays, O(nm), I usually prefer this approach, O(2n + 2m):考虑到公认的答案对于大阵列 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);
}

Unit test:单元测试:

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

There is always going to be a good debate on time vs space, however these days I've found using space is better for the long run.. Mathematics aside let look at a one practical approach to the problem using hashmaps, dictionaries, or associative array's whatever you feel like labeling the simple data structure..关于时间与空间的争论总是会发生,但是现在我发现从长远来看,使用空间更好......抛开数学,让我们看看使用哈希图、字典或关联的一种实用方法来解决这个问题数组是你想要标记简单数据结构的任何东西。

    var marr2 = new Map(arr2.map(e => [e.id, e]));
    arr1.map(obj => marr2.has(obj.id) ? marr2.get(obj.id) : obj);

I like this approach because though you could argue with an array with low numbers you are wasting space because an inline approach like @Tushar approach performs indistinguishably close to this method.我喜欢这种方法,因为尽管您可以与低数字的数组争论,但您正在浪费空间,因为像 @Tushar 方法这样的内联方法与此方法的性能非常接近。 However I ran some tests and the graph shows how performant in ms both methods perform from n 0 - 1000. You can decide which method works best for you, for your situation but in my experience users don't care to much about small space but they do care about small speed.但是我进行了一些测试,图表显示了两种方法在 n 0 - 1000 之间的性能如何。您可以根据自己的情况决定哪种方法最适合您,但根据我的经验,用户不太关心小空间,但是他们确实关心小速度。


绩效衡量


Here is my performance test I ran for source of data这是我为数据源运行的性能测试

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

If you don't care about the order of the array, then you may want to get the difference between arr1 and arr2 by id using differenceBy() and then simply use concat() to append all the updated objects.如果您不关心数组的顺序,那么您可能希望使用differenceBy()通过id获取arr1arr2之间的差异,然后简单地使用concat()附加所有更新的对象。

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>

I am only submitting this answer because people expressed concerns over browsers and maintaining the order of objects.我提交这个答案只是因为人们表达了对浏览器和维护对象顺序的担忧。 I recognize that it is not the most efficient way to accomplish the goal.我认识到这不是实现目标的最有效方法。

Having said this, I broke the problem down into two functions for readability.话虽如此,为了可读性,我将问题分解为两个函数。

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

jsfiddle提琴手

Here a transparent approach, instead of an unreadable and undebuggable oneliner:这是一个透明的方法,而不是一个不可读和不可调试的单行:

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

I like to go through arr2 with foreach() and use findIndex() for checking for occurrence in arr1 :我喜欢使用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)

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

I went with this, because it makes sense to me.我选择了这个,因为这对我来说很有意义。 Comments added for readers!为读者添加评论!

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.`*/

This is how I do it in TypeScript:这就是我在 TypeScript 中的做法:

const index = this.array.indexOf(this.objectToReplace);
this.array[index] = newObject;

The accepted answer using array.map is correct but you have to remember to assign it to another variable since array.map doesnt change original array, it actually creates a new array.使用 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);

 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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM