代码之家  ›  专栏  ›  技术社区  ›  Slim

如何检测两个阵列之间的对象差异?

  •  0
  • Slim  · 技术社区  · 7 年前

    lodash 只是javascript数据结构和函数。

    例如:

    I have a first array which named arr1 = [
    {
        name: 'attribute 1',
        id: 12,
        value: 40,
        docs:[],
        version: 1,
    },
    {
        name: 'attribute 41',
        id: 12,
        value: 6,
        version: 1,
    }
    ]
    

    array2 =  [
        {
            name: 'attribute 1',
            attributeTypeId: 12,
            value: 65,
            docs: ['bla bla']
        }
    ]
    

    我尝试遍历这两个数组,检测差异并返回如下数组:

    result = [
    {
        name: 'attribute 1',
        id: 12,
        value: 65,
        docs:['bla bla'],
        version: 1,
    },
    {
        name: 'attribute 41',
        id: 12,
        value: 6,
        version: 1,
    }]
    

    我编写了一些未完成的函数(尚未优化,只是一个暴力解决方案):

    const filterProperties = (e) => {
        return e.toLowerCase() !== 'name' && e.toLowerCase() !== 'id' 
    }
    
    // function sort
    
    const sortProperties = (a, b) => a < b ? -1 : 1;
    
    let result = []
    attributesUpdate.forEach(attr => {
        const attrProps = Object.getOwnPropertyNames(attr);
    
        // iterate the attributes
        for (let i = 0; i < attributes.length; i++) {
            let attribute = attributes[i];
            // check if the attribute to update has a different name or attributeTypeId
            if (attribute.name !== attr.name) {
                    result = result.concat(attr);
    
            }
    
            // check if the attribute to update has the same name, id
            // of the originalOne
            if (attribute.name === attr.name && attribute.id=== attr.id) {
                let obj = {
                    name: attribute.name,
                    id: attribute.id,
                }
                // get the properties of the attribute
                const attributeProps = Object.getOwnPropertyNames(attribute);
    
                // extract the name and id from the list
                const filtredAttributeProps = attributeProps.filter(filterProperties);
                const filteredattrProps = attrProps.filter(filterProperties);
    
    
                // returns the length of each array of properties
                const attrLength = filteredattrProps.length;
                const attributeLength = filtredAttributeProps.length;
    
                if (attrLength === attributeLength) {
                    for (let j = 0; j < attrLength; j++) {
                        const propName = filteredattrProps[j];
                        obj[propName] = attr[propName];
                    }
    
                    result = result.filter(e => e.name === attr.name 
                        && e.id=== attr.id)
                        .map(e => Object.assign(e, {obj}))
                }
    
                if (attrLength !== attributeLength) {
                    // sort the array of properties
                    const sortedAttrProps = filteredattrProps.sort(sortProperties);
                    const sortedAttributeProps = filtredAttributeProps.sort(sortProperties);
    
                    // check the shortest object
                    const min = attrLength < attributeLength ? attrLength : attributeLength;
                    // get the biggest object
                    const longestObjProps = attrLength === min ? sortedAttributeProps : sortedAttrProps;
                    const longestObj = attrLength === min ? attribute : attr
                    const shortestProps = attrLength === min ? sortedAttrProps: sortedAttributeProps;
                    const shortestObj = attrLength === min ? attr : attribute
    
                    // fill the object with attr properties
                    for(let j = 0; j < min; j++) {
                        const propName = shortestProps[j];
                        obj[propName] = shortestObj[propName];
                    }
    
    
                    // fill the remaining properties in the object
                    const remainingProperties = longestObjProps.filter(e => !shortestProps.includes(e));
                    for (let j = 0; j < remainingProperties.length; j++) {
                        const propName = remainingProperties[j];
                        obj[propName] = longestObj[propName]
                    }
    
                    if (!result.length || result.filter(e => e.name !== attr.name && 
                        e.id!== attr.id).length === 0) {
                            result.concat(obj);
                        }
    
                }
    
            }
        }
    })
    
    console.log('result: ', result);
    

    我得到了这样一个结果:

    [
    {
        name: 'attribute 1',
        attributeTypeId: 12,
        value: 65,
        docs: ['bla bla']
    }
    ]
    

    如何修复此代码以获得所需的结果?我希望我的问题不会被否决。欢迎提出任何建议。

    2 回复  |  直到 7 年前
        1
  •  1
  •   Skeets    7 年前

    这段代码所做的是循环遍历中的对象 array2 arr1 ,它只是更新该对象的属性。如果找不到,它会将对象添加到 arr1 .

    arr1 = [{
        name: 'attribute 1',
        id: 12,
        value: 40,
        docs: [],
        version: 1,
      },
      {
        name: 'attribute 41',
        id: 12,
        value: 6,
        version: 1,
      }
    ];
    
    array2 = [{
      name: 'attribute 1',
      attributeTypeId: 12,
      value: 65,
      docs: ['bla bla']
    }];
    
    updateArray(arr1, array2);
    
    console.log(arr1);
    
    function updateArray(arrayToUpdate, dataToUpdateWith) {
      dataToUpdateWith.forEach(function(obj) {
        var objToUpdate = checkIfNameIdExists(arrayToUpdate, obj.name, obj.attributeTypeId);
        if (objToUpdate === false) {
          objToUpdate = obj;
          arrayToUpdate.push(objToUpdate);
        } else {
          for (var prop in obj) {
            if (objToUpdate.hasOwnProperty(prop)) {
              var nameInFinalObject = prop;
              if (prop === "attributeTypeId") {
                nameInFinalObject = "id";
              }
              objToUpdate[nameInFinalObject] = obj[prop];
            }
          }
        }
    
      });
    }
    
    function checkIfNameIdExists(arrOfObj, name, id) {
      if (name === null) {
        return false;
      }
      var output = false;
      arrOfObj.forEach(function(obj) {
        if (obj.name === name) {
          output = obj;
          return true;
        }
      });
    
      return output;
    }
        2
  •  0
  •   Akrion    7 年前

    1. 每个对象中的值都是相同的类型,并且值不是嵌套的,因此需要递归遍历树以比较相等性等。

    2. 第一个数组是源数组,后续数组(使用相同的 名称 )是变异的形式。 我们是 清除 来自源对象的属性。根据OP给出的信息,我们仅考虑价值变化。

    const d1 = [{ name: 'attribute 1', id: 12, value: 40, docs: [], version: 1, }, { name: 'attribute 41', id: 12, value: 6, version: 1, } ]
    const d2 = [{ name: 'attribute 1', attributeTypeId: 12, value: 65, docs: ['bla bla'] }]
    
    const isChanged = (a, b) =>
       Array.isArray(a) ? !a.every(x => b.includes(x)) : a !== b
    
    const compare = (o1, o2) => Object.entries(o1).reduce((r, [k,v]) => {
      if(k in o2 && isChanged(o2[k], v))
         Object.assign(r, {[k]: o2[k]})
      return r
    }, o1)
    
    const group = (a, b) => [...a, ...b].reduce((r,c) => 
       (r[c.name] = [...r[c.name] || [], c], r), {})
    
    const result = Object.values(group(d1,d2)).reduce((r,c) => 
       (r.push(c.length == 2 ? compare(...c) : c[0]), r), [])
    
    console.log(result)

    其思想是将对象合并到一个数组中,按名称对它们进行分组,如果有任何更改 groups 具有 length of 2 compare 功能。否则只会添加到最终结果中。

    推荐文章