对象数组的键值对交集

时间:2018-10-24 17:05:52

标签: javascript object intersection

我想知道是否有一种方法可以在对象数组中找到键值对的交集。假设您有一个由三个对象组成的数组,这些对象都具有相同的键,如下所示:

    arrayOfObj = [
    {
        "a": 1,
        "b": "stringB"
        "c": {"c1":1,
            "c2": "stringC2"
            }
    },
    {
        "a": 1,
        "b": "stringBdiff"
        "c": {"c1":1,
            "c2": "stringC2"
            }
    },
    {
        "a": 1,
        "b": "stringB"
        "c": {"c1":1,
            "c2": "stringC2"
            }
    }
  ]

我想找到三个对象的公用键值对:

output= [
 {"a":1}, 
 {"c": {"c1":1, 
        "c2":"stringC2"
       }
 }
]

这是我到目前为止所做的,它可以工作,但不适用于嵌套对象。我想知道是否有一种更优雅的方法可以做到这一点,并且也可以对嵌套对象起作用。

    let properties;
    let commonFound = false;
    let notCommonFound = false;
   const commonValues = [];
   let value;
   const initialArray = [{
   "a": 2,
   "b": "stringB",
   "c": {
     "c1": 1,
     "c2": "stringC2"
   }
  },
  {
   "a": 1,
   "b": "stringB",
   "c": {
     "c1": 2,
     "c2": "stringC2"
   }
  },
  {
   "a": 1,
   "b": "stringB",
   "c": {
     "c1": 2,
     "c2": "stringC2"
   }
  }
  
  ];
   
   const commonStorage = [];
   const  reference = initialArray[0];
   properties = Object.keys(reference);
   properties.forEach((property) => {
       for (let i = 0; i < initialArray.length; i++) {
        commonFound = false;
        notCommonFound = false;
          for (let j = 0; j <i ; j++) {        
              if (initialArray[i][property] === initialArray[j][property]) {
                commonFound = true;
                value = initialArray[i][property];
                }
              else {
               notCommonFound = true;
               value = [];
               }         
          }
        }
       if (commonFound && !notCommonFound) {
          commonStorage.push({[property] : value});
       }
   });
     
  console.log(commonStorage);

1 个答案:

答案 0 :(得分:1)

在实现union之前,我们先来看一下我们期望它的行为–

console.log
  ( union
      ( { a: 1, b: 2, d: 4 }
      , { a: 1, c: 3, d: 5 }
      )
      // { a: 1 }

  , union
      ( [ 1, 2, 3, 4, 6, 7 ]
      , [ 1, 2, 3, 5, 6 ]
      )
      // [ 1, 2, 3, <1 empty item>, 6 ]

  , union
      ( [ { a: 1 }, { a: 2 }, { a: 4, b: 5 }, ]
      , [ { a: 1 }, { a: 3 }, { a: 4, b: 6 }, ]
      )
      // [ { a: 1 }, <1 empty item>, { a: 4 } ]

  , union
      ( { a: { b: { c: { d: [ 1, 2 ]    } } } }
      , { a: { b: { c: { d: [ 1, 2, 3 ] } } } }
      )
      // { a: { b: { c: { d: [ 1, 2 ] } } } }
  )

将具有挑战性的问题分解为较小的部分会更加容易。为了实现union,我们计划对merge进行两次union1调用,每个调用占计算结果的一侧

const union = (left = {}, right = {}) =>
  merge
    ( union1 (left, right)
    , union1 (right, left)
    )

实施union1仍然相对复杂,因为需要同时支持对象数组– mapfilter和{{1 }}有助于维持程序的流程

reduce

