Сравните два массива объектов и, если значение для определенного ключа отсутствует, добавьте объект с этим значением в массив - два способа

У меня есть один массив, в котором есть объекты, ключи которых соответствуют другому массиву объектов с теми же ключами, а значением для первого ключа является индекс месяца (0 = январь, 1 = февраль и т. д.), и точки могут пересекать деление года (a:10, a:11, a:0, a:1)

Однако первый массив может иметь разное количество объектов, и значения ключа одного объекта могут не существовать в другом объекте того же ключа, и наоборот.

Предполагая, что оба массива уже упорядочены правильно, я хочу сравнить эти два массива, и если в одном массиве отсутствует объект со значением для ключа, который есть в другом, я хочу добавить новый объект в первый массив с тем же ключ и значение в той же позиции/индексе в массиве, в котором его нет, как и в массиве, в котором он есть.

let arr1 = [{a:0, b:1},{a:1, b:3},{a:3, b:18},{a:4, b:2}]
let arr2 = [{a:10, b:2},{a:11, b:4},{a:0, b:8},{a:1, b:5},{a:2, b:1}]

arr1 отсутствуют объекты со значением a 10, 11 и 2, которые существуют в arr2, а arr2 отсутствуют объекты со значением a 3 и 4, которые существуют в arr1

Что я хочу получить в итоге:

arr1 = [{a:10, b:0},{a:11, b:3},{a:0, b:1},{a:1, b:3},{a:2, b:0},{a:3, b:18},{a:4, b:2}]
arr2 = [{a:10, b:2},{a:11, b:4},{a:0, b:8},{a:1, b:5},{a:2, b:1},{a:3, b:0},{a:4, b:0}]

Теперь у arr1 есть новые элементы/объекты для a:10, a:11 и a:2, а у arr2 есть новые элементы для a:3 и a:4, все из которых имеют b значение 0;

Я пытался изобразить это на бумаге, чтобы логически понять, что я буду делать физически, но я просто не могу понять это, поэтому ответ «Для чайников» был бы действительно полезен. Я думал, что усвоил достаточно, чтобы меня можно было трудоустроить, но это действительно задевает меня, и это только для простого линейного графика холста HTML5. Я получаю данные из БД для сравнения двух линий на одном графике, где может не быть данных за один месяц для одного типа данных, но есть для другого типа данных. И для тех мест, которых нет ни в одном, ни в другом, я хочу опустить линию до 0 по оси Y, а затем вернуться к следующему значению. https://github.com/rmgreenstreet/custom-forms/blob/master/public/javascripts/canvasRender.js


person R Greenstreet    schedule 03.04.2020    source источник
comment
Ваш вопрос не об отсутствующих ключах, а об отсутствующих значениях в массиве. Это полностью отличается от вопроса, на который вы ссылаетесь.   -  person Barmar    schedule 04.04.2020
comment
@Barmar удален   -  person R Greenstreet    schedule 04.04.2020


Ответы (4)


Наличие только месячных индексов не подходит для правильной сортировки. Просто добавьте информацию о годе, и это будет сделано легко. Без сортировки это может выглядеть так:

// loop elements of the arr2 with reduce,
// if there is any element in arr1 with the same value of key 'a', 
// result is the same as on the previous step
// if there is no elements of that kind, add new object {'a': arr2['a'], 'b' : 0} into arr1

function newArray (arr1, arr2) {
  return arr2.reduce((result, obj2) => {
    if (arr1.some(obj1 => obj1['a'] === obj2['a'])) {
      return result;
    }
    return [...result, {['a'] : obj2['a'], 'b':0}];
  }, arr1)
}
// now you can assign the result of newArray() to new variables
const arr1_ = newArray(arr1, arr2)
const arr2_ = newArray(arr2, arr1)

