合并两个对象及其内部的数组

Posted

技术标签:

【中文标题】合并两个对象及其内部的数组【英文标题】:Merging Two Objects with arrays inside of it 【发布时间】:2016-06-10 09:46:31 【问题描述】:

我正在尝试合并其中包含数组和其他元素的两个 javascript 对象。举个例子吧。


        "addresses": [
            
                "type": "email",
                "tags": [
                    "Responsável",
                    "Pai"
                ],
                "address": "johndoepai1@gmail.com"
            ,
            
                "type": "phone",
                "tags": [
                    "Responsável",
                    "Mãe"
                ],
                "address": "551138839332"
            ,
            
                "type": "email",
                "tags": [
                    "Mãe"
                ],
                "address": "johndoemae1@gmail.com"
            ,
            
                "type": "email",
                "tags": [
                    "Aluno"
                ],
                "address": "johndoealuno1@gmail.com"
            
        ],
        "class": [
            "Sala 1",
            "Sala 2",
            "Sala 3"
        ],
        "fullname": "John Doe 1",
        "eid": "2",
        "invisible": true,
        "see_all": false
    ,
    
        "addresses": [
            
                "type": "email",
                "tags": [
                    "Responsável",
                    "Pai"
                ],
                "address": "johndoepai2@gmail.com"
            ,
            
                "type": "email",
                "tags": [
                    "Responsável",
                    "Pai"
                ],
                "address": "johndoepai3@gmail.com"
            ,
            
                "type": "phone",
                "tags": [
                    "Pai"
                ],
                "address": "5519985504400"
            ,
            
                "type": "phone",
                "tags": [
                    "Responsável",
                    "Mãe"
                ],
                "address": "551138839333"
            ,
            
                "type": "email",
                "tags": [
                    "Mãe"
                ],
                "address": "11 983340440"
            ,
            
                "type": "email",
                "tags": [
                    "Aluno"
                ],
                "address": ""
            
        ],
        "class": [
            "Sala 4",
            "Sala 5",
            "Sala 6"
        ],
        "fullname": "John Doe 1",
        "eid": "2",
        "invisible": false,
        "see_all": true
    

如您所见,我们可以在其中包含数组,其中包含一些字符串。 我尝试使用一些递归来做到这一点,但没有成功。

function mergeObjects(target, source)
    var item, tItem, o, idx;
    if (typeof source == 'undefined') 
      return source;
     else if (typeof target == 'undefined') 
      return target;
    

    for (var prop in source) 
        item = source[prop];
        //check if the first element is indeed an object.
        if (typeof item == 'object' && item !== null) 
            //if the first item is an array
          if (_.isArray(item) && item.length) 
              //dealing with array of primitives
              if (typeof item[0] != 'object') 
                  item.forEach(function(conteudo)
                      //push to the target all the elements;
                      target[prop].push(conteudo);
                  );
              else
                //dealing with array of objects;
                for(var attr in item)
                    idx = ;
                    tItem = target[attr]
                    mergeObjects(tItem,item);
                
              
          //if its a normal object
          else 
           // deal with object
           mergeObjects(target[prop],item);
         

         else 
             // item is a primitive, just copy it over
             target[prop] = item;
           
    
    return target;

我预料到了:

  
        "addresses": [
            
                "type": "email",
                "tags": [
                    "Responsável",
                    "Pai"
                ],
                "address": "johndoepai2@gmail.com"
            ,
            
                "type": "email",
                "tags": [
                    "Responsável",
                    "Pai"
                ],
                "address": "johndoepai3@gmail.com"
            ,
            
                "type": "phone",
                "tags": [
                    "Pai"
                ],
                "address": "5519985504400"
            ,
            
                "type": "phone",
                "tags": [
                    "Responsável",
                    "Mãe"
                ],
                "address": "551138839333"
            ,
            
                "type": "email",
                "tags": [
                    "Mãe"
                ],
                "address": "11 983340440"
            ,
            
                "type": "email",
                "tags": [
                    "Aluno"
                ],
                "address": ""
            
        ],
        "class": [
            "Sala 4",
            "Sala 5",
            "Sala 6",
            "Sala 1",
            "Sala 2",
            "Sala 3"
        ],
        "fullname": "John Doe 1",
        "eid": "2",
        "invisible": true,
        "see_all": false
    

"fullname": "John Doe 1",
  "eid": "1234",
  "classes": [
    "Sala 1",
    "Sala 2",
    "Sala 3",
    "Sala 4",
    "Sala 5",
    "Sala 6"
  ],
  "addresses": [
    "type": "phone",
    "tags": [
      "Responsável",
      "Mãe"
    ],
    "address": "551138839332"
  , 
    "type": "email",
    "tags": [
      "Mãe"
    ],
    "address": "johndoemae1@gmail.com"
  , 
    "type": "email",
    "tags": [
      "Aluno"
    ],
    "address": "johndoealuno1@gmail.com"
  , 
    "type": "email",
    "tags": [
      "Responsável",
      "Pai"
    ],
    "address": "johndoepai2@gmail.com"
  , 
    "type": "email",
    "tags": [
      "Responsável",
      "Pai"
    ],
    "address": "johndoepai3@gmail.com"
  , 
    "type": "phone",
    "tags": [
      "Pai"
    ],
    "address": "5519985504400"
  , 
    "type": "phone",
    "tags": [
      "Responsável",
      "Mãe"
    ],
    "address": "551138839333"
  ],
  "invisible": true,
  "see_all": true

