Finding common elements in two arrays and then creating a new array

let arrayOne = [1,2,3,4,5,6,7,8,9];
let arrayTwo = [2,3,5,7,8, 10, 12,13,15];

Hello there,

I was looking for creating a new array, which has common elements in two arrays and then creating a new array, out of two. I stumbled upon this →

The solution given is →

var results = [];

for (var i = 0; i < arr1.length; i++) {
    if (arr2.indexOf(arr1[i]) !== -1) {
        results.push(arr1[i]);
    }
}

But I am unable to understand the logic behind this part:

arr1[i]) !== -1

The code is checking arr2 to find out if a value from arr1 exists in there. The indexOf method gives a value of -1 when what you’re looking for cannot be found.

1 Like

I would have used the filter method instead, letting me replace all of that code with:

var results = arr1.filter(value => arr2.includes(value));
2 Likes

I got it this → arr1[i]) is actually the value in array 1, and

arr2.indexOf(arr1[i]) !== -1) → This is finding index of the value in array 2, and if the value doesn’t exist in any key of array 1 then it will return -1, and the push wont work.

This was slightly tricky one. thanks for sharing knowledge and guiding to the right resource.

2 Likes

Filter function runs a loop?

Yes it does. I’m in the middle right now of putting together a fairly long post on the techniques used to transform your initial code into that simpler code.

1 Like

Oh take care. once Long back you was doing similar thing and accidentally the whole draft got deleted you told us later in some post. Please do save your work this time to avoid accidental loss of hard work.

1 Like

I think that I should explain each of the code improvements that are used to get from your starting code to the filter method.

The initial code

Here’s the code that we’re starting with:

var results = [];
for (var i = 0; i < arr1.length; i++) {
    if (arr2.indexOf(arr1[i]) !== -1) {
        results.push(arr1[i]);
    }
}

Local item variable

The first improvement is using a local variable to access each item in the arr1 array. This might seem similar to using the DRY principle called Don’t Repeat Yourself, but that’s more for when you see a third example of repetition. That is not the motivation here.

Instead the motivation of using a local variable for each item is that it’s a simple improvement that makes it really easy to transform the code into using a forEach method instead.

var results = [];
for (var i = 0; i < arr1.length; i++) {
    var value = arr1[i];
    // if (arr2.indexOf(arr1[i]) !== -1) {
    if (arr2.indexOf(value) !== -1) {
        // results.push(arr1[i]);
        results.push(value);
    }
}

Replace for loop with forEach

Assigning the array item to the value variable helps to set us up for the next improvement, where we use the forEach method instead of a for loop. Typically for loops result in too much focus on the index number, which tends to be the least important part.

Using the forEach method helps to ensure that each item in the array gains that importance instead.

var results = [];
// for (var i = 0; i < arr1.length; i++) {
arr1.forEach(function hasSameValue(value) {
    // var value = arr1[i];
    if (arr2.indexOf(value) !== -1) {
        results.push(value);
    }
// }
});

Use includes method

The includes method tells you if an array contains an item. Using the includes method helps to simplify the code, so that you don’t need to cognitively deal with indexOf -1 values.

var results = [];
arr1.forEach(function hasSameValue(value) {
    // if (arr2.indexOf(value) !== -1) {
    if (arr2.includes(value)) {
        results.push(value);
    }
});

Prevent spooky side-effects

Currently the forEach function reaches out and changes something else, the results array, from inside of the function. Functions that makes changes elsewhere are said to have side-effects, and is typically discouraged as it can be difficult to test, and with some code it results in unexpected behaviour.

This is why instead of using forEach, it’s much more preferred to instead use map, filter, and reduce which all result in new arrays that you can assign to a variable.

Map gives you a 1-for-1 mapping resulting in the same size array.
Filter gives you another array letting you filter out some of those values.
Reduce lets you use an accumulator to achieve slightly more difficult tasks.

In this case we are using filter, as that’s exactly what we need.

// var results = [];
// arr1.forEach(function hasSameValue(value) {
var results = arr1.filter(function hasSameValue(value) {
    if (arr2.includes(value)) {
        // results.push(value);
        return true;
    }
});

Simplify the conditional

Because the if statement is now only returning true when the condition is true, we can simplify that so that instead the condition itself is returned, letting us achieve exactly the same thing with less complexity.

var results = arr1.filter(function hasSameValue(value) {
    // if (arr2.includes(value)) {
    //     return true;
    // }
    return arr2.includes(value);
});

Streamline using arrow-notation

Arrow-notation functions are anonymous functions using a shorter syntax. It also improves how the this context is handled, but we’re not dealing with that here.

What follows is a step-by-step progression of the different things that arrow-notation helps you to simplify.

Remove function name
As arrow-notation functions are typically simple one-liners, they don’t use function names. Either the code is simple enough to understand without the name, or the code is assigned to a separate named variable. In this case the code inside of the function is simple enough that we can easily understand what it does now without the function name.

var results = arr1.filter(function (value) {
    return arr2.includes(value);
});

Remove function keyword
An arrow-notation function removes the function keyword, and instead uses an => arrow placed after the function parameters and before the code in the function.

// var results = arr1.filter(function (value) {
var results = arr1.filter((value) => {
    return arr2.includes(value);
});

Remove parameter parenthesis
When there is only one function parameter, it is customary to remove the enclosing parenthesis.

// var results = arr1.filter((value) => {
var results = arr1.filter(value => {
    return arr2.includes(value);
});

Remove the return keyword
Array notation functions don’t need the return keyword. The last statement is instead automatically returned from the function.

var results = arr1.filter(value => {
    // return arr2.includes(value);
    arr2.includes(value);
});

Remove function braces
When the function code is simple enough to be a single statement, you can remove the enclosing braces too. When doing that the semicolon must also be removed too from the end of the function statement.

// var results = arr1.filter(value => {
var results = arr1.filter(value =>
    // arr2.includes(value);
    arr2.includes(value)
// });
);

Inline the code
And lastly, the code can be inlined onto one line, helping to achieve the simplification that is intended from arrow-notation functions.

var results = arr1.filter(value => arr2.includes(value));

The goal here is not necessarily to achieve one-liners, but instead to simplify the code so that it is as easy as possible to understand what it does.

Named arrow-notation functions
Sometimes when there are several map and filter methods being used, it can be beneficial to move the arrow-notation out to separately named variables. When that happens I prefer to use parenthesis around even single function parameters, as those help to give better visual cues about what you’re looking at.

var hasSameValue = (value) => arr2.includes(value);
var results = arr1.filter(hasSameValue);

The final code

After exploring the above options, the easiest to understand code looks to be the inline code, which is where we end this exploration.

var results = arr1.filter(value => arr2.includes(value));
4 Likes

3 posts were split to a new topic: Advice about OOP in JS

Awesome. I didn’t get it fully last night, but I wake up today(IST), and the first thing I did was to read this.

This part was wonderful. You precisely summarized the mental difficulty in understanding some complex notations.

Thank you so much for not just sharing your knowledge, but putting so much effort and valuable time to write such a lengthy tuts for some one else and simplifying it in such a causal pedagogy that every things becomes so clear.

My JS is improving; First time I understand every bit shared by you.

6 Likes

Thank you so much! That helps to make it all worthwhile.

5 Likes

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.