# Build your JavaScript Muscles with map, reduce, filter and other array iterators

A basic definition of an Array goes like,

> An array is a special variable, which can hold more than one value at a time.

Arrays in JavaScript, are single variables used to store different kind of elements.  One of the primary needs in dealing with an Array is to iterate through and work with the array elements.

There are several ways to iterate through JavaScript Arrays. Language provided loops like, `for`, `forEach`, `for-of`, `for...in`(careful here) are to solve the problems. However the elegance and power both comes when we iterate through JavaScript Arrays in use-case driven fashion.

As part of this story, I will be explaining Six array Iteration use-cases and how to solve them easily with fundamental support provided by JavaScript language itself. We will be seeing the usage of `filter()`, `map()`, `reduce()`, `find()`, `some()` and `every()` in action along with some of them in combinations.

# How to read this
This is going to be bit lengthy. But I am sure, it will be an easy and fun learning experience for you. 
- If you are aware of these concepts already, it is going to be a good brush-up
time for you. Feel free to comment with any additional methods, examples that will be a benefit to others as well.
- If you are getting started with it or haven't used much before, I would suggest you to do some hands-on along with the use-cases explained below. Feel free to read more on internet about each of the methods and frame your own examples as we go.

All the source code used in this story can be  [found on my GITHub repo](https://github.com/atapas/vanilla/blob/master/src/array-iterators.js). Please fork, use and comment. All set? Let's start building the strong muscles!

# Let's Do it with Examples
We will be taking an example of an array of *Customer* objects. Let us assume that, we have bunch of customer data returned from the server. Each of the Customer has properties like, id, first name, last name, gender, married, age, expense and, the list of purchases made.

An array of Customer objects may look like this (for simplicity, we just have 5 customer records!):
```javascript
// Customer object
let customers = [
   {
      'id': 001,
      'f_name': 'Abby',
      'l_name': 'Thomas',
      'gender': 'M',
      'married': true,
      'age': 32,
      'expense': 500,
      'purchased': ['Shampoo', 'Toys', 'Book']
   },
   {
      'id': 002,
      'f_name': 'Jerry',
      'l_name': 'Tom',
      'gender': 'M',
      'married': true,
      'age': 64,
      'expense': 100,
      'purchased': ['Stick', 'Blade']
   },
   {
      'id': 003,
      'f_name': 'Dianna',
      'l_name': 'Cherry',
      'gender': 'F',
      'married': true,
      'age': 22,
      'expense': 1500,
      'purchased': ['Lipstik', 'Nail Polish', 'Bag', 'Book']
   },
   {
      'id': 004,
      'f_name': 'Dev',
      'l_name': 'Currian',
      'gender': 'M',
      'married': true,
      'age': 82,
      'expense': 90,
      'purchased': ['Book']
   },
   {
      'id': 005,
      'f_name': 'Maria',
      'l_name': 'Gomes',
      'gender': 'F',
      'married': false,
      'age': 7,
      'expense': 300,
      'purchased': ['Toys']
   }
];
```

![filter.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1564373733219/0Pqng4yAr.png)
# Use-Case 1: Get 'Senior Citizens' by Filtering out other customers
Let us assume, a Customer is qualified as a  [Senior Citizen](https://dictionary.cambridge.org/dictionary/english/senior-citizen) if his/her age is equal or more than 60.

## Approach - Use Array.prototype.filter() method
The Array `filter()` method takes a callback function which is also called as, *test function*. The `filter()` method **creates a new array** with all elements that **pass** the *test* implemented by the provided callback function.

The callback function takes three arguments, current value, current index and the source array itself. The most used syntax looks like:
```javascript
const newArray = arr.filter((element, index, array) => {
   // Do Something Here...
});
```
### Filtering out non-Senior Citizens
We need a test condition which will define the callback(or test) function for the `filter()` method. The test condition is, `Customer's age >= 60`.  This will filter out all the customers from the array that are not satisfying the test condition.

Here is the code look like:
```javascript
// filter example - Build Customer Data for Senior Citizens
const seniorCustomers = customers.filter((customer) => {
   return (customer.age >= 60)
});
console.log('[filter] Senior Customers = ', seniorCustomers);
```
Output:
```javascript
[filter] Senior Customers =  [
  {
    id: 2,
    f_name: 'Jerry',
    l_name: 'Tom',
    gender: 'M',
    married: true,
    age: 64,
    expense: 100,
    purchased: [ 'Stick', 'Blade' ]
  },
  {
    id: 4,
    f_name: 'Dev',
    l_name: 'Currian',
    gender: 'M',
    married: true,
    age: 82,
    expense: 90,
    purchased: [ 'Book' ]
  }
]
```

![map.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1564373758821/JPjq1gO-n.png)
# Use-Case 2: Transform the customer array with a new attribute
Have you noticed, the customer details in the array do not have a full name or title? In this use-case, we will be transforming the customer array to a new array which will have a title and full name added to it.

## Approach - Use Array.prototype.map() method
The `map()` method creates a new array with the results of a callback function on every element in the calling array. You are free to write any transformation logic in the callback function to create something that suites your use-case.

The callback function takes three arguments, current value, current index and the source array itself. The most used syntax looks like:
```javascript
const newArray = arr.map((currentValue, index, array) => {
    // Do Something Here...
});
```

### Transform to add title and full name
Here we will be using the Array's `map()` method to go over each of the customer value and add a `full_name` property based on certain conditions. At the end, we will get a new customer array where each of the customer object element has `full_name` added to it. 
```javascript
// map example - Build Customer Data with title and full name
const customersWithFullName = customers.map((customer) => {
   let title = '';
   if(customer.gender === 'M') {
      title = 'Mr.';
   } else if(customer.gender === 'F' && customer.married) {
      title = 'Mrs.';
   } else {
      title = 'Miss';
   }
   customer['full_name'] = title 
                           + " " 
                           + customer.f_name 
                           + " " 
                           + customer.l_name;
   return customer;
});
console.log('[map] Customers With Full Name = '
               , customersWithFullName);
```
Output -  One of the Customer element inside the mapped array will look like this:
```javascript
// Notice, full_name property added to it.
 {
    id: 1,
    f_name: 'Abby',
    l_name: 'Thomas',
    gender: 'M',
    married: true,
    age: 32,
    expense: 500,
    purchased: [ 'Shampoo', 'Toys', 'Book' ],
    full_name: 'Mr. Abby Thomas'
  }
```

![reduce.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1564373780520/Fo5W8fmPI.png)
# Use-Case 3: Get the average age of the Customers who purchased 'Book'
If you look into the  [Customer array](https://github.com/atapas/vanilla/blob/ecdf1f7e776feefe07007f055ae8585619a1663f/src/array-iterators.js#L2) once again, you will notice that, our Customers have purchased various things. Now we would like to perform some analytics so, we would like to know the average age of the Customers who have purchased the Item, 'Book'.

## Approach - Use Array.prototype.reduce() method
The `reduce()` method uses a *reducer* function on each element of the array, returning a single output value. 
```javascript
arr.reduce(
   reducer(
      accumulator, 
      currentValue, 
      index, 
      array),
   initialValue);
```
The `reduce()` method takes two arguments:
- A *reducer* function which is also called as callback function to be called on each element of the array.
- An optional *initial value*. This is used as the first argument to the first call of the *reducer* function. If no *initialValue* is provided, the first element in the array will be used and skipped. 
 
 Note: Calling `reduce()` method on an empty array without an *initialValue* will throw a `TypeError`.

As we know about the `reduce()` method now, let us go deep into the *reducer* function. The *reducer* function takes four arguments:
- An accumulator: It accumulates the reducer's return values. It is the accumulated value returned from the last invocation of the *reducer*, or *initialValue*, if supplied.
- Current Value: The current element of the array.
- Current Index: The index of the current element of the array.
- Source Array: Entire source array.

The image below illustrates the behavior of the *reducer* function well:
![reducer.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1564320315738/JNdqxib8d.png)
*"Your reducer function's returned value is assigned to the accumulator, whose value is remembered across each iteration throughout the array and ultimately becomes the final, single resulting value." - from  [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)*

### Average age of 'Book' buyers
This is how we can use the array `reduce()` method to compute the average age of  all the customers who have purchased 'Book'.
```javascript
// reduce example - Get the Average Age of 
// Customers who purchased 'Book'
let count = 0;
const total = customers.reduce(
   (accumulator, customer, currentIndex, array) => {
      if(customer.purchased.includes('Book')) {
         accumulator = accumulator + customer.age;
         count = count + 1;
      }
      return (accumulator);
   }, 
0);
console.log('[reduce] Customer Avg age Purchased Book:'
               , Math.floor(total/count));
```
Output:
```bash
[reduce] Customer Avg age Purchased Book: 45
```
In above example, the *initialValue* is passed as, 0. The *reducer* function(which is an arrow function) does the logic of summing up the age. It also finds the count of the such customers to use it for calculating an average later.

![some.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1564373804935/6YHY1iOfS.png)
# Use-Case 4: Do we have a Young Customer(age less than 10 years)?
Our Customers are span across different age groups. Do we have any customers below 10 years of age? At this point, we are just interested in a 'Yes'/'No' kind of answer.

### Approach - Use Array.prototype.some() method
The `some()` method checks if a specified condition is satisfied for at least one of the elements in the array. The condition is specified by you using a callback function. If the test condition is passed for at least one element of the array, a Boolean `true` is returned, `false` otherwise.

```javascript
 arr.some((element, index, array) => {
  // Do Something Here...
 });
```
 Note: This method returns `false` for any condition put on an empty array.
### Do we have such customers?
```javascript
const hasYoungCustomer = customers.some((customer) => {
   return (customer.age < 10);
});
console.log('[some] Has Young Customer(Age < 10):', hasYoungCustomer);
```
Output:
```bash
[some] Has Young Customer(Age < 10): true
```

![find.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1564373837828/zAt0qo8rU.png)
# Use-Case 5: Who's the Young Customer (age less than 10 years)?
In previous use-case we have seen that, we have a Customer below 10 years age. Let us *find* out, who's he/she?
## Approach - Use Array.prototype.find() method
The `find()` method returns the ***first matching*** element from the array. The match condition to be provided by you as a test/callback function to the `find()` method. If there is no match, `undefined` will be returned.

If there are multiple elements matches the criteria, `find()` method will always return the first match. There is also a `findIndex()` method which works in similar fashion but, returns the index of the first matched element. 
```javascript
arr.find((currentElement, currentIndex, array) => {
  // Do Something Here...
});
```
### Find the below 10 years old Customer
The test(or callback) function should have a logic like, `customer.age < 10`. This is how our `find()` method should look like:
```javascript
const foundYoungCustomer = customers.find((customer) => {
   return (customer.age < 10);
});
console.log('[find] Found Young Customer(Age < 10): ', foundYoungCustomer);
```
Output:
```javascript
[find] Found Young Customer(Age < 10): {
  id: 5,
  f_name: 'Maria',
  l_name: 'Gomes',
  gender: 'F',
  married: false,
  age: 7,
  expense: 300,
  purchased: [ 'Toys' ],
  full_name: 'Miss Maria Gomes'
}
```
![every.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1564373984677/FZLmJrt0o.png)
# Use-Case 6: Do we have a Customer without any purchase?
Among all the customers we have got, how do we check if someone is just for the  [Window Shopping](https://dictionary.cambridge.org/dictionary/english/window-shopping) and haven't really purchased anything!

## Approach - Use Array.prototype.every() method
The `every()` method takes a test(or callback) function as an argument to check on every element to determine if ***all the elements*** in the array *passed* the test. If all passed, returns `true`, else `false`.
```javascript
arr.every((currentElement, currentIndex, array) => {
  // Do Something Here...
});
```
 Note: Unlike `some()`, this method returns `true` for any condition put on an empty array.

### Is there a Window Shopper?
```javascript
const isThereWindowShopper = customers.every((customer) => {
   return (customer.purchased.length === 0);
})
console.log('[every] Is there a window shopper?', isThereWindowShopper);
```
Output:
```bash
[every] Is there a window shopper? false
```
As you would have noticed that, all of our customers have purchased at least one item. Hence the `every()` method helped us correctly to find that, we do not have a *Window Shopper* yet!

%[<HR/>]

# Let us Chain things together
As it happens with any physical exercises, building muscle for just one part of the body may not be that satisfactory. Similarly, if we try using all the methods discussed above in combination, we will be able to solve complex problems easily. Here is an example use-case of how these methods can be *chained together* to achieve a result.

## Get the total amount spent by Married Customers
We will solve this problem in step-by-step.
- Filter the married customers using `filter()` method
 ```javascript
 const marriedCustomers = customers.filter((customer) => {
   return (customer.married);
 });
 ```
- Use the output of the above(married customers) as input to a `map()` method to get an array of expenses done by them.
 ```javascript
 const expenseMapped = marriedCustomers.map((marriedCustomer) => {
   return marriedCustomer.expense;
 });
 ```
- Use the output of the above(expenses of married customers) as input to a `reduce()` method to get total expense.
 ```javascript
 const totalExpenseMarriedCustomer = expenseMapped.reduce(
   (accum, expense) => {
   return accum + expense;
 }, 0);
 console.log('Total Expense of Married Customers in INR: '
 , totalExpenseMarriedCustomer);
 ```
 Output:
 ```bash
  Total Expense of Married Customers in INR:  2190
 ```
As you see, we have chained `filter()` => `map()` => `reduce()` methods in above example. But wait, isn't it too much of code? Yes, but we have a better way. The above chain can be written like this:
```javascript
const total = customers
                     .filter(customer => customer.married)
                     .map(married => married.expense)
                     .reduce((accum,expense) => accum + expense);
 console.log('Orchestrated total expense in INR: ', total);
```

Sweet, isn't it?

# Conclusions
To conclude this story I would like to suggest to try out these cool methods, if not already. There may be a notion of complexity in understanding some of these methods like `reduce()` or `map()`. But when you start embracing them and write example code, you will just start loving it!

# Credits and Resources
- Muscle men in the cover and post image are from  [here](https://www.vectorstock.com/royalty-free-vector/cartoon-characters-of-strong-and-muscular-vector-18549883) 
- Learn about all about JavaScript Array APIs and Methods from  [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) 
- Some  [cool examples](https://itnext.io/15-useful-javascript-examples-of-map-reduce-and-filter-74cbbb5e0a1f) using the Array iterator methods.
- Read about forEach vs map()  [here](https://codeburst.io/javascript-map-vs-foreach-f38111822c0f) 

Please like/share the post if it was useful. I would love to hear from you on your experience in using these methods.