但我得到的是,如您所见,缺少一些电子邮件元素。


    "addresses": [
        
            "type": "email",
            "tags": [
                "Responsável",
                "Pai"
            ],
            "address": "johndoepai2@gmail.com"
        ,
        
            "type": "email",
            "tags": [
                "Responsável",
                "Pai"
            ],
            "address": "johndoepai3@gmail.com"
        ,
        
            "type": "phone",
            "tags": [
                "Pai"
            ],
            "address": "5519985504400"
        ,
        
            "type": "phone",
            "tags": [
                "Responsável",
                "Mãe"
            ],
            "address": "551138839333"
        ,
        
            "type": "email",
            "tags": [
                "Mãe"
            ],
            "address": "11 983340440"
        ,
        
            "type": "email",
            "tags": [
                "Aluno"
            ],
            "address": ""
        
    ],
    "class": [
        "Sala 4",
        "Sala 5",
        "Sala 6",
        "Sala 1",
        "Sala 2",
        "Sala 3"
    ],
    "fullname": "John Doe 1",
    "eid": "2",
    "invisible": true,
    "see_all": false

元素的顺序不是问题。 我错过了递归树的哪一点?

【问题讨论】:

您可以尝试查看下划线库:underscorejs.org,看看是否可以实现相同的功能。 @JamesonYu 来自下划线,对于我搜索的内容没有任何帮助。我已经尝试了 lodash 的 _.merge,但它并没有达到我的预期。 您的第二个代码块是您期望的结果吗?如果没有,请告诉我们您期望的结果。 @jfriend00 已修复。我忘了把我得到的东西。 【参考方案1】:

简短回答:您必须先自己合并数组。或者你可以使用类似 Lodash's mergeWidth Ramda's R.mergeWith 之类的好东西(有关 Ramda 的示例代码,请参见下文)。

你想要像原生的 Object.assign (有限的浏览器支持,所以它必须是 polyfill)或者你的 JS 库有的任何合并。

您遇到的问题是所有这些合并实现如何处理重复键。通常key会被设置为最后传入的值:

var thing1 = 
  someProperty: 'foo'
;

var thing2 = 
  someProperty: 'bar'
;

var result1 = Object.assign(, thing1, thing2);
// -> someProperty: 'bar' (set to the last value passed in, in thing2)

var result2 = Object.assign(, thing2, thing1);
// -> someProperty: 'foo' (again set to the last value passed in, in thing1)

// Make the *** snippet display the output.
document.body.innerhtml += JSON.stringify(result1, null, 2);
document.body.innerHTML += '<br>' + JSON.stringify(result2, null, 2);

与您的示例的工作方式相同,只是属性是一个更复杂的数组。它只是被设置为传入的最后一个数组:

var thing1 = 
  someList: [1, 2, 3]
;

var thing2 = 
  someList: [4, 5, 6]
;

var result = Object.assign(, thing1, thing2);
// -> someList: [4, 5, 6]

// Make the *** snippet display the output.
document.body.innerHTML = JSON.stringify(result, null, 2);

如果您使用像 Ramda 这样的好东西,您可以合并并指定一个函数来控制键相等时的行为,就像您的情况一样。在这里我们可以检查该值是否为数组,如果是则连接。否则,返回最新值(如上所述):

var thing1 = someList: [1, 2, 3];
var thing2 = someList: [4, 5, 6];

var dupeMergeFn = function(a, b) 
  return (Array.isArray(a)) ? a.concat(b) : b;
;

var result = R.mergeWith(dupeMergeFn, thing1, thing2);

// Make the *** snippet display the output.
document.body.innerHTML = JSON.stringify(result, null, 2);
&lt;script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.19.1/ramda.min.js"&gt;&lt;/script&gt;

【讨论】:

问题是,我应该保留这两个内容,我不能像那些例子那样丢失其中一个。 我更新了这篇文章来展示如何使用 Ramda 的 mergeWith 来实现你想要的。应该以类似的方式与 Lodash 的 mergeWith

以上是关于合并两个对象及其内部的数组的主要内容,如果未能解决你的问题,请参考以下文章

js利用Map对两个数组对象进行合并去重

在 JavaScript 中合并两个对象(NodeList)数组

如何合并两个对象数组

js 数组对象根据相同value的key合并两个对象

javascript 数组对象根据相同属性值{key:value}合并两个对象

vue合并两个对象数组