9 Essential Array Puzzles To Level Up Your JavaScript [JS Foundation]
Tags: codenewbie, javascript, beginners, webdev
Published: 2023-01-07 04:13:16

There are no shortcuts to becoming a great problem solver(which is what a dev is).

Mastery comes at the cost of practice. And since algorithms and data structures form the backbone of every software, practicing them should be on top of your priority queue. As the most used data structure for storing a sequence of values, arrays are as essential as they can be.

Today we'll hone our skills by learning how to:

*Flatten an array
*Remove duplicates from an array
*Remove duplicates from an array of objects
*Sort an array of objects
*Merge 2 arrays
*Return the difference between two arrays
*Check if an array contains duplicate elements
*Return the Intersection of two arrays
*Find the Longest String in an Array

As the old saying goes "practice makes perfect". Let's crack it.



##How to flatten an array:

The easiest way is to use the built-in .flat() method. 
The flat() method takes an optional maximum depth parameter.

For example, given the following array:
```javascript
const arr = [1, 2, [3, 4], [[5, 6], [7, 8]]];
```
You can flatten it like this:
```javascript
const flattened = arr.flat(2); //max depth
console.log(flattened); // [1, 2, 3, 4, 5, 6, 7, 8]
```
The flat() method returns a new array with all sub-arrays concatenated into it recursively up to the specified depth.

You can also create your own recursive function that iterates over the elements of the array and adds them to a new array, one at a time. 

* If an element is an array, the function should call itself with the element as an argument, and then concatenate the resulting array to the new array. 
* If an element is not an array, the function should simply add it to the new array.

Here is an example of how you could write a recursive function to flatten an array:

```javascript
function flatten(arr) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      result = result.concat(flatten(arr[i]));
    } else {
      result.push(arr[i]);
    }
  }
  return result;
}

const arr = [1, [2, [3, [4]]], 5];
console.log(flatten(arr)); // [1, 2, 3, 4, 5]
```

You can make this function more concise by using the .reduce() method and the ternary operator. 

```javascript
function flatten(arr) {
  return arr.reduce((accumulator, currentItem) => 
    Array.isArray(currentItem)
      ? accumulator.concat(flatten(currentItem))
      : accumulator.concat(currentItem), 
  []);
}
```

If you need a refresher on the .reduce() check this guide -> https://dev.to/gustavupp/a-complete-guide-to-javascripts-reduce-function-with-real-world-examples-5h6k

This function has a linear time complexity(which means that it is efficient for large arrays). 
It also has a constant space complexity(which means that it does not use up more memory as the size of the array increases).


##How to remove duplicates from an array:

By far the easiest and more concise way of removing duplicates of an array is by using the Set object. 

The Set object stores only unique values, so you can create a new set from the array and then use the Array.from() method to convert the set back into an array.

This is one way you can do it using the Set object:

```javascript
const arr = [1, 2, 3, 1, 2, 3, 4, 5];

const unique = Array.from(new Set(arr));

console.log(unique); // [1, 2, 3, 4, 5]
```

