Scotch.io's Real-World Vue Book is launching! Get 50% Off

The Ultimate Guide to JavaScript Algorithms

Combining Arrays Without Duplicates

When developing software, we often need to combine data efficiently and without repetition. This often comes up when manipulating listed data with the use Arrays. If you like the easy way out, then you most likely searched on Google and downloaded a library that offers such functionality or copied some code snippet to get the job done(which isn’t a crime though 😉 ).

However, in this article, we will be taking the mostly avoided path of handling matters ourselves. We will explore 3 ways to combine the elements of multiple arrays into one without any elements getting repeated.

Ready? Let the union begin!

You should already have your development environment setup for this course. Open the cloned repository and inside the Beginner folder, navigate to the mergeArrays folder. Our work for this challenge will be done in here. Make sure to follow along in the index-START.js file.

The Challenge

Given two or more arrays, write a function that combines their elements into one array without any repetition. E.g

mergeArrays([1,2,3,3,3], [1,4,5,2]) // should return [1,2,3,4,5]

Algorithmic Thinking

Unlike in the previous challenges we’ve considered, we are to write a function that accepts an infinite number of arguments. The aim here is to come up with a universal solution that’d work for any number of arrays passed into it.

To achieve this, we have to somehow detect the number of arrays provided and extract them individually before merging them into one array without any element repeating itself.

Coding capes on? Let’s do this!

Code Implementation

Since we want to create a solution that can merge any number of arrays, the first step in solving this challenge is to dynamically detect and extract the arguments(arrays) our function receives and merge them into one.

Accessing the Arguments

Functions receive values with which they carry out various operations in the form of arguments that are passed to them on call.

calculateSum(1,2,3) // returns 6 as the sum of all three numbers

In the code snippet above the function calculateSum is called with 1,2 &3 passed in as arguments with which the sum is calculated.

In like manner, our function receives the arrays to be combined as arguments. To access these arguments from within the function, JavaScript exposes an array-like object arguments which contains the values of all the arguments passed to that function. Thus we may access the arguments passed to the function as shown below:

function mergeArrays() {
    let arrays = Array.from(arguments);
    console.log(arrays)
   // some more code here
}

Notice that in order to convert the arguments object to a real array, we directly create an array from it using the Array.from() method.

The **Array.from()** method creates a new **Array** instance from an array-like or iterable object.

ES6 however offers us a cleaner and more concise way to achieve the same result using the rest parameter syntax which allows us to accept an infinite number of arguments as shown below:

function mergeArrays(...arrays) {
    console.log(arrays)
   // some more code here
}

Using the syntax above, we are able to combine all the arguments passed(irrespective of the number) into one array which we name arrays. Our console.log of arrays in both cases above will give the array below when tested with the challenge sample.

arrays = [[1,2,3,3,3], [1,4,5,2]]

Combining the Arrays

The next step in solving this problem is combining the arrays received into one array(still containing duplicates). To do this, we use a forEach loop to iterate through each array and add the elements therein into another array which we call jointArray.

function mergeArrays(...arrays) {
    let jointArray = []
    arrays.forEach(array => {
        jointArray = [...jointArray, ...array]
    });
    //Some more code here
}

For each array, we use the spread operator to spread its elements as well as the elements in the jointArray which is initially empty into a new array. We then reassign this array to jointArray. On complete execution of this statement, jointArray now contains all the elements from all the arrays received(still with duplicates).

Having covered the two steps common to every solution to this problem, we shall now examine the three ways to remove the duplicates from within our jointArray. They are:

  • Using Sets
  • Using .filter()
  • Using .reduce()

Using Sets

A Set in JavaScript is an object used to store a collection of values of any type.

In the solution below we apply this concept in eliminating the duplicates within our joint array.

function mergeArrays(...arrays) {
    let jointArray = []

    arrays.forEach(array => {
        jointArray = [...jointArray, ...array]
    });
    return [...new Set([...jointArray])]
}

