R Greenstreet
R Greenstreet

Reputation: 740

Compare two arrays of objects and if value for a certain key is missing, add an object with that value to the array - two ways

I have one array that has objects whose keys match another array of objects with the same keys, and the value for the first key is a Month index (0 = January, 1 = February, etc.) and the points may cross a year division (a:10, a:11, a:0, a:1)

However the first array may not have the same number of objects, and the values for the key on one object may not exist in the other object on the same key, and vice-versa.

Assuming both arrays are already ordered correctly, I want to compare those two arrays, and if one array is missing an object with a value for a key that the other one has, I want to add a new object to the first array with the same key and value, in the same position/index within the array that doesn't have it, as it is in the array that does have it.

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 is missing objects with an a value of 10, 11, and 2 that exist in arr2, and arr2 is missing objects with an a value of 3 and 4 that exist in arr1

What I want to end up with is:

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

Now arr1 has new items/objects for a:10, a:11, and a:2 while arr2 has new items for a:3 and a:4, all of which have a b value of 0;

I've tried plotting this out on paper to see logically what I would do physically, but I just can't get my head around it, so a "For Dummies" answer would be really helpful. I thought I had learned enough to be employable, but this is really sticking me up, and it's just for a simple HTML5 canvas line graph. I'm getting data from a DB to compare two lines on the same graph, where there may not be data for one month for one type of data, but there is for the other type of data. And for those spots that don't exist in one or the other, I want to drop the line down to 0 on the Y axis, then back up to the next value. https://github.com/rmgreenstreet/custom-forms/blob/master/public/javascripts/canvasRender.js

Upvotes: 3

Views: 4194

Answers (4)

Tarukami
Tarukami

Reputation: 1160

Having just month indexes is not OK for proper sorting. Just add some information about year and it will be done easily. Without sorting it may look like:

// 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's final working function (having changed the a value to be a mm/yyyy string:

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;
};

Upvotes: 3

pban
pban

Reputation: 104

You should loop through the first array, and check if each key is in the second array. If it's not you should insert an item using arr.splice().

If you know both lists are sorted, ie the keys are in order, then you can also don't need to check the whole array for the new key.

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

After you go through the first array, do the second array.

Upvotes: 0

SpiritOfDragon
SpiritOfDragon

Reputation: 1432

Here is my solution. I am using lodash for helper functions.

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

Working code snippet

// 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>

Upvotes: 0

x1n13y84issmd42
x1n13y84issmd42

Reputation: 970

The main annoyance of this task is checking for presence of items with certain values of a. A naive straightfoward solution would require iterating over arr2 for evey item of arr1 and vice versa, which makes it O(n2), i.e. slow.

An alternative approach suggests employing objects as fast lookup maps. The idea is to turn your array inside out, use as as keys and bs as values.

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

Pardon my inclination for one-liners.

Upvotes: 1

Related Questions