0

I want to compare one array with another:

array1 = ['billy', 'bob', 'paul'];
array2 = ['billy', 'michael', 'bob'];

I want to detect whether array2 contains a name that isn't found in array1.

array2 can be longer or shorter than array1, in other words, array2 might be missing names or have more names than array1, but array2 cannot have a DIFFERENT name as compared to array1.

So far, I can detect whether array2 is longer than array 1. If it is, it is obviously adding names and is therefore not valid:

if (array1.length < array2.length) {
      console.log('no');
}

but I this isn't as precise as it needs to be (if both arrays have an equal number of values, it returns true even if the individual vales don't correlate).

see the following for example scenarios:

array1 = ['billy', 'bob', 'paul'];
array2 = ['billy', 'b', 'paul']; //should not be valid

array1 = ['billy', 'b', 'paul'];
array2 = ['billy', 'bob', 'paul']; //should not be valid

array1 = ['billy', 'bob', 'paul'];
array2 = ['billy', 'michael', 'paul']; //should not be valid


array1 = ['billy', 'bob', 'paul'];
array2 = ['billy', 'bob', 'paul', 'michael']; //should not be valid


array1 = ['billy', 'bob', 'paul'];
array2 = ['billy', 'bob']; //this is valid


array1 = ['billy', 'bob', 'paul'];
array2 = ['billy']; //this is valid

array1 = ['bob', 'bob', 'billy', 'paul'];
array2 = ['paul', 'bob', 'bob', 'bob']; //this IS NOT valid

array1 = ['bob', 'bob', 'billy', 'paul'];
array2 = ['paul', 'bob', 'bob']; //this is valid

I'm assuming I should be using .every() but I am unsure as to how to implement it when comparing two arrays as all the examples I find test values of one array against a single value.

update: Array 2 cannot have more instances of a specific name than array 1, but it can have fewer.

