Сравнете два масива от обекти и ако стойността за определен ключ липсва, добавете обект с тази стойност към масива - два начина

Имам един масив, който има обекти, чиито ключове съвпадат с друг масив от обекти със същите ключове и стойността за първия ключ е индекс на месеца (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 платнена линейна графика. Получавам данни от DB, за да сравня два реда на една и съща графика, където може да няма данни за един месец за един тип данни, но има за другия тип данни. И за онези петна, които не съществуват в едното или другото, искам да сваля линията надолу до 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 , a:11, a:0, a:1. Ще актуализирам въпроса. - person R Greenstreet; 04.04.2020