Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c0370a6

Browse files
authored
Merge pull request HackYourFuture#17 from remarcmij/master
Added REVIEW Week 7 content
2 parents ddd4771 + 249807e commit c0370a6

File tree

3 files changed

+244
-1
lines changed

3 files changed

+244
-1
lines changed

Week7/REVIEW.md

Lines changed: 244 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,247 @@ This review covers:
66
• Map,
77
• Reduce
88
• Filter
9-
```
9+
```
10+
11+
## Git Workflow
12+
13+
Please refer to [Tutorial for HackYourFuture Git pull requests and collaboration workflow](https://github.com/HackYourFuture/Git/blob/master/Lecture-3.md)
14+
15+
## Map, filter, reduce
16+
17+
The array methods **map()**, **filter()** and **reduce()** are best understood by looking at how they could be implemented if we were to write them ourselves. In the next few sections we will present simplified versions of the native implementations. We have prefixed the method names with `my` to distinguish them from the built-in versions.
18+
19+
Each of the three methods use a `for` loop internally. You will notice that once you start using these methods the need for `for` loops in your own code is greatly reduced (hurray!).
20+
21+
### Array#map\*
22+
23+
The **map** method returns a new array where each element of the subject array is transformed by a user-supplied transformation (= _mapping_) function.
24+
25+
```js
26+
Array.prototype.myMap = function (mapFn) {
27+
const arr = [];
28+
for (let i = 0; i < this.length; i++) {
29+
arr.push(mapFn(this[i], i, this));
30+
}
31+
return arr;
32+
};
33+
```
34+
35+
<small>\* Array#map is a short-hand notation for Array.prototype.map.</small>
36+
37+
Because the **map()** method is called on an array (using dot-notation), the value of `this` refers to that array itself (in this review called the _subject_ array).
38+
39+
Internally, the **map()** method initializes a new, empty array to which it will push transformed elements, one by one, as it iterates through the subject array, calling the `mapFn` function for each individual element. When the loop has been completed, the new array is returned. Note that the subject array itself remains unmodified.
40+
41+
`this[i]` refers to an element of the subject array at loop index 'i' (because `this` is a reference to the subject array).
42+
43+
As you can see, the `mapFn` function is called with three arguments:
44+
45+
1. the current array element to be transformed
46+
2. the index of the element (starting with `0`)
47+
3. the subject array itself
48+
49+
In the example below we will use the Array#map method to create a new array that holds the squares of a subject array of numbers. The mapping function is represented by an ES6 fat arrow function:<br>`num => num * num`
50+
51+
```js
52+
const numbers = [3, 5, 2, 7];
53+
const squares = numbers.map(num => num * num);
54+
console.log(squares); // -> [9, 25, 4, 49]
55+
```
56+
57+
For illustrative purposes we can add a `console.log` statement to our mapping function and see what we get passed as second and third argument:
58+
59+
```js
60+
const numbers = [3, 5, 2, 7];
61+
const mapFn = (num, index, arr) => {
62+
console.log(num, index, arr);
63+
return num * num;
64+
}
65+
const squares = numbers.map(mapFn);
66+
console.log('squares', squares)
67+
```
68+
69+
Output:
70+
71+
```js
72+
3 0 [ 3, 5, 2, 7 ]
73+
5 1 [ 3, 5, 2, 7 ]
74+
2 2 [ 3, 5, 2, 7 ]
75+
7 3 [ 3, 5, 2, 7 ]
76+
squares [ 9, 25, 4, 49 ]
77+
```
78+
79+
For each of the first four lines in the output (from the `console.log` inside the `for` loop) the first number is the value of the current element, the second number is the current loop index value and the array value is the original subject array.
80+
81+
As is usual in JavaScript you do not necessarily have to use all the parameters that were passed to the `mapFn` function. In fact, in many cases you will only need the first argument (the current array element) as we saw in the first example.
82+
83+
### Array#filter
84+
85+
The **filter()** method returns a new array with all elements that pass the test implemented by a user-supplied (predicate\*) function.
86+
87+
```js
88+
Array.prototype.myFilter = function (predicateFn) {
89+
const arr = [];
90+
for (let i = 0; i < this.length; i++) {
91+
if (predicateFn(this[i], i, this)) {
92+
arr.push(this[i]);
93+
}
94+
}
95+
return arr;
96+
};
97+
```
98+
99+
<small>\*A predicate is a function that returns a boolean, whose value depends on its supplied arguments.</small>
100+
101+
This method works in a similar fashion as the **map()** method, but now elements are only pushed to the new array if the predicate function returns `true`.
102+
103+
In the example below the predicate function test whether the current element is even by checking whether its value divided by two has a remainder of zero. The result of this comparison (`true` or `false`) is the return value of the predicate and determines whether the current element gets added to the new array or not.
104+
105+
```js
106+
const numbers= [6, 3 , 10, 1];
107+
const evenNumbers = numbers.filter(num => num % 2 === 0);
108+
console.log(evenNumbers); // -> [6, 10]
109+
```
110+
111+
### Array#reduce
112+
113+
Of the three methods **map**, **filter** and **reduce**, the **reduce** method presents the most difficulty for new learners. The _Mozilla Developer Network_ (MDN) web site gives the following definition:
114+
115+
> The reduce() method applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value\*\*.
116+
117+
<small>\*\*Although reference is made to a 'single value', this single value may well be an array or an object, as you will see later in the examples below.</small>
118+
119+
```js
120+
Array.prototype.myReduce = function (reducerFn, initialValue) {
121+
let accumulator = initialValue;
122+
for (let i = 0; i < this.length; i++) {
123+
accumulator = reducerFn(accumulator, this[i], i, this);
124+
}
125+
return accumulator;
126+
};
127+
```
128+
129+
The key to understanding the **reduce()** method is in the line:
130+
131+
```js
132+
accumulator = reducerFn(accumulator, this[i], i, this);
133+
```
134+
135+
In the case we don't need the current loop index and the subject array in the reducer function (which is oftent the case), we can simplify this to:
136+
137+
```js
138+
accumulator = reducerFn(accumulator, this[i]);
139+
```
140+
141+
From this line we can define the reducer function as a function that takes an accumulator value and the current array element and returns a new accumulator value.
142+
143+
The whole process is visualised in the figure below (the term _bucket_ was used here to represent the accumulator).
144+
145+
![image](assets/reduce-bucket.png)
146+
147+
The **reduce()** method is the most flexible of the map/filter/reduce triplet. In fact, it is possible to rewrite **map()** and **filter** using **reduce()**.
148+
149+
#### Using reduce() to filter
150+
151+
```js
152+
const arr = [6, 3, 10, 1];
153+
const evenNumbers = arr.reduce((acc, elem) => {
154+
if (elem % 2 === 0) {
155+
acc.push(elem);
156+
}
157+
return acc;
158+
}, []);
159+
console.log(evenNumbers); // -> [6, 10]
160+
```
161+
162+
In this example our accumulator is an (initially empty) array. We put elements (in this case integer numbers) in the accumulator only when they are divisible by 2.
163+
164+
#### Using reduce() to map
165+
166+
In this example an array of integer numbers is mapped to an array of their squares.
167+
168+
```js
169+
const arr = [6, 3, 10, 1];
170+
const squares = arr.reduce((acc, elem) => {
171+
acc.push(elem * elem);
172+
return acc;
173+
}, []);
174+
console.log(squares); // -> [36, 9, 100, 1]
175+
```
176+
177+
### Using reduce() to 'group by'
178+
179+
In this example our accumulator is not an array, but an (initially empty) object. It groups the array elements by gender.
180+
181+
```js
182+
const arr = [
183+
{ gender: 'F', name: 'Joyce'},
184+
{ gender: 'M', name: 'Jim' },
185+
{ gender: 'F', name: 'Lucy' },
186+
{ gender: 'M', name: 'Ferdinand' }
187+
];
188+
const groupedNames = arr.reduce((acc, elem) => {
189+
if (acc[elem.gender]) {
190+
acc[elem.gender].push(elem);
191+
} else {
192+
acc[elem.gender] = [elem];
193+
}
194+
return acc;
195+
}, {});
196+
console.log(groupedNames);
197+
```
198+
199+
Result:
200+
201+
```js
202+
{
203+
F: [
204+
{ gender: 'F', name: 'Joyce' },
205+
{ gender: 'F', name: 'Lucy' }
206+
],
207+
M: [
208+
{ gender: 'M', name: 'Jim' },
209+
{ gender: 'M', name: 'Ferdinand' }
210+
]
211+
}
212+
```
213+
214+
### Method chaining
215+
216+
The methods **map()**, **filter()** and **reduce()** each return a new array. This makes it possible to chain these methods and create a 'pipeline' of operations, to be applied in sequence. Let's take the last example, but now filtering out only those array elements for which the name starts with a 'J':
217+
218+
```js
219+
const arr = [
220+
{ gender: 'F', name: 'Joyce' },
221+
{ gender: 'M', name: 'Jim' },
222+
{ gender: 'F', name: 'Lucy' },
223+
{ gender: 'M', name: 'Ferdinand' }
224+
];
225+
const groupedNames = arr
226+
.filter(elem => elem.name.startsWith('J'))
227+
.reduce((acc, elem) => {
228+
if (acc[elem.gender]) {
229+
acc[elem.gender].push(elem);
230+
} else {
231+
acc[elem.gender] = [elem];
232+
}
233+
return acc;
234+
}, {});
235+
console.log(groupedNames);
236+
```
237+
238+
Result:
239+
240+
```js
241+
{
242+
F: [{ gender: 'F', name: 'Joyce' }],
243+
M: [{ gender: 'M', name: 'Jim' }]
244+
}
245+
```
246+
247+
## In summary
248+
249+
![image](assets/map-filter-reduce-emoji.png)
250+
251+
Credit: http://www.globalnerdy.com/2016/06/23/map-filter-and-reduce-explained-using-emoji/
252+
163 KB
Loading

Week7/assets/reduce-bucket.png

38.2 KB
Loading

0 commit comments

Comments
 (0)