# 数组的函数式编程
# 5.1 数组的函数式方法
本节创建的所有函数称为投影函数(Projecting Function)。把函数应用于一个值并创建一个新值的过程称为投影。
# map 函数
const map = (array, fn) => {
let results = []
for(let value of array) {
results.push(fn(value))
}
return results
}
// 使用
map([1, 2, 3], x => x * 2)
// [2, 4, 6]
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# filter 函数
与 map 函数类似,只需要增加一个断言就可以:
const filter = (array, fn) => {
let results = []
for (let value of array) {
fn(value) ? results.push(value) : undefined
}
return results
}
// 使用
let books = [
{name: 'ruby', rating: 5},
{name: 'vb', rating: 2},
{name: 'php', rating: 6},
{name: 'js', rating: 8}
]
filter(books, book => book.rating > 5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 5.2 连接操作
假设现在我们有下面的一个数据,我们需要找出 rating 大于 5 的书籍的 title:
let books = [
{title: 'js', author: 'newming', year: 2018, rating: 6},
{title: 'php', author: 'lilei', year: 2013, rating: 7},
{title: 'python', author: 'guoyongfeng', year: 2014, rating: 2},
{title: 'java', author: 'ruanyifeng', year: 2012, rating: 4},
{title: 'c++', author: 'tj', year: 2013, rating: 8}
]
1
2
3
4
5
6
7
2
3
4
5
6
7
利用上边的 map 和 filter 可以实现:
var goodBooks = filter(books, book => book.rating > 5)
goodBooks = map(goodBooks, book => ({title: book.title}))
// 连接
var goodBooks = map(filter(books, book => book.rating > 5), book => ({title: book.title}))
1
2
3
4
5
2
3
4
5
现在 books 做了一些更新
var books = [
{
name: 'beginners',
bookDetails: [
{
id: 111,
title: 'js',
author: 'aaa',
rating: [7]
},
{
id: 222,
title: 'c',
author: 'aaa',
rating: [7]
}
]
},
{
name: 'beginners',
bookDetails: [
{
id: 333,
title: 'go',
author: 'aaa',
rating: [9]
},
{
id: 444,
title: 'php',
author: 'aaa',
rating: [4]
}
]
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
仍然是上一个问题,取出 rating 大于 5 的书籍的 title,这个时候因为 bookDetails 为一个数组,map 遍历后返回的数组中包含了另外的数组,通过上面的方法已经不可以了。
map(books, book => book.bookDetails)
// 使用 map 函数处理后,无法直接交给 filter 处理
[
[
{
id: 111,
title: 'js',
author: 'aaa',
rating: [7]
},
{
id: 222,
title: 'c',
author: 'aaa',
rating: [7]
}
],
[
{
id: 333,
title: 'go',
author: 'aaa',
rating: [9]
},
{
id: 444,
title: 'php',
author: 'aaa',
rating: [4]
}
]
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
这时我们需要一个 concatAll 的函数,将所有嵌套的数组连接到一个数组中。
# concatAll 函数
const concatAll = (array, fn) => {
let results = []
for (const value of array) {
results.push.apply(results, value)
}
return results
}
// 使用
concatAll(
map(books, book => book.bookDetails)
)
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
这时我们得到的结果将会这样的:
[
{
id: 111,
title: 'js',
author: 'aaa',
rating: 7
},
{
id: 222,
title: 'c',
author: 'aaa',
rating: 7
},
{
id: 333,
title: 'go',
author: 'aaa',
rating: 9
},
{
id: 444,
title: 'php',
author: 'aaa',
rating: 4
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
现在可以继续使用 filter 函数了
let goodRating = book => book.rating
filter(
concatAll(
map(books, book => book.bookDetails)
)
, goodRating)
1
2
3
4
5
6
2
3
4
5
6
# reduce 函数
reduce 函数的第一个实现:
const reduce = (array, fn) => {
let accumlator = 0
for (const value of array) {
accumlator = fn(accumlator, value)
}
return accumlator
}
1
2
3
4
5
6
7
2
3
4
5
6
7
上面的只支持加法,因为没有接受默认参数。最终实现:
const reduce = (array, fn, initialValue) => {
let accumlator
if (initialValue !== undefined) {
accumlator = initialValue
} else {
accumlator = array[0]
}
if (initialValue === undefined) {
for (let i = 1; i < array.length; i++) {
accumlator = fn(accumlator, array[i])
}
} else {
for (const value of array) {
accumlator = fn(accumlator, value)
}
}
return accumlator
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# zip 函数
zip 函数的任务是合并两个给定的数组。例如要将下面两个数组合并起来:
// 第一个数组,包含了书籍详情
let books = [
{
name: 'beginners',
bookDetails: [
{
id: 111,
title: 'C# 6.0',
author: 'newming',
rating: [6.8]
},
{
id: 112,
title: 'javascript',
author: 'newming',
rating: [8],
reviews: []
}
]
},
{
name: 'pro',
bookDetails: [
{
id: 113,
title: 'python',
author: 'newming',
rating: [8.5]
},
{
id: 114,
title: 'nodejs',
author: 'newming',
rating: [8.8],
reviews: []
}
]
},
]
// 第二个数组包含了 reviews 评价详情
let reviewsDetails = [
{
id: 111,
reviews: [{good: 4, excellent: 12}]
},
{
id: 112,
reviews: []
},
{
id: 113,
reviews: []
},
{
id: 114,
reviews: [{good: 14, excellent: 2}]
},
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
实现将两个数组合并到一个数组中。
# zip 函数的实现:
const zip = (leftArr, rightArr, fn) => {
let index, results = []
for (index = 0; index < Math.min(leftArr.length, rightArr.length); index++) {
results.push(fn(leftArr[index], rightArr[index]))
}
return results
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
使用 zip 实现两个数组相同位置数值相加:
zip([1, 2, 3], [5, 8, 10], (x, y) => x + y)
// [6, 10, 13]
1
2
2
使用 zip 解决上边图书信息合并:
let bookDetails = concatAll(
map(books, book => book.bookDetails)
)
let mergedBookDetails = zip(bookDetails, reviewsDetails, (book, review) => {
if (book.id === review.id) {
let clone = Object.assign({}, book)
clone.ratings = review
return clone
}
return book // 书上没有这句,如果 book 和 review 的顺序不一致,数据可能就都丢了
})
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12