You can also use our loved .reduce() (what can't you do with the reduce?? lol)

```javascript
const arr = [1, 2, 3, 1, 2, 3, 4, 5]

arr.reduce((accumulator, currentItem) => {
    if (!accumulator.includes(currentItem))
        accumulator.push(currentItem)
    return accumulator
},[])
```

##How to sort an array of objects

To sort an array of objects you have to choose a property from the object to sort the array by.
 
You can then call .sort() and pass it a comparator function. 

This is how the comparator function works:
* It takes two arguments
* If the first should come before the second it returns a negative value. 
* If the first should come after the second it returns a positive value
* It returns 0 if the values are equal.

Here's how you could sort an array of objects by the value of the name property:

```javascript
const arr = [  
  {name: 'Bob', age: 30},  
  {name: 'Alice', age: 25},  
  {name: 'Charlie', age: 35}
];

arr.sort((a, b) => {
  if (a.age < b.age) { //you can sort by any property
    return -1;
  } else if (a.age > b.age) {
    return 1;
  } else {
    return 0;
  }
});

/*
[
  {name: 'Alice', age: 25}, 
  {name: 'Bob', age: 30}, 
  {name: 'Charlie', age: 35}
] */
```

You can also use the ternary operator to make a one-liner version:

```javascript
arr.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
```
Note: Sometimes nested ternaries can be hard to read.

* If sorting by a `string` it's better to use the .localeCompare() method:

```javascript
arr.sort((a, b) => a.name.localeCompare(b.name));
```

Keep in mind that the sort() method modifies the original array, so if you want to preserve the original array, you should make a copy of it first.


## How to merge 2 arrays

To merge two arrays you can use the concat() method. 
The concat() method returns a new array that includes the elements from both arrays.

Here is an example of how you could use concat() to merge two arrays:

```javascript
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

const merged = arr1.concat(arr2);
console.log(merged); // [1, 2, 3, 4, 5, 6]
```

A fancier or rather an ES6 version uses the spread operator (...). 
The spread operator expands the elements of an array into separate arguments.

This is how it would look like

```javascript
const merged = [...arr1, ...arr2];
console.log(merged); // [1, 2, 3, 4, 5, 6]
```

Both of these approaches: 
* Create a new array that includes the elements from both arrays
* Do not modify the original array. 

If modifying the array is not an issue you can simply use .push()  to add the elements of the other array to the end of the first array.

```javascript
arr1.push(...arr2);
console.log(arr1); // [1, 2, 3, 4, 5, 6]
```

## Return the difference between 2 arrays

There are kinds of differences:
* Non-symmetric
* Symmetric

**Non-symmetric** 
* Elements that are in the first array but not in the second

**Symmetric** 
* Elements that are in the first array but not in the second, and elements that are in the second but not in the first

Here is how you can use the filter() method to find the non-symmetric difference between two arrays:

```javascript
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [3, 4, 5, 6, 7];

const difference = arr1.filter(x => !arr2.includes(x));
console.log(difference); // [1, 2]
```

And this is to get the symmetric difference:
```javascript
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [3, 4, 5, 6, 7];

const difference = arr1
    .filter(x => !arr2.includes(x))
    .concat(arr2.filter(x => !arr1.includes(x)));

console.log(difference); // [1, 2, 6, 7]
```

This approach returns a new array and does not modify the original arrays.

Alternatively, you can use the Set object and its .has() instead of .includes()

Non-symmetric difference:

```javascript
const set = new Set(arr2);
const difference = arr1.filter(x => !set.has(x));
console.log(difference); // [1, 2]
```

Symmetric difference:

```javascript
const set = new Set(arr2);
const set1 = new Set(arr1)
const difference = arr1
    .filter(x => !set.has(x))
    .concat(arr2.filter(x => !set1.has(x)));

console.log(difference); // [1, 2, 6, 7]
```

##how to Check if an array contains duplicate elements

The simplest way of doing this is by using the Set object and the size property. 

The Set object stores only unique values, so you can create a set from the array and compare the size of the set to the size of the original array. 

If the size of the set is smaller than the size of the array, it means that the array contains duplicate elements.

Here's how you'd do it:

```javascript
const arr = [1, 2, 3, 4, 5, 5];

const hasDuplicates = new Set(arr).size !== arr.length;
console.log(hasDuplicates); // true
```

You can achieve the same with an object(also called a hash or dictionary) 

As you loop through the array you set the current value as the `key` and `true` as the keys' value. 

If you try to access a `key` and there is already a value there, it means you found a duplicate.

This is a way of doing it:

```javascript
const hasDuplicates = (arr) => {
    let hash = {}
   for(let i = 0; i < arr.length; i++){
       if(!hash[arr[i]]){
           hash[arr[i]] = true
       } else {
           return true;
       }
   }
    return false
}

console.log(hasDuplicates(arr)); // true
```

Both of these approaches create a new object (a set or a hash object) and do not modify the original array. 
They also have a linear time complexity(they are efficient for large arrays).

##How to return the intersection between 2 arrays

The most common way would be by using the filter() and includes().

It would look something like this:

```javascript
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [3, 4, 5, 6, 7];

const intersection = arr1.filter(x => arr2.includes(x));
console.log(intersection); // [3, 4, 5]
```

This approach returns a new array and does not modify the original arrays.

As an alternative, you can use the Set object(again) and the .has() method 

```javascript
const set = new Set(arr2);
const intersection = arr1.filter(x => set.has(x));
console.log(intersection); // [3, 4, 5]
```

Do you see a pattern? Whenever work something out with a plain object you can create a version using the Set Object and vice versa.

##How to find the longest string in an array

To do so, you can use our popular .reduce() method and a function to compare the values. 

The comparator function will compare the length of the current string to the length of the previous longest string, and return the current string if it is longer, or the previous longest string if it is not.

This is how you can do it:

```javascript
const arr = ['short', 'medium', 'long', 'longest'];

const longest = arr.reduce((acc, x) => x.length > acc.length ? x : acc, '');
console.log(longest); // 'longest'
```

This approach creates a new variable (the acc or accumulator) and does not modify the original array. 

The time complexity is linear (meaning that it is efficient for large arrays).

The same can be achieved by using the sort() method and the spread operator (...) to find the longest string in an array more concisely:

```javascript
const longest = [...arr].sort((a, b) => b.length - a.length)[0];
console.log(longest); // 'longest'
```

## Conclusion

In this article we have learned how to:

*Flatten an array
*Remove duplicates from an array
*Remove duplicates from an array of objects
*Sort an array of objects
*Merge 2 arrays
*Return the difference between two arrays
*Check if an array contains duplicate elements
*Return the Intersection of two arrays
*Find the Longest String in an Array

Thanks for reading!

If you like this article:

*Leave a comment below(You can just say hi!)
*Follow me on Twitter [@theguspear](https://twitter.com/theguspear).

Catch you later,

Gus.