Sweepster
  • 1,829
  • 4
  • 27
  • 66
  • 2
    Possible duplicate of [How to compare arrays in JavaScript?](https://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript) – Axifive Feb 27 '19 at 00:44
  • Just to make sure I understand—you want to check if `array2` is a *subset* of `array1`, right? That is, `array2` cannot have anything that's not in `array1`? – Ahmed Fasih Feb 27 '19 at 01:17
  • I'm curious about something, is this sample also valid: `array1 = ['billy', 'bob', 'paul'];` and `array2 = ['bob', 'billy'];`. In other words, do you want to check only if every element in `array2` is included in `array1` or the order is also important, i.e., same values on same indexes. – Shidersz Feb 27 '19 at 03:29
  • that is correct ahmed – Sweepster Feb 27 '19 at 21:55
  • @Shidersz the order does not matter so yes, it would be valid – Sweepster Feb 27 '19 at 21:56

4 Answers4

3

This approach uses the function some to stop when at least one name is not in array1 and the sum of the names is not the same between the arrays.

let isValid = (arr, arr2) => {
  let sum = (array, n) => array.reduce((a, an) => a + (an === n), 0);  
  return !arr2.some(n => {
    let sum2 = sum(arr2, n);
    return !arr.some(an => an === n && sum(arr, an) === sum2);
  });
};

console.log(isValid(['billy', 'bob', 'paul'], ['billy', 'b', 'paul'])); //should not be valid
console.log(isValid(['billy', 'b', 'paul'], ['billy', 'bob', 'paul'])); //should not be valid
console.log(isValid(['billy', 'bob', 'paul'],['billy', 'michael', 'paul'])); //should not be valid
console.log(isValid(['billy', 'bob', 'paul'], ['billy', 'bob', 'paul', 'michael'])); //should not be valid
console.log(isValid(['billy', 'bob', 'paul'], ['billy', 'bob'])); //this is valid
console.log(isValid(['billy', 'bob', 'paul'], ['billy'])); //this is valid
console.log(isValid(['bob', 'bob', 'billy', 'paul'], ['paul', 'bob', 'bob', 'bob'])); //this is NOT valid
console.log(isValid(['bob', 'bob', 'billy', 'paul'], ['paul', 'bob', 'bob'])); //this is valid
.as-console-wrapper { max-height: 100% !important; top: 0; }
Ele
  • 33,468
  • 7
  • 37
  • 75
  • its a good start butwhen array2 contains more of the same name than array 1, it should be false. – Sweepster Feb 27 '19 at 22:11
  • Is there any documentation to explain what exactly this code is doing? It works perfectly but I would love to understand it. From what I gather, this is some sort of compressed function, right? Thank you – Sweepster Feb 27 '19 at 23:58
  • 1
    I updated the answer with a minor change because of performance, I realized when I was posting the answer for your recent question. – Ele Feb 28 '19 at 00:57
2

You can use every to check if every object in array2 is in array1:

var array1 = ['billy', 'bob', 'paul'];
var array2 = ['billy', 'michael', 'bob'];

var allFound = array2.every(e => array1
.includes(e));

console.log(allFound); //Should return false because 'michael' is not in array1

This also works the other way:

var array1 = ['billy', 'bob', 'paul'];
var array2 = ['billy', 'michael', 'bob'];

var allFound = array1.every(e => array2.includes(e));

console.log(allFound); //Should return false because 'paul' is not in array2

As suggested in the comments, you can also make array1 (the one which you want to check against - see first example) a Set, which is similar to an array but contains only unique values:

var array1 = ['billy', 'bob', 'paul'];
var array2 = ['billy', 'michael', 'bob'];

var array1Unique = new Set(array1);

var allFound = array2.every(e => array1Unique.has(e));

console.log(allFound); //Should return false because 'michael' is not in array1
Code Maniac
  • 37,143
  • 5
  • 39
  • 60
Jack Bashford
  • 43,180
  • 11
  • 50
  • 79
  • 3
    Can also turn `array1` into a `Set` beforehand if you want to reduce computational complexity – CertainPerformance Feb 27 '19 at 00:44
  • Bad: the inner `includes` does a linear scan for each element, making the whole needlessly quadratic. – Ahmed Fasih Feb 27 '19 at 00:44
  • 1
    @AhmedFasih what do you mean? Would `indexOf` be better? How should I make it less `needlessly quadratic`? – Jack Bashford Feb 27 '19 at 00:45
  • Smells of premature optimization. I mean the comment regarding the quadratic complexity. Just thinking about it i would not even know if anything less than `O(n^2)` can be reached without additional constraints. And it just does not matter for small numbers of items, so optimize when it becomes a problem. – H.B. Feb 27 '19 at 00:51
  • I'm sorry, but I have no idea what big O notation is, and I don't understand what you mean about the `additional constraints`. – Jack Bashford Feb 27 '19 at 00:52
  • It measures the asymptotic complexity of algorithms, `n` commonly being the length of some list; does not matter too much, that's just how you would write "quadratic complexity". What i mean by constraints is e.g. that maybe this problem could be solved more efficiently when the arrays are sorted, so you do not have to scan the entire list or something like that. – H.B. Feb 27 '19 at 00:54
  • In that case, what would you suggest doing @H.B.? How would you scan sorted arrays without `includes`? – Jack Bashford Feb 27 '19 at 00:55
  • 1
    I don't know, that was just about the only thing i could imagine being relevant here. Maybe if the list is hashed that could also help with lookup, but things like that should already be happening under the hood of `includes`, so maybe that already i not quadratic; would have to look at the specification. – H.B. Feb 27 '19 at 00:58
  • Actually, that cannot be the case, i was thinking of `Set` functions, `has` probably has hashing. Arrays always should require to check every item. – H.B. Feb 27 '19 at 01:00
  • So, with this case, `includes` is necessary? – Jack Bashford Feb 27 '19 at 01:00
  • Well, in your last example you define `array1Unique` but do not use it. If you operate on arrays you may need `includes` but on sets you can use `has` which may be faster due to implementation. – H.B. Feb 27 '19 at 01:02
  • OK @H.B., thanks for that quite comprehensive explanation/discussion! I will look into `has` and implement it correctly. – Jack Bashford Feb 27 '19 at 01:03
  • (I tried to see the impact experimentally, [see result here](https://jsfiddle.net/h2mftngs/). You broke the first example, by the way, because the set is not defined there.) – H.B. Feb 27 '19 at 01:16
  • Jack, check out https://stackoverflow.com/q/18023576/500207 and related questions to start learning about linear vs quadratic runtime, etc. An algorithm with quadratic runtime (or quadratic complexity) means, if `array1` and `array2` both double in length, the amount of work quadruples; if they increase ten-fold in length, the amount of work increases by 10*10 = 100. The amount of work a linear algorithm does will stay proportional to the length of `array1` and `array2`. – Ahmed Fasih Feb 27 '19 at 01:28
  • `array2.includes(e)` is linear: as `array2` gets 3x longer, the amount of work is 3x—and you're doing that for each element of `array1`. This is equivalent to a two nested `for`-loops, which is a dead giveaway that it's quadratic. In contrast to `array2.includes(e)`, which is linear, the following: `array1Unique.has(e)` is constant-time, meaning, looking up whether an element is in a set is the same amount of work whether the set has 1 element or 1 million elements. So as `array2` grows, the amount of work stays proportional to it—the whole algorithm is linear. – Ahmed Fasih Feb 27 '19 at 01:34
  • @H.B. is onto something though because life's complicated. As they showed, quadratic code using plain arrays can be faster than linear code using sets, because of the overhead of creating a set, and because for small arrays that fit in CPU cache, the JavaScript runtime can produce super-efficient code, whereas a set is a big fat data structure. I tend to use more efficient algorithms first (i.e., I reach for linear algorithm over a quadratic by default) and only switch to quadratic if profiling shows I'll get a good speedup. Other schools of thought exist, like go with whatever's easier first. – Ahmed Fasih Feb 27 '19 at 01:39
  • On a code interview, though, always implement the most straightforward and clearest approach first, even if it's quadratic (or worse), because you have something that works and can test better implementations against, and the interviewer will be delighted to ask you "what's the runtime complexity of this code?" and let you wax intelligent about how to change it to be more efficient, and the tradeoffs involved and how you should always profile. – Ahmed Fasih Feb 27 '19 at 01:44
  • But what is array1 contains bob, bob, billy, paul and array 2 contains bob, bob, bob, paul. It should return false because array 2 has more instances of bob than array 1. If array 2 has fewer instances, that is valid. – Sweepster Feb 27 '19 at 22:01
1

If you want a simple and easy to understand solution, you don't need to use every. Just use the code below.

The code below uses a .forEach() loop to loop through array2 and, uses another .forEach() loop to find out if the value in array2 is also in array1.

Note: There are many more efficient methods, but this method is easier to understand if you're new to programming and want to understand how this is done.

Edit: As you suggested in the comments, I fixed my code so each value in array2 has only one counterpart in array1. Not every time it matches the value from array2 and array1, it removes the value from array1.

var array1 = ['bob', 'bob', 'billy', 'paul'];
var array2 = ['bob', 'bob', 'bob', 'billy'];
var contains = false;
var output = true;

array2.forEach(e2 => {
  contains = false;
  array1.forEach(e1 => {
    if (e2 == e1) {
      contains = true;
      array1.splice(array1.indexOf(e1), 1);
    }
  })
  
  if (!contains) {
    output  = false;
  }
});

console.log(output);
Aniket G
  • 3,471
  • 1
  • 13
  • 39
  • thats neat, but an issue arises if his name is doubled up: if array 1 is bob, bob, billy, paul then array 2 has bob bob bob, billy, it should be false. – Sweepster Feb 27 '19 at 21:58
  • Oh. So if array 2 has the same name, but more of it (3 bobs) and array 1 has only 2 bobs, it should be false? I can fix that – Aniket G Feb 28 '19 at 00:15
  • @Sweepster right I fixed my answer above. This works better for the test case you suggested – Aniket G Feb 28 '19 at 02:31
0

You can do the task with a simple for loop and .includes()

Example below:

var array1 = ['billy', 'michael', 'paul'];
var array2 = ['billy', 'michael', 'bob'];

var check = true;
for(var i = 0; i < array2.length; i++){
 if(!array1.includes(array2[i])){
  check = false;
  break;
 }
}
console.log(check);
Spangle
  • 762
  • 1
  • 5
  • 14
  • except if I have two names that are the same in one array, then I have an issue because it will always return true even if array 2 has more instances of that same name than array 1. – Sweepster Feb 27 '19 at 21:59