Sweepster
Sweepster

Reputation: 1949

How do I match initials found in one array to values in another array?

I have the below code which checks to make sure at least one value in array 1 appears in array 2.

As long as it does, it will check if any initials (values of one character in length) exist in array2.

If there are, it will check if that initial matches the first letter of any value in array1.

var array1 = ['Bob', 'Freddy', 'Johnson'];
var array2 = ['Bob', 'Johnson', 'F'];
var isValid = '';

if (array1.some(v => array2.includes(v)) == true) { //At least one value in array 1 appear in array 2
  if (anyInitials(array2) == 'yes') { // initials appear in array 2
    if (findName(array2, array1) == true) { //check initials in array 2 vs array 1
      isValid = 'yes';
    } else { //let's check the inverse, just in case
      isValid = 'no';
    }
  }
}
console.log('Do we match? = ' + isValid);

function findName(arr1, arr2) {
  for (let initial of arr1) {
    if (initial.length === 1) {
      return arr2.findIndex(name => name[0] === initial) != -1
    }
  }
}

function anyInitials(a) {
  var arrayLength = a.length;
  var isInitials = 'no';

  for (var i = 0; i < arrayLength; i++) {
    if (a[i].length == 1) {
      isInitials = 'yes';
    }
  }

  return isInitials;
}

It all works fine but I have problems in more convoluted scenarios such as below:

var array1 = ['Bob','Freddy', 'Johnson', 'Frank'];
var array2 = ['Bob', 'Johnson', 'Frank', 'F'];

How can I ensure that the initial "F" in array2 is being tested against "Freddy" in array1 and not "Frank" (because Frank exists in both arrays, it should be ignored)?

Also,

var array1 = ['Bob','Freddy', 'Johnson', 'Frank', 'Frederic'];
var array2 = ['Bob', 'Johnson', 'F', 'F'];

In the above, the first "F" should test against "Freddy" and the second "F" should test against "Frank" because Freddy was already matched. However, we have a remainder in "Frederic", which does not have a corresponding match (there are no names left in array2) so isValid should say 'no';

A more complex situation:

var array1 = ['Bob','F', 'Freddy', 'Johnson', 'Frank'];
var array2 = ['Bob', 'J', 'F', 'Johnson', 'Freddy'];

"J" should not find a match because "Johnson" already has a pair. One "F" should automatically match with it's pair, while the second "F" should test against the remaining "Frank". This leaves "Frank" to test against "J", which should return "no".

It doesn't matter which name in array 1 the initial in array2 pairs with at this point. What matters is whether or not it can pair at all.

Either array can contain as many or as few values (to a minimum of 1 in each).

Thank you

Addendum: the proposed solution thus far would involve removing all matching names from both arrays, leaving only those values that differ in each. This still leaves me with the problem of matching an initial to a name (a modified findName function). These such pairs would then need to be removed from both arrays, leaving only the remainders in each array (if any). If there are remainders in array1, then isValid would say 'no".

Upvotes: 2

Views: 98

Answers (2)

Kamil Kiełczewski
Kamil Kiełczewski

Reputation: 92367

Try

function check(array1, array2) {
  let i1 = array1.map( x => array2.indexOf(x) );
  let i2 = array2.map( x => array1.indexOf(x) );
  let a1 = array1.filter( (x,i) => !i2.includes(i) );
  let a2 = array2.filter( (x,i) => !i1.includes(i) );  

  let j2 = a2.map( x => a1.reduce( (a,y,j)=> x[0]==y[0] ? j : -1, -1 ) );

  return j2.reduce((a,c)=> a||c>-1, false)
}

The i1 contains indexes (form array2) of array1 elements in array2 (or -1 if element from array1 not appear in array2). The a2 contains elements from array2 excluding elements from array1. Symmetric situation is for i2 and a1. The array j2 element has value >-1 if corresponding a2 element first letter x[0] appear as first letter in some element of a1 (y[0]). We return true if j2 contains at least one non negative value.

function check(array1, array2) {
  let i1 = array1.map( x => array2.indexOf(x) );
  let i2 = array2.map( x => array1.indexOf(x) );
  let a1 = array1.filter( (x,i) => !i2.includes(i) );
  let a2 = array2.filter( (x,i) => !i1.includes(i) );  
  
  let j2 = a2.map( x => a1.reduce( (a,y,j)=> x[0]==y[0] ? j : -1, -1 ) );
  
  return j2.reduce((a,c)=> a||c>-1, false)
}



// TEST

var array1 = ['Bob','Freddy', 'Johnson'];
var array2 = ['Bob', 'Johnson', 'F'];
console.log('test 1', check(array1, array2));

var array1 = ['Bob','Freddy', 'Johnson', 'Frank'];
var array2 = ['Bob', 'Johnson', 'Frank', 'F'];
console.log('test 2', check(array1, array2));

var array1 = ['Bob','Freddy', 'Johnson', 'Frank', 'Frederic'];
var array2 = ['Bob', 'Johnson', 'F', 'F'];
console.log('test 3', check(array1, array2));

var array1 = ['Bob','F', 'Freddy', 'Johnson', 'Frank'];
var array2 = ['Bob', 'J', 'F', 'Johnson', 'Freddy'];
console.log('test 4', check(array1, array2));

Upvotes: 0

epascarello
epascarello

Reputation: 207501

You can not really do it in one step, you are going to have to do it in two.

  • First step is to filter everything that is a one to one match.
  • After that you have to determine if you have a initial or the full name and make matches.

var names1 = ['Bob','Freddy', 'Johnson', 'Frank', 'Frederic'];
var names2 = ['Bob', 'Johnson', 'F', 'F', 'F'];
var names3 = ['Bob', 'Johnson', 'F', 'F', 'G'];
var names4 = ['Bob', 'Johnson', 'F', 'F'];

function test (array1, array2) {

  // If lenghts differ, then it fails
  if (array1.length !== array2.length) {
    return false
  }

  // copy arrays so you do not alter orginals
  var first = array1.slice()
  var second = array2.slice()

  // remove the exact matches
  array1.forEach( function (name, index) {
    var secondIndex = second.indexOf(name)
    if (secondIndex > -1) {
      first.splice(first.indexOf(name), 1)
      second.splice(secondIndex, 1)
    }
  })

  // if all matched, then we pass
  if (!first.length) {
    return true;
  }

  // now check to see if we have a abv match, 
  // every item in the array needs a match to pass
  return first.every(function (text) {
    var index = -1
    // if we have a full name, than match first
    if (text.length > 1) {
      var index = second.indexOf(text[0])
    } else {
      // we have an initial so need to first letter in full name
      var index = second.findIndex( function (secondText) {
        return text === secondText[0]
      })
    }
    // if we do not have a match then we have a failure
    if (index === -1) {
      return false
    } else {
      // when we have a match, remove it from the second so it can not be used again
      second.splice(index, 1)
      return true
    }
  })
}

console.log(1, test(names1, names1))
console.log(2, test(names1, names2))
console.log(3, test(names2, names1))
console.log(4, test(names1, names3))
console.log(5, test(names3, names1))
console.log(6, test(names1, names4))

Upvotes: 1

Related Questions