Пересечение пары ключ-значение массива объектов

1

Я хотел бы знать, есть ли способ найти пересечение пары значений ключа в массиве объектов. Скажем, у вас есть массив из трех объектов, все из которых имеют одинаковые ключи:

    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);
  • 0
    так в принципе вы хотите удалить дубликаты?
  • 0
    Попробуйте прочитать этот вопрос - я поработаю над адаптацией, которая ответит на этот вопрос более непосредственно, когда у меня будет время: D
Показать ещё 2 комментария
Теги:
object
intersection

1 ответ

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, каждый из которых union1 одну сторону вычисленного результата -

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

Реализация union1 остается относительно сложным из-за необходимости поддерживать как объекты, так и массивы - последовательность map, filter и reduce помогает поддерживать поток программы

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 же, как и в других 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)

Конечные зависимости -

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

const isArray =
  Array.isArray

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

Убедитесь, что полная программа работает в вашем браузере ниже -

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

unionAll

Выше union принимает только два входа, и в вашем вопросе вы хотите вычислить объединение объектов 2+. Мы реализуем 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 -

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
      ( ...

Если мы хотим поддерживать такие вещи, как проверка равенства функций, RegExps или других объектов, здесь мы сделаем необходимые изменения


рекурсивный diff

В этом связанном Q & A мы вычисляем рекурсивный diff двух объектов

  • 0
    Спасибо за ваш ответ @ user633183! Однако можно ли изменить unionAll, чтобы он принимал массив объектов вместо объектов или массивов? В моей ситуации вход представляет собой динамически генерируемый массив, содержащий объекты.
  • 0
    @ JordanA29 конечно, просто обновите const unionAll = (x = None, ...xs) => до const unionAll = ([ x = None, ...xs ]) =>

Ещё вопросы

Сообщество Overcoder
Наверх
Меню