最后,我们以与the other Q&A相同的方式实现const union1 = (left = {}, right = {}) => Object.entries (left) .map ( ([ k, v ]) => // both values are objects isObject (v) && isObject (right[k]) ? [ k, union (v, right[k]) ] // both values are "equal" : v === right[k] ? [ k, v ] // otherwise : [ k, {} ] ) .filter ( ([ k, v ]) => isObject (v) ? Object.keys (v) .length > 0 : true ) .reduce ( assign , isArray (left) && isArray (right) ? [] : {} )

merge

最终依赖项–

const merge = (left = {}, right = {}) =>
  Object.entries (right)
    .map
      ( ([ k, v ]) =>
          isObject (v) && isObject (left [k])
            ? [ k, merge (left [k], v) ]
            : [ k, v ]
      )
    .reduce (assign, left)

在下面的浏览器中验证完整的程序是否正常工作

const isObject = x =>
  Object (x) === x

const isArray =
  Array.isArray

const assign = (o, [ k, v ]) =>
  (o [k] = v, o)


unionAll

const isObject = x => Object (x) === x const isArray = Array.isArray const assign = (o, [ k, v ]) => (o [k] = v, o) const merge = (left = {}, right = {}) => Object.entries (right) .map ( ([ k, v ]) => isObject (v) && isObject (left [k]) ? [ k, merge (left [k], v) ] : [ k, v ] ) .reduce (assign, left) const union = (left = {}, right = {}) => merge ( union1 (left, right) , union1 (right, left) ) const union1 = (left = {}, right = {}) => Object.entries (left) .map ( ([ k, v ]) => isObject (v) && isObject (right[k]) ? [ k, union (v, right[k]) ] : v === right[k] ? [ k, v ] : [ k, {} ] ) .filter ( ([ k, v ]) => isObject (v) ? Object.keys (v) .length > 0 : true ) .reduce ( assign , isArray (left) && isArray (right) ? [] : {} ) console.log ( union ( { a: 1, b: 2, d: 4 } , { a: 1, c: 3, d: 5 } ) // { a: 1 } , union ( [ 1, 2, 3, 4, 6, 7 ] , [ 1, 2, 3, 5, 6 ] ) // [ 1, 2, 3, <1 empty item>, 6 ] , union ( [ { a: 1 }, { a: 2 }, { a: 4, b: 5 }, ] , [ { a: 1 }, { a: 3 }, { a: 4, b: 6 }, ] ) // [ { a: 1 }, <1 empty item>, { a: 4 } ] , union ( { a: { b: { c: { d: [ 1, 2 ] } } } } , { a: { b: { c: { d: [ 1, 2, 3 ] } } } } ) // { a: { b: { c: { d: [ 1, 2 ] } } } } )上方仅接受两个输入,并且在您的问题中,您要计算2个以上对象的并集。我们按以下方式实现union-

unionAll

在浏览器中验证结果–

const None =
  Symbol ()

const unionAll = (x = None, ...xs) =>
  x === None
    ? {}
    : xs .reduce (union, x)

console.log
  ( unionAll
      ( { a: 1, b: 2, c: { d: 3, e: 4 } }
      , { a: 1, b: 9, c: { d: 3, e: 4 } }
      , { a: 1, b: 2, c: { d: 3, e: 5 } }
      )
      // { a: 1, c: { d: 3 } }

  , unionAll
      ( { a: 1 }
      , { b: 2 }
      , { c: 3 }
      )
      // {}

  , unionAll
      ()
      // {}
  )


备注

您需要考虑–

const isObject = x =>
  Object (x) === x

const isArray =
  Array.isArray

const assign = (o, [ k, v ]) =>
  (o [k] = v, o)

const merge = (left = {}, right = {}) =>
  Object.entries (right)
    .map
      ( ([ k, v ]) =>
          isObject (v) && isObject (left [k])
            ? [ k, merge (left [k], v) ]
            : [ k, v ]
      )
    .reduce (assign, left)

const union = (left = {}, right = {}) =>
  merge
    ( union1 (left, right)
    , union1 (right, left)
    )

const union1 = (left = {}, right = {}) =>
  Object.entries (left)
    .map
      ( ([ k, v ]) =>
          isObject (v) && isObject (right[k])
            ? [ k, union (v, right[k]) ]
            : v === right[k]
              ? [ k, v ]
              : [ k, {} ]
      )
    .filter
      ( ([ k, v ]) =>
          isObject (v)
            ? Object.keys (v) .length > 0
            : true
      )
    .reduce
      ( assign
      , isArray (left) && isArray (right) ? [] : {}
      )

const None =
  Symbol ()

const unionAll = (x = None, ...xs) =>
  x === None
    ? {}
    : xs .reduce (union, x)
    
console.log
  ( unionAll
      ( { a: 1, b: 2, c: { d: 3, e: 4 } }
      , { a: 1, b: 9, c: { d: 3, e: 4 } }
      , { a: 1, b: 2, c: { d: 3, e: 5 } }
      )
      // { a: 1, c: { d: 3 } }

  , unionAll
      ( { a: 1 }
      , { b: 2 }
      , { c: 3 }
      )
      // {}
      
  , unionAll
      ()
      // {}
  )

我们正在union ( { a: someFunc, b: x => x * 2, c: /foo/, d: 1 } , { a: someFunc, b: x => x * 3, c: /foo/, d: 1 } ) // { d: 1 } (actual) // { a: someFunc, c: /foo/, d: 1 } (expected)

中测试是否等于
union1

如果我们想支持诸如检查功能,RegExps或其他对象是否相等的事情,则可以在此处进行必要的修改


递归差异

this related Q&A中,我们计算了两个对象的递归const union1 = (left = {}, right = {}) => Object.entries (left) .map ( ([ k, v ]) => isObject (v) && isObject (right[k]) ? [ k, union (v, right[k]) ] : v === right[k] // <-- equality? ? [ k, v ] : [ k, {} ] ) .filter ( ...