Key value pair intersection of an array of objects

前端 未结 1 852
长发绾君心
长发绾君心 2021-01-24 01:49

I would like to know if there is a way to find the intersection of a key value pair in an array of objects. Let\'s say you have an array of three objects which all have the same

相关标签:
1条回答
  • 2021-01-24 01:57

    Before we implement intersect we'll first look at how we expect it to behave –

    console.log
      ( intersect
          ( { a: 1, b: 2, d: 4 }
          , { a: 1, c: 3, d: 5 }
          )
          // { a: 1 }
    
      , intersect
          ( [ 1, 2, 3, 4, 6, 7 ]
          , [ 1, 2, 3, 5, 6 ]
          )
          // [ 1, 2, 3, <1 empty item>, 6 ]
    
      , intersect
          ( [ { a: 1 }, { a: 2 }, { a: 4, b: 5 }, ]
          , [ { a: 1 }, { a: 3 }, { a: 4, b: 6 }, ]
          )
          // [ { a: 1 }, <1 empty item>, { a: 4 } ]
    
      , intersect
          ( { a: { b: { c: { d: [ 1, 2 ]    } } } }
          , { a: { b: { c: { d: [ 1, 2, 3 ] } } } }
          )
          // { a: { b: { c: { d: [ 1, 2 ] } } } }
      )
    

    Challenging problems like this one are made easier by breaking them down into smaller parts. To implement intersect we will plan to merge two calls to intersect1, each contributing one side of the computed result –

    const intersect = (left = {}, right = {}) =>
      merge
        ( intersect1 (left, right)
        , intersect1 (right, left)
        )
    

    Implementing intersect1 is remains relatively complex due to the need to support both objects and arrays – the sequence of map, filter, and reduce helps maintain a flow of the program

    const intersect1 = (left = {}, right = {}) =>
      Object.entries (left)
        .map
          ( ([ k, v ]) =>
              // both values are objects
              isObject (v) && isObject (right[k])
                ? [ k, intersect (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) ? [] : {}
          )
    

    Lastly we implement merge the same way we did in the other Q&A –

    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)
    

    The final dependencies –

    const isObject = x =>
      Object (x) === x
    
    const isArray =
      Array.isArray
    
    const assign = (o, [ k, v ]) =>
      (o [k] = v, o)
    

    Verify the complete program works in your browser below –

    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 intersect = (left = {}, right = {}) =>
      merge
        ( intersect1 (left, right)
        , intersect1 (right, left)
        )
    
    const intersect1 = (left = {}, right = {}) =>
      Object.entries (left)
        .map
          ( ([ k, v ]) =>
              isObject (v) && isObject (right[k])
                ? [ k, intersect (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
      ( intersect
          ( { a: 1, b: 2, d: 4 }
          , { a: 1, c: 3, d: 5 }
          )
          // { a: 1 }
    
      , intersect
          ( [ 1, 2, 3, 4, 6, 7 ]
          , [ 1, 2, 3, 5, 6 ]
          )
          // [ 1, 2, 3, <1 empty item>, 6 ]
    
      , intersect
          ( [ { a: 1 }, { a: 2 }, { a: 4, b: 5 }, ]
          , [ { a: 1 }, { a: 3 }, { a: 4, b: 6 }, ]
          )
          // [ { a: 1 }, <1 empty item>, { a: 4 } ]
    
      , intersect
          ( { a: { b: { c: { d: [ 1, 2 ]    } } } }
          , { a: { b: { c: { d: [ 1, 2, 3 ] } } } }
          )
          // { a: { b: { c: { d: [ 1, 2 ] } } } }
      )


    intersectAll

    Above intersect only accepts two inputs and in your question you want to compute the intersect of 2+ objects. We implement intersectAll as follows -

    const None =
      Symbol ()
    
    const intersectAll = (x = None, ...xs) =>
      x === None
        ? {}
        : xs .reduce (intersect, x)
    
    console.log
      ( intersectAll
          ( { 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 } }
    
      , intersectAll
          ( { a: 1 }
          , { b: 2 }
          , { c: 3 }
          )
          // {}
    
      , intersectAll
          ()
          // {}
      )
    

    Verify the results in your browser –

    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 intersect = (left = {}, right = {}) =>
      merge
        ( intersect1 (left, right)
        , intersect1 (right, left)
        )
    
    const intersect1 = (left = {}, right = {}) =>
      Object.entries (left)
        .map
          ( ([ k, v ]) =>
              isObject (v) && isObject (right[k])
                ? [ k, intersect (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 intersectAll = (x = None, ...xs) =>
      x === None
        ? {}
        : xs .reduce (intersect, x)
        
    console.log
      ( intersectAll
          ( { 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 } }
    
      , intersectAll
          ( { a: 1 }
          , { b: 2 }
          , { c: 3 }
          )
          // {}
          
      , intersectAll
          ()
          // {}
      )


    remarks

    You'll want to consider some things like –

    intersect
      ( { 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)
    

    We're testing for what's considered equal here in intersect1

    const intersect1 = (left = {}, right = {}) =>
      Object.entries (left)
        .map
          ( ([ k, v ]) =>
              isObject (v) && isObject (right[k])
                ? [ k, intersect (v, right[k]) ]
                : v === right[k] // <-- equality?
                  ? [ k, v ]
                  : [ k, {} ]
          )
        .filter
          ( ...
    

    If we want to support things like checking for equality of Functions, RegExps, or other objects, this is where we would make the necessary modifications


    recursive diff

    In this related Q&A we compute the recursive diff of two objects

    0 讨论(0)
提交回复
热议问题