【问题标题】:Replacing objects in array替换数组中的对象
【发布时间】:2016-10-01 19:18:04
【问题描述】:

我有这个 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 实现它?

【问题讨论】:

标签: javascript lodash


【解决方案1】:

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

在这里,如果在arr2 中找到idarr2.find(o => o.id === obj.id) 将返回元素,即来自arr2 的对象。如果不是,则返回arr1 中的相同元素,即obj

【讨论】:

  • @Michael 不,它只适用于最新的浏览器。您可以看到Specifications,对于旧版浏览器,请使用polyfill
  • 有时我们会遇到非常有启发性和有用的代码;这是其中之一。谢谢
  • 这是一种非常低效的实现目标的方法,因为 find 方法每次调用时都会迭代整个数组。
  • @JavaBanana 你有什么建议?
  • 如果还包含 ES5 语法会很棒。
【解决方案2】:

Object.assign(target, source) 有什么问题?

数组在Javascript中仍然是类型对象,所以只要找到匹配的键,使用assign仍然应该重新分配运算符解析的任何匹配键,对吧?

【讨论】:

【解决方案3】:

由于您使用的是 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>

【讨论】:

    【解决方案4】:

    感谢 ES6,我们可以通过简单的方式实现 -> 例如在 util.js 模块上;)))。

    1. 合并2个实体数组

      export const mergeArrays = (arr1, arr2) => 
         arr1 && arr1.map(obj => arr2 && arr2.find(p => p.id === obj.id) || obj);
      

    获取 2 个数组并将其合并.. Arr1 是主数组,优先级为 高合并进程

    1. 合并具有相同类型实体的数组

      export const mergeArrayWithObject = (arr, obj) => arr && arr.map(t => t.id === obj.id ? obj : t);
      

    它将相同类型的数组与某种类型的类型合并

    示例:人员数组 ->

    [{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"}]
    

    【讨论】:

      【解决方案5】:

      关于时间与空间的争论总是很激烈,但是这些天我发现从长远来看,使用空间更好。除了数学,让我们看看使用哈希图、字典的一种实用方法, 或关联数组的任何你喜欢标记简单数据结构的东西..

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

      我喜欢这种方法,因为尽管您可以与具有低数字的数组争论,但您正在浪费空间,因为像 @Tushar 方法这样的内联方法的性能与这种方法几乎没有区别。但是,我进行了一些测试,图表显示了这两种方法在 ms 中的性能如何从 n 0 到 1000 执行。您可以根据您的情况决定哪种方法最适合您,但根据我的经验,用户不太关心小空间,但他们确实关心小速度。



      这是我为数据源运行的性能测试

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

      【讨论】:

        【解决方案6】:

        考虑到接受的答案对于大型数组可能效率低下,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));
        })
        

        【讨论】:

        • 这会改变数组吗?
        • @user3808307 不会,因为 reduce 函数不会发生变异。见reduce
        【解决方案7】:
        // 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]
        

        【讨论】:

          【解决方案8】:

          如果您不关心数组的顺序,那么您可能希望通过id 使用differenceBy() 来获得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);
          &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.13.1/lodash.js"&gt;&lt;/script&gt;

          【讨论】:

            【解决方案9】:

            我之所以提交这个答案,是因为人们表达了对浏览器和维护对象顺序的担忧。我认识到这不是实现目标的最有效方式。

            话虽如此,为了便于阅读,我将问题分解为两个函数。

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

            【讨论】:

            • 仅供参考,它似乎没有在小提琴中工作。
            • 我忘了在小提琴中添加console.log。我更新了它,它现在应该打印出结果。
            • 似乎仍然不起作用,但在我的应用程序中确实有效。这是主要的:-)
            【解决方案10】:

            这里是一种更透明的方法。我发现 oneliners 更难阅读,也更难调试。

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

            【讨论】:

              【解决方案11】:

              我喜欢通过arr2foreach() 并使用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)

              【讨论】:

                【解决方案12】:
                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);
                

                【讨论】:

                  【解决方案13】:

                  我选择了这个,因为它对我来说很有意义。为读者添加评论!

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

                  【讨论】:

                    【解决方案14】:

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

                    【讨论】:

                      【解决方案15】:

                      这就是我在 TypeScript 中的做法:

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

                      【讨论】:

                      • 这种方式在javascript中找不到对象的索引。
                      • @PranKumarSarkar 当然可以,它只会匹配对象引用jsfiddle.net/hutchthehippo/g9a6wro5/22
                      • 如果数组中的对象是相同类型的,那么你可以。但在我的例子中,我的数组包含 json 对象,每个 json 对象包含不同的键值对,所以它给出了错误的结果。
                      猜你喜欢
                      • 1970-01-01
                      • 2020-11-08
                      • 2020-11-16
                      • 2018-10-03
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多