Merging arrays is a common task you’ll face in most programming languages, especially merging arrays while only adding unique values. In this article, I’ll talk you through a few approaches to this in TypeScript, as well as the unique TypeScript quirks you might face. If you’re trying to solve this problem in React, why not check out this article we have.
Approach 1: My Approach
function merge(arr1: unknown[], arr2: unknown[]) {
const newArr: unknown[] = [...arr1];
for (let i = 0; i < arr2.length; i++) {
const item = arr2[i];
if (newArr.includes(item)) continue;
newArr.push(item);
}
return newArr;
}
Here’s a simple approach to a merge function in TypeScript. The code is simple, we just
- Create a new array with all the contents of array 1
- Loop through all the items in array 2, and if they aren’t in array1, add them to the new array
- Return the new array
And testing this function, we get what we expect!
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [3, 4, 5, 6, 7];
console.log(merge(arr1, arr2)); //[ 1, 2, 3, 4, 5, 6, 7 ]
Approach 2: Comparing By Value
There is an issue with the above approach. We can see this with another example:
const arr3 = [
{
user: 'Abigail',
},
{
user: 'Brian',
},
{
user: 'Carl',
},
];
const arr4 = [
{
user: 'Brian',
},
{
user: 'Carl',
},
{
user: 'Denise',
},
];
We’d expect our array to be Abigail, Brian, Carl, Denise, since they’re the unique items. But here’s what we get:
console.log(merge(arr3, arr4));
/*
[
{ user: 'Abigail' },
{ user: 'Brian' },
{ user: 'Carl' },
{ user: 'Brian' },
{ user: 'Carl' },
{ user: 'Denise' }
]
*/
This is all to do with the fact that some types are compared by value in JavaScript, like numbers, while some types are compared by reference. You can think of this like an address; checking if two places are the same by value would be going to the places, and looking if the buildings are the same. Checking by reference would be looking at the addresses, and seeing if they refer to the same place.
const num1 = 1;
const num2 = 1;
console.log(num1 === num2); //true
const obj1 = { foo: 'bar' };
const obj2 = { foo: 'bar' };
const obj3 = obj2;
console.log(obj1 === obj2); //false, same value but different references
console.log(obj2 === obj3); //true, same reference
In the above example, TypeScript compares the values, so the result is true. Comparing objects, however, even though they’re identical, obj1 and obj2 are not equal, because they’re referring to different objects. If we copy the value of obj2 into obj3 then these are considered the same, since they both refer to obj2.
We can fix this by comparing the objects by value:
function merge<T>(arr1: T[], arr2: T[]) {
return [
...arr1,
...arr2.filter((item1) => {
arr1.some((item2) => isDeepStrictEqual(item1, item2));
}),
];
}
With this code, we:
- Fill a new array with the items in array 1, using the spread operator
- Filter array2 to only the items that can’t be found in array 1
- The key part here is that we’re using node’s built-in deep equality utility function to compare the objects, instead of comparing them using the regular equals operator.
If we test our new function, we get the following:
console.log(merge(arr1, arr2));
//[ 1, 2, 3, 4, 5 ]
console.log(merge(arr3, arr4));
//[ { user: 'Abigail' }, { user: 'Brian' }, { user: 'Carl' } ]
Working exactly as expected!
Which approach you want to use might depend on your use case. If you’re only dealing with primitives like numbers and strings, you can safely go for the first approach and not have to worry. There also might be some cases where you might want to compare objects by reference, and in that case, the first approach would still be useful. If you’re looking to compare objects by value, however, I’d go for the second approach.
Conclusion
Thanks for reading this article on merging arrays in TypeScript. Hopefully, I’ve solved your problem, and you can adapt my code to your use case, with a bit of understanding of my approach. If I have helped you out, if you liked the article, or if you’re having any troubles, feel free to leave a comment below!
💬 Leave a comment