The Ultimate Guide to JavaScript Algorithms: Combining Arrays Without Duplicates
Publikováno: 19.2.2019
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...
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!✌????