Notice that we use the JavaScript spread operator once again to spread out all the elements of the jointArray into the new set. This eliminates all duplicates as sets are designed to only hold unique values.

We then spread the unique elements of the set back into an array which we return as the final result. Pretty simple, right?

Another way to achieve this is by using the Array.from() method to convert the set which is an iterable object to an array as shown below:

function mergeArrays(...arrays) {
    let jointArray = []

    arrays.forEach(array => {
        jointArray = [...jointArray, ...array]
    });
    return Array.from(new Set([...jointArray]))
}

Using .filter()

The **.filter()** method is used to create a new array from another array. The new array contains only elements that satisfy the specified condition. It accepts one main parameter i.e the function specifying the testing condition.

In this approach, we use the .filter() method to loop through the jointArray while testing to see if the index of the current item matches the index of the current iteration.

function mergeArrays(...arrays) {
    let jointArray = []

    arrays.forEach(array => {
        jointArray = [...jointArray, ...array]
    })
    const uniqueArray = jointArray.filter((item,index) => jointArray.indexOf(item) === index)
    return uniqueArray
}

The **indexOf()** method is used to get the first index at which a given element can be found in an array. It returns -1 if the element isn't present.

In the code snippet above, we use this method to retrieve the first index at which the current item can be found within the array and compare it with the current index for that iteration. If these values match, then this is the first time the element occurs within the array. Hence, it passes the test and is added to the uniqueArray. If the values do not match however, it means the value has been encountered before, hence it is filtered out.

At the end, we return the uniqueArray which now holds the result of our operation i.e an array without duplicates.

Let’s consider one more approach using the .reduce() method.

Using .reduce()

The **.reduce()** method is used to execute a given function on every element within an array until a final value is arrived at. It accepts two parameters, the function to be executed and the initial value of the accumulator. The accumulator is a variable used to store the results of each iteration for use in the next.

In this solution, we use the .reduce() method to continuously execute a function that checks if the current item is already in the accumulator array (newArray). Take note that the accumulator is initialized to an empty array at the beginning of the iteration.

If the element is within the array already, we return the array as it is, to be used in the next iteration. If it isn’t, we return a new array which we create from the elements in the accumulator(newArray) and the current item. This new array is used as the accumulator in the next iteration.

function mergeArrays(...arrays) {
    let jointArray = []

    arrays.forEach(array => {
        jointArray = [...jointArray, ...array]
    })
    const uniqueArray = jointArray.reduce((newArray, item) =>{
        if (newArray.includes(item)){
            return newArray
        } else {
            return [...newArray, item]
        }
    }, [])
    return uniqueArray
}

At the end of the reduction process we are left with an array that contains all elements of the merged arrays without duplicates. We store this in uniqueArray which we return at the end.

Wheeew!!!** We made it. it’s testing time**! Testing

Testing Correctness with Jest

To start the tests for this challenge, run the command below:

npm run test mergeArrays
  • Using Sets
  • Using .filter()
  • Using .reduce()

We have passed all tests 👍 .

Testing Performance on JSPerf

Follow this link, to carry out a performance comparison on the three solutions above. The screenshot below shows the result.

The result of our comparison reveals that using the .filter() method is the fastest approach of all three. Next is using a set, which is 33% slower.

Practical Application

A solid understanding of the concepts and techniques highlighted in this article would go a long way in improving how we handle duplicate data in real world applications.

We can also use these techniques in cleaning up large data-set for statistical computations and visual representation.

As in every other case before, this is often encountered in coding interviews as well.

Conclusion

In our consideration of this challenge, we have explored various ways to combine multiple arrays into one without duplicates.

We have also determined from our tests on JSPerf that the best performing solution is using the .filter() method.

Further Reading

To attain a deeper understanding of the highlighted concepts above, you may use the following links:

See you in the next one!✌🏿

Like this article? Follow @worldclassdev on Twitter