Окончательная рабочая функция OP (изменив значение a на строку mm/yyyy:

function equalize(arr1, arr2) {
    let newArr = arr2.reduce(function (result, obj2) {
        if (arr1.some(obj1 => obj1['a'] === obj2['a'])) {
            return result;
        }
        return [...result, {'a' : obj2['a'], 'b':0}];
    }, arr1);
    newArr.sort(function (a, b) {
        console.log(`a: ${a}`)
        a = a.x.split('/');
        b = b.x.split('/')
        return new Date(a[1], a[0], 1) - new Date(b[1], b[0], 1)
    });
    return newArr;
};
person Tarukami    schedule 03.04.2020
comment
Вы бы рекомендовали для этой сортировки иметь пару ключ/значение для месяца и года отдельно? Или месяц/год вместе в одном ключе/значении? - person R Greenstreet; 04.04.2020

Основное неудобство этой задачи — проверка наличия элементов с определенными значениями a. Наивное прямолинейное решение потребовало бы перебора arr2 для каждого элемента arr1 и наоборот, что делает его O(n2), то есть медленным.

Альтернативный подход предлагает использовать объекты в качестве карт быстрого поиска. Идея состоит в том, чтобы вывернуть ваш массив наизнанку, использовать as в качестве ключей и bs в качестве значений.

let arr1 = [{a:1, b:1},{a:2, b:3},{a:4, b:18},{a:5, b:2}]
let arr2 = [{a:2, b:2},{a:3, b:4},{a:4, b:8},{a:6, b:5},{a:7, b:1}]

// Using objects as lookup maps.
let m1 = {}
let m2 = {}

// Filling the maps.
// The 'a' becomes a key, the 'b' becomes a value.
arr1.forEach(v => m1[v.a] = v.b)
arr2.forEach(v => m2[v.a] = v.b)

// Iterating over the keys of m1, checking if m2 has that key,
// if not - adding it with a value of 0.
for (let f in m1) m2[f] || (m2[f] = 0)

// The same goes for m2 -> m1.
for (let f in m2) m1[f] || (m1[f] = 0)

// At this point both m1 & m2 have all the keys from both arrays without gaps.

let res1 = []
let res2 = []

// Assembling the resulting arrays.
for (let f in m1) res1.push({a: f, b: m1[f]})

for (let f in m2) res2.push({a: f, b: m2[f]})

Простите мою склонность к остротам.

person x1n13y84issmd42    schedule 03.04.2020

Вот мое решение. Я использую lodash для вспомогательных функций.

function combineArrays (a, b) {
  Object.keys(b).forEach(function (k) {
    const key = parseInt(k);
    if (!a[key]) {
      a[key] = b[key];
      a[key].b = 0;
    }
  });

  return _.values(a);
}

Фрагмент рабочего кода

// const arr1 = [{ a: 1, b: 1 }, { a: 2, b: 3 }, { a: 4, b: 18 }, { a: 5, b: 2 }];
// const arr2 = [{ a: 2, b: 2 }, { a: 3, b: 4 }, { a: 4, b: 8 }, { a: 6, b: 5 }, { a: 7, b: 1 }];

let arr1 = [{a:0, b:1},{a:1, b:3},{a:3, b:18},{a:4, b:2}]
let arr2 = [{a:10, b:2},{a:11, b:4},{a:0, b:8},{a:1, b:5},{a:2, b:1}]

const arr1Map = _.keyBy(arr1, 'a');
const arr2Map = _.keyBy(arr2, 'a');

function combineArrays(a1Map, a2Map) {
  const a = _.cloneDeep(a1Map);
  const b = _.cloneDeep(a2Map);

  Object.keys(b).forEach(function(k) {
    const key = parseInt(k);
    if (!a[key]) {
      a[key] = b[key];
      a[key].b = 0;
    }
  });

  return _.values(a);
}

console.log(combineArrays(arr1Map, arr2Map));
console.log(combineArrays(arr2Map, arr1Map));
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

person SpiritOfDragon    schedule 03.04.2020
comment
К сожалению, это не сработает для меня, так как порядок должен быть сохранен. - person R Greenstreet; 04.04.2020

Вы должны перебрать первый массив и проверить, находится ли каждый ключ во втором массиве. Если это не так, вы должны вставить элемент, используя arr .splice().

Если вы знаете, что оба списка отсортированы, т. е. ключи расположены по порядку, то вам также не нужно проверять весь массив на наличие нового ключа.

let j = 0;
for (let i = 0; i < arr1.length; i++) {
  let possiblyMissingKey = arr1[i].a;
  while (arr2[j].a < possiblyMissingKey && j < arr2.length)
    j++;
  if (arr2[j].a != possiblyMissingKey) {
    let itemToInsert = {a:possiblyMissingKey, b:0};
    arr2.splice(j, 0, itemToInsert);
  }
}

После того, как вы пройдете первый массив, сделайте второй массив.

person Paul Banwell    schedule 03.04.2020
comment
Я чувствую, что это действительно близко, но я не упомянул, что значение a — это индекс месяца, где 0 = январь, 1 = февраль и т. д., и возможно, что оно выйдет за границы года, поэтому может перейти на:10 , а:11, а:0, а:1. Я обновлю вопрос. - person R Greenstreet; 04.04.2020