Rey
Rey

Reputation: 1433

Finding Common Values While Looping Over Two Arrays

I have a situation where I need to compare and find common values over two arrays. I am clear on how to do it with one, but not sure how to do it in this case.

My first array looks like this:

[ { kind: 'E',
    path: [ 'short_name' ],
    lhs: 'testing',
    rhs: 'testing1' },
  { kind: 'E',
    path: [ 'agent_name' ],
    lhs: 'testing',
    rhs: 'testing2' } ]

The array above represents information pertaining to what changed on a document.

My second array looks like this:

[ { lhs: 'legacyId', rhs: 'id_number' },
  { lhs: 'name.short', rhs: 'short_name' },
  { lhs: 'name.long', rhs: 'agent_name' },
  { lhs: 'gender', rhs: 'gender' },
  { lhs: 'dob', rhs: 'date_of_birth' } ]

What I need to do is loop through and find common values for "path" in the first array's elements, and the second array's "rhs" value.

So according to my examples here, I should end up with these values being found: short_name and agent_name.

How could I write a loop to do this over the two arrays?

Upvotes: 4

Views: 1015

Answers (4)

Kobe
Kobe

Reputation: 6446

You can reduce the 1st array and use a forEach loop over the second array to see if each of the values are equal, then push the value to the accumulator:

const arr1 = [{ kind: 'E', path: [ 'short_name' ], lhs: 'testing', rhs: 'testing1' }, { kind: 'E', path: [ 'agent_name' ], lhs: 'testing', rhs: 'testing2' }]
const arr2 = [{ lhs: 'legacyId', rhs: 'id_number' }, { lhs: 'name.short', rhs: 'short_name' }, { lhs: 'name.long', rhs: 'agent_name' }, { lhs: 'gender', rhs: 'gender' }, { lhs: 'dob', rhs: 'date_of_birth' }]

const common = arr1.reduce((a, o1) => {
  const match = arr2.find(o2 => o1.path[0] === o2.rhs)
  match && a.push(match.rhs)
  return a
}, [])

console.log(common)

If you truly wanted to, you could write this in one line with a find instead of a second reduce:

const a = [{ kind: 'E', path: [ 'short_name' ], lhs: 'testing', rhs: 'testing1' }, { kind: 'E', path: [ 'agent_name' ], lhs: 'testing', rhs: 'testing2' }]
const b = [{ lhs: 'legacyId', rhs: 'id_number' }, { lhs: 'name.short', rhs: 'short_name' }, { lhs: 'name.long', rhs: 'agent_name' }, { lhs: 'gender', rhs: 'gender' }, { lhs: 'dob', rhs: 'date_of_birth' }]

const common = a.reduce((a, o1) => (a.push(b.find(o2 => o1.path[0] === o2.rhs).rhs), a), [])

console.log(common)

Or, for a more performant solution ;) You could use a set:

const a = [{ kind: 'E', path: [ 'short_name' ], lhs: 'testing', rhs: 'testing1' }, { kind: 'E', path: [ 'agent_name' ], lhs: 'testing', rhs: 'testing2' }]
const b = [{ lhs: 'legacyId', rhs: 'id_number' }, { lhs: 'name.short', rhs: 'short_name' }, { lhs: 'name.long', rhs: 'agent_name' }, { lhs: 'gender', rhs: 'gender' }, { lhs: 'dob', rhs: 'date_of_birth' }]
var commonValues = []
var set = new Set([])

for (let i = 0; i < a.length; i++) { 
  const value = a[i].path[0]
  if (!set.has(value)) set.add(value)
}
for (let i = 0; i < b.length; i++) {
  const val = b[i].rhs
  if (set.has(val)) commonValues.push(val)
}

console.log(commonValues)

Upvotes: 6

Cal Irvine
Cal Irvine

Reputation: 1144

You can do it in O(n^2) time by nesting a loop and searching through the second loop for every value of the first loop, or you can use a hashmap to do it in O(n) time. In JavaScript we use objects for that because their values can be accessed in O(1) time.

Example:

const array1 = [
  { kind: "E", path: ["short_name"], lhs: "testing", rhs: "testing1" },
  { kind: "E", path: ["agent_name"], lhs: "testing", rhs: "testing2" }
];

const array2 = [
  { lhs: "legacyId", rhs: "id_number" },
  { lhs: "name.short", rhs: "short_name" },
  { lhs: "name.long", rhs: "agent_name" },
  { lhs: "gender", rhs: "gender" },
  { lhs: "dob", rhs: "date_of_birth" }
];

const hashMap = {};
const commonValues = [];

for (let i = 0; i < array1.length; i++) {
  const currentValue = array1[i].path[0];
  hashMap[currentValue] = true;
}

for (let i = 0; i < array2.length; i++) {
  const currentValue = array2[i].rhs;
  if (hashMap[currentValue]) commonValues.push(currentValue);
}

//now commonValues contains all of the common between them. You may want to filter out duplicate matches, depends on your use case. 

While it may seem like a lot more code, the time complexity savings are huge as your data gets arbitrarily large by doing it this way. Imagine your data is 100 items long, already an O(n^2) solution would require 10,000 passes to find all of the matches. Conversely with the above solution, it would require 200 passes. These savings in time add up quickly as your data gets larger.

Upvotes: 2

charlietfl
charlietfl

Reputation: 171679

Make a Set out of the rhs values in second array and reduce first array and filter paths by checking against the set

const arr1= [ { kind: 'E',
    path: [ 'short_name' ],
    lhs: 'testing',
    rhs: 'testing1' },
  { kind: 'E',
    path: [ 'agent_name' ],
    lhs: 'testing',
    rhs: 'testing2' } ],
    
    arr2= [ { lhs: 'legacyId', rhs: 'id_number' },
  { lhs: 'name.short', rhs: 'short_name' },
  { lhs: 'name.long', rhs: 'agent_name' },
  { lhs: 'gender', rhs: 'gender' },
  { lhs: 'dob', rhs: 'date_of_birth' } ],
  
  arr2Set = new Set(arr2.map(({rhs}) => rhs)),
  matches = arr1.reduce((a,{path}) =>[...a, ...path.filter(p => arr2Set.has(p))],[]);
  
  
  console.log(matches)

Upvotes: 1

Calvin Nunes
Calvin Nunes

Reputation: 6516

You can use a simply for inside a for...

This is the most simple (maybe not the most performatic) way you can do that, getting the value from first array, then looping the second one. If a value is found, then push it to another array (results) and break the second loop (there's no need to still running if a value already match).

var a = [ { kind: 'E',
    path: [ 'short_name' ],
    lhs: 'testing',
    rhs: 'testing1' },
  { kind: 'E',
    path: [ 'agent_name' ],
    lhs: 'testing',
    rhs: 'testing2' } ]


var b =[ { lhs: 'legacyId', rhs: 'id_number' },
  { lhs: 'name.short', rhs: 'short_name' },
  { lhs: 'name.long', rhs: 'agent_name' },
  { lhs: 'gender', rhs: 'gender' },
  { lhs: 'dob', rhs: 'date_of_birth' } ]
  
var results = [];  
for (var i = 0; i < a.length; i++){
  var path = a[i].path;
  
  for (var j = 0; j < b.length; j++){
    var rhs = b[j].rhs;
    if (rhs == path){
      results.push(b[j].rhs)
      break;
    }
    
  }
}

console.log(results)

Upvotes: 2

Related Questions