Applying Array Superpowers 🦸🏽‍♂️
Tags: functional, javascript, react, beginners
Published: 2020-11-02 23:30:05

# Overview

Previously, we came to understand some basics about `map`, `filter`, and the 'King 👑' Array Method - `reduce`.

This post will just serve as some slightly more practical application where we'll apply our knowledge to an array of student data.

To follow along, you can fork this:

{% replit @CodeFinity/Student-Data-Starter-Data %}

The code shown 👆🏽 is simply to get you an Array of 50 random numbers between 50 and 100. We will use these as a basis to retend that these are some exam scores for a class 🤷🏽‍♂️.

# Transforming our Data into Objects

It doesn't make sense to just have some random numbers. These should be objects with some student ids associated with them.

We'll retend that our ids are just from 1 to 50. So, our data will be transformed to something like: `[{id: 1, score: 55}` all the way down.

Transforming each and every piece of data sounds like...🤔...`map`❗

`const studentScores = examResults.map((examResult, index) => ({studentId: index + 1, examResult}))
`

## Breakdown

`(examResult, index)` shows usage of an optional 2nd parameter, `index` that we can specify in the `map` _callback function._ This _parameter_ represents the _index_ of the current item. With 50 elements, this will start at `0` and end at `49.`

`({studentId: index + 1, examResult})` We are returning an _object literal_ with 2 🔑s, `studentId` and `examResult`. 

`studentId`'s value is nothing but the current `index + 1` - so it will run from 1-50, as we see in the results.

`examResult` is nothing but...the `examResult` 🤷🏽‍♂️. We use _object shorthand_ so that the 🔑 takes on that name and the _value_ is the _value bound to_ `examResult` (which is that first _parameter_ in the _callback function).

Our results look something like this:

```bash
[
  { studentId: 1, examResult: 75 },
  { studentId: 2, examResult: 85 },
  { studentId: 3, examResult: 61 },
```

# Add Letter Grades

Next, we want to add another 🔑, `letterGrade`. This will give us the letter grade on a standard 10 point scale.

For this, let's make a _**pure** library function_ that we can reuse at will:

```javascript
const assignLetterGrade = score => {
  if (score > 90) {
    return "A"
  }
  if (score > 80) {
    return "B"
  }
  if (score > 70) {
    return "C"
  }
  if (score > 60) {
    return "D"
  }
  return "F"
}
```

This function simply takes in a `score` and `return`s the appropriate 'letter grade.' Note 🎵 that there is no need for `else` with the use of 'early' `return`s inside of the `if`s.

```javascript
const studentGrades = studentScores.map(studentScore => {
  // Avoid mutation of the original object data
  const currStudentScore = {...studentScore}
  currStudentScore.letterGrade = assignLetterGrade(currStudentScore.examResult)
  return currStudentScore
})
```

We saw this same sort of technique in our previous post {%link https://dev.to/codefinity/array-superpowers-4feo %}

`currStudentScore.letterGrade = assignLetterGrade(currStudentScore.examResult)`

Here, we are doing the update; that is, adding a new 🔑 and using the `return`ed result from `assignLetterGrade` that we wrote earlier.

# Filter Out Low Scores

Again, let's write a _**pure** library function_ that just takes in any _number_ and some specific 'threshold' number and just `return`s a _boolean_ that let's us know whether it's 'low' or not, based on the 'threshold:' `const isLow = (num, threshold) => num < threshold` 

Now, we'll use `filter` along with this 'library function' to create a list of all of the students that scored under `75`: `const lowGrades = studentGrades.filter(({examResult}) => isLow(examResult, 75))`

Inside of our `filter` _callback,_ we are _destructuring_ the property that we care about, `examResult.`

We send this to our 'library function' to see if the score is less than `75`. If it is, this entire 'student object' will be _returned._ The result if an _array_ of all students that have scored less than `75`.

```bash
[
  { studentId: 1, examResult: 57, letterGrade: 'F' },
  { studentId: 2, examResult: 71, letterGrade: 'C' },
  { studentId: 3, examResult: 74, letterGrade: 'C' },
```

# Get Average Score

To figure out the average score, we will need to get the total after adding up each and every `examResult`, and then divide the the `length` of `studentGrades`, which is of course '50.' 

```javascript
studentGrades.reduce((total, {examResult}) => {
  total += examResult;
  return total
}, 0) / studentGrades.length
```
## Breakdown

`(total, {examResult}` - `reduce` requires two _parameters._ One keeps the '🏃🏽‍♂️ total' (commonly referred to as an 'accumulator). The second _parameter_ is each individual 'student grade record,' from which we are _destructuring_ just the `examResult`.

```javascript
  total += examResult;
  return total
```

Here we are updating `total` and continuing to `return` it as we keep _interating_ over each 'student score.'

Stepping back and taking a look 👀 at `reduce`, we can see that there are 2 _arguments._ The first is the _callback function (that takes 2 parameters as discussed 👆🏽) _and_ the second is the `0`.

```javascript
reduce((total, {examResult}) => {
  total += examResult;
  return total
}, 

// Optional second parameter initializes 'total' to '0'
0)
```

`}, 0` - **⚠️ This part is critical.** This **_parameter_ initializes `total` to be `0`.** W/o this, `total` would be initialized as the _first element in the 'student grades array' - an object. So, we would be 'adding' an _object literal_ and we would get `NaN` 👎🏽.

`/ studentGrades.length` Finally, we are dividing our numerical total by that length, '50,' resulting in the average! 👏🏽

# Tally Up the Grade Distribution

For our final task, we want to know how many "As," "Bs," "Cs," etc. there were. We want our results to look something like this: `{A: 10, B: 12` - an _object literal_ where each 🔑 is one of the letter grades and the _value_ is the 'count' of however many of that grade that there is...

```javascript
const gradeTally = studentGrades.reduce((tally, {letterGrade}) => {
  
  // Does 'tally' already have a 🔑 for the current letter grade?
  if (tally[letterGrade]) {

    // Add 1 to its value
    tally[letterGrade] = tally[letterGrade] + 1
  } else {

    // Initialize it with a value of 1
    tally[letterGrade] = 1
  }

  return tally
}, 

// Initialize 'tally' as an empty object
{})
```

## Breakdown

1. `tally` is initialized as an empty object - `{})`
2. We bring in the first `letterGrade` - `{letterGrade}`
3. Use _bracket notation_ to see if there is any current _value_ inside of `tally` for the current letter grade: `tally[letterGrade]`. Naturally, as `tally` is empty the first time, this will always be _false._
4. Set this 'letter grade 🔑' inside of `tally` with a _value_ of `1` - `tally[letterGrade] = 1`
5. Continue this process by either adding a new 🔑 with a value of `1`, _or_ by _adding_ 1 to the current value.

## Refactor ♻️ With a Ternary

```javascript
const gradeTally = studentGrades.reduce((tally, {letterGrade}) => {  
  tally[letterGrade] = tally[letterGrade] ? tally[letterGrade] += 1 : 1
  return tally
}, {})
```

# Final Code

{% replit @CodeFinity/Student-Data-Starter-Data-1 %}