Alan A
Alan A

Reputation: 2551

How does one generate an array-index based intersection, either of array or object type, from other arrays?

I am trying to find the difference between two objects based on their keys, the output should be a re-hashed object of the interestion of the two objects with keys maintained, eg:

a[3] = {'data': true};
a[4] = {'data': true};
a[5] = {'data': true};

b[2] = {'data': true};
b[3] = {'data': true};
b[4] = {'data': true};
b[5] = {'data': true};

Outout should be:

[3] = {'data': true};
[4] = {'data': true};
[5] = {'data': true};

So far I have this:

var intersection = a.filter(x => b.includes(x))

I have created the fiddle below, but the result is an empty array:

https://jsfiddle.net/ejtkx6fo/1/

Upvotes: 0

Views: 244

Answers (2)

pilchard
pilchard

Reputation: 12918

To compare values it can be as simple as a single map() call, though you'll need to account for differing lengths of the two arrays and customize the return value for unmatched values.

const intersection = a.map((x, i) => b[i].data === x.data ? {...x} : undefined);

const a = [],
  b = [];
  
a[3] = {'data': true};
a[4] = {'data': true};
a[5] = {'data': true};

b[2] = {'data': true};
b[3] = {'data': true};
b[4] = {'data': true};
b[5] = {'data': true};

const intersection = a.map((x, i) => b[i].data === x.data ? {...x} : undefined);

console.log(intersection)


To compare keys you need to access the Object.keys() and compare as needed. Here I am using a some() call to check if any keys don't match.

You'll note that if you add a non-matching key (b[5] = {'data': true, 'id': 1};) this will result in an intersection comparing a to b but not the other direction. (All the keys in a are in b but not all the keys of b are in a);

const a = [],
  b = [];
  
a[3] = {'data': true};
a[4] = {'data': true};
a[5] = {'data': true};

b[2] = {'data': true};
b[3] = {'data': true};
b[4] = {'data': true};
b[5] = {'data': true, 'id': 1};

function intersectByAllKeys(arr1, arr2) {
  return arr1.map((x, i) => {
    const arr2Keys = arr2[i] ? Object.keys(arr2[i]) : [];
    return Object.keys(x).some(k => arr2Keys.indexOf(k) < 0) ? undefined : {...x};
  });
}
console.log(intersectByAllKeys(a, b))

console.log(intersectByAllKeys(b, a))


In order to return a true intersection you will need to compare both directions. Since map() and forEach() skip undefined entries it is simplest to use a for() loop here. Also, since the intersection will never be longer than the shorter of the supplied array we will iterate based on the length of the shorter array (here achieved by destructuring the arguments after sorting const [a, b] = [...arguments].sort((a, b) => (a.length - b.length))).

const a = [],
  b = [];
  
a[3] = {'data': true};
a[4] = {'data': true, 'id': 3};
a[5] = {'data': true};

b[2] = {'data': true};
b[3] = {'data': true};
b[4] = {'data': true, 'id': 0};
b[5] = {'data': true, 'id': 1};

function missingKeys(a, b) {
  return a.some(k => b.indexOf(k) < 0);
}

function intersectSymetricallyByAllKeys(arr1, arr2) {
  const [a, b] = [...arguments].sort((a, b) => (a.length - b.length));
  
  const intersection = [];
  for (let i=0; i<a.length; i++){
    if (!a[i] || !b[i]) {
      intersection[i] = undefined;
    } else {
      const aKeys = Object.keys(a[i]) ?? [];
      const bKeys = Object.keys(b[i]) ?? [];
      
      intersection[i] = missingKeys(aKeys,bKeys) || missingKeys(bKeys,aKeys) ? 
        undefined : {...a[i]}
    }
  }
  
  return intersection;
}

console.log(intersectSymetricallyByAllKeys(a, b));
console.log(intersectSymetricallyByAllKeys(b, a));

Upvotes: 1

George Pandurov
George Pandurov

Reputation: 391

I just tweaked a little your code and I think this is what you are looking for:

var incoming = [];
var cookie = [];

incoming[3] = {'data': true};
incoming[4] = {'data': true};
incoming[5] = {'data': true};

cookie[2] = {'data': true};
cookie[3] = {'data': true};
cookie[4] = {'data': true};
cookie[5] = {'data': true};

console.log(typeof(cookie));

var x = Object.keys(incoming);
var y = Object.keys(cookie);

var intersection = x.filter(z => y.includes(z))
var result = {}
for (let i of intersection) {
    result[i] = incoming[i]
}
//var intersection = incoming.filter(x => cookie.some(y => x === y))
//var intersection = Object.keys(cookie).filter({}.hasOwnProperty.bind(incoming));
//var intersection = (pred) => (incoming,cookie) => incoming.filter(x => !cookie.some(y => pred(x, y)))

console.log(incoming);
console.log(cookie);
console.log(intersection);
console.log(result);

Look at the console log of result.

Just a little more information on what happens here: If you want to have the keys and the values you should use an object instead of array. The way you are using the arrays you are also creating indexes [0], [1], and [2] with values undefined and this is how javascript works when you use index for array and there are previous indexes that are missing in the mentioned array.

Upvotes: 1

Related Questions