JavaScript函数式编程介绍:使用map(),filter()和reduce()进行列表处理

Functional programming in JavaScript is all the rage these days.

如今,JavaScript中的函数式编程风靡一时。

And it should be: The benefits for readability, maintainability, and testability are dramatic.

应该是:可读性,可维护性和可测试性的好处是巨大的。

"Functional programmming" means many things to many people. One of my favorite tools from the FP mindset is programming in a list-processing style. This entails taking arrays—or lists, as I prefer to call them—as your fundamental data structure.

“功能编程”对许多人来说意味着很多事情。 FP思维方式中我最喜欢的工具之一是以列表处理方式进行编程。 这需要将数组(或我更喜欢称其为列表)的数据作为基本数据结构。

Then, your program becomes a series of operations on elements in the list. And there are many contexts in which this is useful: Mapping AJAX results to React components with map. Removing extraneous data with filter. Doing...Well, fancy things with reduce.

然后,您的程序将对列表中的元素进行一系列操作。 在许多情况下这很有用: 使用map AJAX结果映射到React组件 。 用filter删除无关的数据。 做...好吧,用reduce看中事物。

These functions, called "Array Extras", are abstractions over for loops. There is nothing you can do with with these functions that you can't achieve with for, and vice-versa.

这些功能称为“数组额外功能”,是for循环的抽象。 这些功能对for无法实现for ,反之亦然。

There are some finer points as to their usage, though, which are important to keep in mind as you cultivate an increasingly functional mindsetn.

但是,关于它们的用法,还有一些要点,在培养功能日益强大的思维方式时,请记住这些要点。

Today, we'll lay some ground work by taking a look at all three of these functions. We might even meet an extra function or two, just for good measure.

今天,我们将通过研究所有这三个功能来奠定基础。 出于良好的考虑,我们甚至可能会遇到一两个额外的功能。

But, we'll never figure out what's in store if we don't get started. So let's get started with something you already know: The good ol' for loop.

但是,如果我们不入门,我们永远也不会弄清楚店里有什么。 因此,让我们开始使用的东西你已经知道:好醇” for循环。

与for的迭代 ( Iteration with for )

We use for loops to iterate over every item in an array. Usually, we do something to each item along the way.

我们使用for循环来遍历数组中的每个项目。 通常,我们会在此过程中对每个项目进行处理。

One simple example would be capitalizing every string in an array.

一个简单的示例将大写数组中的每个字符串。

const strings = ['arielle', 'are', 'you', 'there'];
const capitalizedStrings = [];

for (let i = 0; i < strings.length; i += 1) {
    const string = strings[i];
    capitalizedStrings.push(string.toUpperCase());
}

console.log(capitalizedStrings);

In this snippet, we start a list of lowercase names. Then, we initialize an empty array, in which we'll store our capitalized strings.

在此代码段中,我们开始列出一个小写名称。 然后,我们初始化一个空数组,在其中存储大写的字符串。

Inside of the for loop, we simply grab the next string on every iteration; capitalize it; and push it into capitalizedStrings. At the end of the loop, capitalizedStrings contains every word in strings, but...You know, capitalized.

for循环内部,我们只需在每次迭代中获取下一个字符串即可; 大写; 并将其推送到capitalizedStrings 。 在循环的最后, capitalizedStrings strings包含strings中的每个单词,但是...大写。

This brings us to our first function: forEach. This is a method on arrays that "automatically" loops through the list for us. In other words, it handles the details of initializing and incrementing a counter for us.

这将我们带到我们的第一个功能: forEach 。 这是对阵列的一种方法,可为我们“自动”遍历列表。 换句话说,它我们处理初始化和递增计数器的细节。

Instead of the above, where we manually index into strings, we can simply call forEach, and receive the next string on every iteration. The updated version would look something like:

代替上面的方法,我们在其中手动索引为strings ,我们可以简单地调用forEach ,并在每次迭代时接收下一个字符串。 更新后的版本如下所示:

const strings = ['arielle', 'are', 'you', 'there'];
const capitalizedStrings = [];

strings.forEach(function (string) {
    capitalizedStrings.push(string.toUpperCase());
})

...This is almost the same as what we started with. But getting rid of that i is already a big win.

...这几乎与我们开始时一样。 但是摆脱这一点i已经是一个巨大的胜利。

This also introduces a major pattern we'll see time and time again. Namely: We prefer to use methods on Array.prototype that abstract away details like initializing and incrementing counters. That way, we can focus on the important logic, rather than the boilerplate.

这也引入了一种主要模式,我们将一次又一次看到。 即:我们更喜欢在Array.prototype上使用抽象化细节的方法,例如初始化和递增计数器。 这样,我们可以专注于重要的逻辑,而不是样板。

But first! A message from our sponsor.

但首先! 来自赞助商的信息。

凯撒,以蛮力闻名 ( Caesar, Known for Brute Force )

In the snippets below, we'll use encrypting and decrypting strings in our examples of map, reduce, and filter.

在下面的代码片段中,我们将在mapreducefilter示例中使用加密和解密字符串。

For example:

例如:

// Thanks to EvanHahn for this: https://gist.github.com/EvanHahn/2587465
const caesar = require('./caesar');

// We can encrypt the string: 'this is my secret message' with `caesar`
// Here, we scramble the message by shifting each letter forward by 2 letters: 'a' becomes 'c'; 's' becomes  'u'; etc.
const encryptedString = caesar('this is my super secret message.', 2);
console.log(encryptedString); // 'vjku ku oa uwrgt ugetgv uvtkpi.'

The idea is that, if I send you a normal message, like 'this is my super secret message', and someone else gets their hands on it, they can read the secret immediately. This obviously sucks if you're sending sensitive information, like passwords, which someone might be listening for.

这个想法是,如果我向您发送一条普通消息,例如'this is my super secret message' ,而其他人得到了帮助,他们可以立即读取该机密。 如果您要发送敏感信息(例如密码),而有人正在听这些信息,这显然很糟糕。

...And, yes. Someone is always listening.

...是的。 有人 总是在听。

Encrypting a string means: "scrambling it to make it hard to read without unscrambling." This way, even if someone is listening, and even if they do intercept your message, it will remain unreadable until they unscramble it. To quote the example above, it's pretty non-obvious what the string 'vjku ku oa uwrgt ugetgv uvtkpi.' is supposed to mean.

加密字符串的含义是:“对其进行加扰使其难以阅读而不加扰动。” 这样,即使有人听,并且即使他们确实拦截了您的消息,消息也将保持不可读状态,直到他们对其进行解密。 引用上面的示例,字符串'vjku ku oa uwrgt ugetgv uvtkpi.' 应该是这个意思。

The Caesar cipher is one of the simplest ways to scramble a string like this. To encrypt with the Caesar cipher, we pick a key, n, between 1 and 24, and replace every letter in the original string with the one n letters further down the alphabet.

凯撒密码是扰乱此类字符串的最简单方法之一。 要使用Caesar密码进行加密,我们选择一个介于1和24之间的密钥 n ,并将原始字符串中的每个字母替换为字母表后面的n个字母。

So, if we choose the key 2, a becomes c; b becomes d; c becomes e; etc.

因此,如果我们选择键2,则a变为cb变成d ; c变成e ; 等等

Substituting letters like this makes the original string pretty unreadable. But, we scrambled by simply shifting letters. So, all you need to do to unscramble is shift them back. If you get a message you know was encrypted with the key 2, all you need to do to decrypt is shift letters back two spaces. So, c becomes a; d becomes b; etc.

像这样替换字母会使原始字符串变得难以阅读。 但是,我们只是通过移动字母来进行加扰。 因此,您要做的所有工作就是将它们移回原位。 如果收到一条消息,您知道该密钥已使用密钥2加密,则解密所需要做的就是将字母向后移两个空格。 因此, c变为ad变成b ; 等等

Unfortunately, this is a pretty useless form of encryption nowadays. It's extremely easy to break. An easy way to decrypt any string encrypted with a Caesar cipher is to just try to decrypt it with every possible key. One of the results will be correct. All you need to do is scan the list of 24 outputs for the one that's English. The others will be just as unreadable as the original string.

不幸的是,如今这是一种非常无用的加密形式。 破解起来非常容易。 解密使用Caesar密码加密的任何字符串的一种简单方法是尝试使用所有可能的密钥对其进行解密。 其中一个结果是正确的。 您需要做的就是扫描24个输出列表中的英文输出。 其他的将与原始字符串一样不可读。

Below, I'll use a function called tryAll, which does just that. Namely: It takes an encrypted string, and returns a list of every possible decryption. One of those results will be the string we want. So, this always breaks the cipher.

在下面,我将使用一个名为tryAll的函数来执行此操作。 即:它接受一个加密的字符串,并返回所有可能解密的列表。 这些结果之一将是我们想要的字符串。 因此,这总是会破坏密码。

Of course, scanning a list of 24 possible decryptions...Kind of sucks. It sort of feels like we should be able to automatically throw away the ones that are obviously wrong.

当然,扫描24种可能的解密列表……很烂。 感觉就像我们应该能够自动丢弃那些明显错误的内容。

In fact, it's easy to do that. In the section on filter, you'll see a function, called isEnglish, which does just this. In particular, it reads a string; counts how many of the most common 1,000 words in English occur in that string; and, if it finds more than 3 of those words in the sentence, it classifies the string as English. If the string contains fewer than 3 words from that list, it throws it out as "junk".

实际上,这样做很容易。 在filter的部分中,您将看到一个名为isEnglish的函数,该函数就是这样做的。 特别是,它读取一个字符串。 计算该字符串中英语中最常见的1,000个单词中有多少个; 并且,如果在句子中找到三个以上的单词,则将该字符串分类为英语。 如果该字符串包含少于3个单词,则将其丢弃为“垃圾”。

There are more sophisticated ways to check if a string is English, of course, but this is fine for now.

当然,还有更复杂的方法来检查字符串是否为英语,但这目前还可以。

The implementations of tryAll and isEnglish aren't important, but if you're curious, you can find them at this gist: tryAll / isEnglish.

tryAllisEnglish的实现并不重要,但是如果您好奇,可以在以下要点找到它们: tryAll / isEnglish

使用地图转换列表 ( Transforming Lists with map )

Refactoring a for loop to use forEach hints at advantages of this style. But there's still room for improvement in the updated version.

重构for循环以使用forEach暗示这种样式的优点。 但是更新版本仍有改进的空间。

In the example above, we're updating capitalizedStrings within the callback to forEach. There's nothing inherently wrong with this. But, it saves a lot of headache to avoid side effects like that whenever possible.

在上面的示例中,我们将回调内的capitalizedStrings更新为forEach 。 这天生没有错。 但是 ,它避免了很多可能的副作用,从而避免了很多麻烦。

If we don't have to update data structures that live in a different scope...We probably shouldn't.

如果我们没有生活在不同的范围内更新数据结构......我们也许不应该。

In this particular case, we wanted to turn every string in strings into its capitalized version. This is a very common use case for a for loop: Take everything in a list; turn it into something else; and collect the results in a new list.

在这种特殊情况下,我们希望把每串strings到它的大写版本。 这是for循环的非常常见的用例:将所有内容放在列表中; 把它变成其他东西; 并将结果收集到新列表中。

Transforming every element in a list into a new one and collecting the results is called mapping. JavaScript has a built-in function for just this use case, called, appropriately enough, map.

将列表中的每个元素转换为新元素并收集结果称为映射 。 JavaScript为此用途提供了一个内置函数,即适当地称为map

We used forEach because it abstracts away the need to manage the iteration variable, i. We don't have to manually index into the array, etc., and so we can focus on the logic that really matters.

之所以使用forEach是因为它抽象出了管理迭代变量i 。 我们不必手动索引到数组等中,因此我们可以专注于真正重要的逻辑。

Similarly, we use map because it abstracts away initializing an empty array, and pushing to it. Just like forEach accepts a callback that does something with each string value, map accepts a callback that does something with each string value.

类似地,我们使用map因为它抽象了初始化一个空数组并将其推送到它的过程。 就像forEach接受对每个字符串值执行某些操作的回调一样, map接受对每个字符串值执行某些操作的回调。

Let's look at a quick demo before a final explanation. In this example, I'm using a function to "encrypt" a list of messages. We could use a for loop, and we could use forEach...But, it's better with map.

在进行最终解释之前,让我们看一下快速演示。 在此示例中,我使用一个函数来“加密”消息列表。 我们可以使用for循环,也可以使用forEach ...但是,最好使用map

const caesar = require('../caesar');

const key = 12;
const messages = [
    'arielle, are you there?',
    'the ghost has killed the shell',
    'the liziorati attack at dawn'
]

const encryptedMessages = messages.map(function (string) {
    return caesar(string, key);
})

console.log(encryptedMessages);

Note what happened here.

注意这里发生了什么。

  • We start with a list of messages, in plain English.

    我们从简单的英文messages列表开始。
  • We use the map method on messages to encrypt each string with the caesar function, and automagically store the result in a new array.

    我们对messages使用map方法,通过caesar函数对每个字符串进行加密,然后自动将结果存储在新数组中。

After the above code runs, encryptedMessages looks like: ['mduqxxq, mdq kag ftqdq?', 'ftq staef tme wuxxqp ftq etqxx', 'ftq xuluadmfu mffmow mf pmiz'].

上面的代码运行之后, encryptedMessages看起来像: ['mduqxxq, mdq kag ftqdq?', 'ftq staef tme wuxxqp ftq etqxx', 'ftq xuluadmfu mffmow mf pmiz']

This is a much higher level of abstraction than manually pushing to a list.

这比手动推送到列表要高得多。

Now is a good time to point out that we can use arrow functions to express this sort of thing more concisely:

现在是指出我们可以使用箭头函数更简洁地表达这种事情的好时机:

const caesar = require('../caesar');

const key = 12;
const messages = [
    'arielle, are you there?',
    'the ghost has killed the shell',
    'the liziorati attack at dawn'
];

const encryptedMessages = messages.map(string => string.toUpperCase()):

console.log(encryptedMessages);

Even better.

更好

This is generally how I prefer to write, but I'll stick with function syntax for the sake of clarity.

通常,这是我更喜欢写的方式,但是为了清楚起见,我将坚持使用function语法。

用过滤器扔东西 ( Throwing Things Away with filter )

Another common pattern is using a for loop to process items in a list, but only pushing/preserving some of them.

另一个常见的模式是使用for循环来处理列表中的项目,但仅推送/保留其中的一些项目。

We usually decide which items to keep, and which to throw away, by doing an if check.

我们通常通过进行if检查来决定保留哪些物品,丢弃哪些物品。

In raw JS, this might look like:

在原始JS中,这可能类似于:

// Secret message! This was a string encrypted with a key between 1 and 24.
const encryptedMessage = 'mduqxxq, mdq kag ftqdq?'];

// We can break this code by just decrypting with _every_ possible key.
const possibilities = tryAll(encryptedMessage);

// Most of these decryption attempts aren't readable. Sad.
// We can speed up finding the ones that are probably junk with an if check
const likelyPossibilities = [];
possibilities.forEach(function (decryptionAttempt) {
    // Keep the string if it looks like an English sentence
    if (isEnglish(decryptionAttempt)) {
        likelyPossibilities.push(decryptionAttempt);
    }
})

First, we try to decrypt the encrypted message with every possible key. That means we end up with an array of 24 possibilities.

首先,我们尝试使用所有可能的密钥解密加密的消息。 这意味着我们最终有24种可能性。

In this loop, we test each one to see if it looks like an English string. If so, we keep it. If not, we throw it away.

在此循环中,我们测试每个人是否看起来像英语字符串。 如果是这样,我们保留它。 如果没有,我们将其丢弃。

This is clearly a common use case. So, there's a built-in for it, aptly named filter.

显然,这是一个常见的用例。 因此,有一个内置的名称,恰当地称为filter

As with map, we pass filter a callback, which also gets each string. The difference is that, filter will only save items in an array if the callback returns true. So, we could express the above as:

map ,我们通过filter回调,该回调也获取每个字符串。 区别在于,仅当回调返回true时, filter才会将项目保存在数组中。 因此,我们可以将以上内容表达为:

// Secret message! This was a string encrypted with a key between 1 and 24.
const encryptedMessage = 'mduqxxq, mdq kag ftqdq?'];

// We can break this code by just decrypting with _every_ possible key.
const possibilities = tryAll(encryptedMessage);

// Most of these decryption attempts aren't readable. Sad.
// We can speed up finding the ones that are probably junk with an if check
const likelyPossibilities = possibilities.filter(function (string) {
    // If isEnglish(string) returns true, filter saves in the output array
    return isEnglish(string);
})

Since this callback just calls isEnglish, we could actually write it even more concisely, like this:

由于此回调仅调用isEnglish ,因此实际上我们可以更简洁地编写它,如下所示:

// Secret message! This was a string encrypted with a key between 1 and 24.
const encryptedMessage = 'mduqxxq, mdq kag ftqdq?'];

// We can break this code by just decrypting with _every_ possible key.
const possibilities = tryAll(encryptedMessage);

// Most of these decryption attempts aren't readable. Sad.
// We can speed up finding the ones that are probably junk with an if check
const likelyPossibilities = possibilities.filter(isEnglish);

...Bang.

...砰。

减少负担 ( Bringing Things Together with reduce )

So far, we've seen abstractions for three common use cases around arrays:

到目前为止,我们已经看到了数组周围三个常见用例的抽象:

  • forEach, which allows us to iterate over a list as with for, but eliminates the need to manually manage the iteration variable i; index into the list; etc.

    forEach ,它允许我们像for一样遍历一个列表,但是不需要手动管理迭代变量i ; 索引到列表; 等等
  • map, which lets us transform each element of a list, and collect the results in an array

    map ,它使我们可以转换列表的每个元素,并将结果收集到数组中
  • filter, which lets us choose which elements of a list to keep

    filter ,它使我们可以选择保留列表中的哪些元素

Another common use case is: Iterating over a list to collect its elements into a single result.

另一个常见的用例是:遍历列表以其元素收集到单个结果中。

The prototypical example is adding up a bunch of numbers.

典型的例子是将一堆数字相加。

// prices of: (big) box of oreos, girl scout cookies, fidget spinner, gold-plated Arch linux magnet
const prices = [12, 19, 7, 209];

// variable to store our total prices in
let totalPrice = 0;
for (let i = 0; i < prices.length; i += 1) {
    totalPrice += prices[i];
}

// Report our total prices: $247
console.log(`Your total is ${totalPrice}.`);

As I'm sure you'd guess, there's an abstraction for this, too: reduce.

就像您确定的那样,我也对此有一个抽象: reduce

Refactoring the above loop with reduce looks like:

reduce重构上述循环看起来像:

const prices = [12, 19, 7, 209];

prices.reduce(function (totalPrice, nextPrice) {
    // totalPrice is the price so far
    console.log(`Total price so far: ${totalPrice}`)
    console.log(`Next price to add: ${nextPrice}`)

    // update totalPrice by adding the next price
    totalPrice += nextPrice

    // return the total price, and start over again
    return totalPrice
}, 0)
// ^ the second argument to `reduce` is the totalPrice to start with on the first iteration

Like map and filter, reduce accepts a callback, which it runs on each element of the array.

mapfilter一样, reduce接受一个回调,该回调在数组的每个元素上运行。

Unlike map and filter, the callback we pass to reduce accepts two arguments: a totalPrice, and a nextPrice.

mapfilter不同,我们传递给reduce的回调接受两个参数: totalPricenextPrice

totalPrice is like total in the first example: It's the price we've gotten by adding up all the prices we've seen so far.

totalPrice就像第一个示例中的total :这是我们通过将到目前为止所看到的所有价格相加得到的价格。

nextPrice is the number we got by doing prices[i] in the first example. Recall that map and reduce automatically index into the array for us, and pass this value to their callbacks automatically. reduce does the same thing, but passes that value as the second argument to its callback.

nextPrice是第一个示例中通过prices[i]获得的数字。 回忆一下该map并为我们自动reduce到数组的索引,然后将此值自动传递给其回调。 reduce做同样的事情,但是将该值作为第二个参数传递给其回调。

Just like with map and reduce, on each iteration, we have to return the value we're interested in getting back at the end of the loop. Namely, totalPrice.

就像mapreduce ,在每次迭代中,我们都必须在循环结束时返回我们想要返回的值。 即totalPrice

Finally, note that we pass another argument after the callback to reduce. In this case, we pass 0. This is analogous to the line in the first example where we set const total = 0. That second argument is the initial value of totalPrice.

最后,请注意,我们在回调后传递另一个参数reduce 。 在这种情况下,我们传递0 。 这类似于我们将const total = 0设置为第一个示例中的行。 第二个参数是totalPrice的初始值。

reduce is harder to grok than map or filter, so let's take a look at another example.

mapfilterreduce更难掌握,因此让我们来看另一个示例。

In the previous example, we used reduce to collect an array of numbers into a sum. But reduce is versatile. We can use it to turn a list into a any single result.

在前面的示例中,我们使用reduce来将一个数字数组收集为一个和。 但是reduce是通用的。 我们可以使用它将列表转换为任何单个结果。

For example, we can use it to build up a string.

例如,我们可以使用它来构建一个字符串。

const courses = ['Introduction to Programming', 'Algorithms & Data Structures', 'Discrete Math'];

const curriculum = courses.reduce(function (courseList, course) {
    // add the name to the class, with a preceding newline and tab for indentation
    return courseList += `\n\t${course}`;
}, 'The Computer Science curriculum consists of:');

console.log(curriculum);

This generates the output:

生成输出:

The Computer Science curriculum consistsof:
    Introduction to Programming
    Algorithms & Data Structures
    Discrete Math

I don't see a ton of examples of using reduce to build strings like this, for some reason, but it's common practice in a functional style.

由于某种原因,我没有看到很多使用reduce来构建这样的字符串的示例,但这是功能样式的常见做法。

Again, reduce is versatile: We can use it to turn a list into any kind of "single" result.

同样, reduce是通用的:我们可以使用它将列表转换为任何类型的“单个”结果。

Even if that single result is another array.

即使单个结果是另一个数组。

Check it:

核实:

const names = ['arielle', 'jung', 'scheherazade'];

const titleCase = function (name) {
    // uppercase first letter of name
    const first = name[0];
    const capitalizedFirst = first.toUpperCase();

    // get rest of name
    const rest = name.slice(1);

    // create list with all letters of the name, including capitalized first letter
    const letters = [capitalizedFirst].concat(rest);

    // join letters, return result
    return letters.join('');
}

const titleCased = names.reduce(function (titleCasedNames, name) {
    // title-case the next name
    const titleCasedName = titleCase(name);

    // add the title-cased name to a list
    titleCasedNames.push(titleCasedName);

    // return list of capitalizedNames
    return titleCasedNames
}, [])
// ^ start with an empty list

console.log(titleCased);
// ["Arielle", "Jung", "Scheherazade"]

So...This is neat. We used reduce to turn a list of lower-cased names into a list of title-cased names.

所以...这很整洁。 我们使用reduce将一个小写名称列表转换为一个标题大写名称列表。

Previously, we turned a list of numbers into a single sum, and a list of strings into a single string. Here, we turned a list of names into a single list of title-case names.

以前,我们将数字列表转换为单个和,将字符串列表转换为单个字符串。 在这里,我们将名称列表变成了标题大小写名称的单个列表。

This is still allowed, because the single list of title-cased names is still a single result. It just happens to be a collection, rather than a primitive type.

仍然允许这样做,因为标题大小写名称的单个列表仍然是单个结果。 它只是一个集合,而不是原始类型。

You might have noticed something about that last example: We used reduce for the same task that we used map for earlier.

您可能已经注意到了有关最后一个示例的一些信息:对于先前使用map的相同任务,我们使用了reduce

It might not be obvious, yet, but—this is a big deal.

可能还不很明显,但是- 这很重要

You might guess that we can write map in terms of reduce. You'd be right.

您可能会猜想我们可以根据reduce编写map 。 你说的没错。

const map = (list, mapFunction) => {
    const output = list.reduce((transformedList, nextElement) => {
        // use the mapFunction to transform the nextElement in the list 
        const transformedElement = mapFunction(nextElement);

        // add transformedElement to our list of transformed elements
        transformedList.push(transformedElement);

        // return list of transformed elements
        return transformedList;
    }, [])
    // ^ start with an empty list

    return output;
}

We can get the same result as above with:

我们可以通过以下方式获得与上述相同的结果:

// congratulations...you just implemented `map` with `reduce`. badass.
const titleCased = map(names, titleCase);

console.log(titleCased);
// ["Arielle", "Jung", "Scheherazade"]

Obligatory disclaimer: If you were actually implementing a production-ready map, you'd need to add some features to this implementation. We'll leave all that off in the interest of clarity.

强制性免责声明:如果您实际上是在实现可用于生产的map ,则需要在此实现中添加一些功能。 为了清楚起见,我们将忽略所有这些内容。

Alright—last one. If we can use reduce to implement map...What about filter?

好的-最后一个。 如果我们可以使用reduce实现map ...那么filter呢?

const filter = (list, predicate) => {
    const output = list.reduce(function (filteredElements, nextElement) {
        // only add `nextElement` if it passes our test
        if (predicate(nextElement)) {
            filteredElements.push(nextElement);
        }

        // return the list of filtered elements on each iteration
        return filteredElements;
        }, [])
    })
}

We'd use this like:

我们将这样使用:

It's alright if this one feels confusing. It's a new idea even for more experienced developers. But, it's worth the work to wrap your head around it: We'll need this little bit of insight to make sense of transduction a little bit later.

如果这让人感到困惑,那也没关系。 即使对于经验丰富的开发人员来说,这也是一个新想法。 但是,值得您全神贯注的工作是:我们需要一点点洞察力,以便稍后再进行转导。

...And, trust me, transduction alone is cool enough to be worth the work.

...而且,请相信我,仅转导就足够了,值得进行这项工作。

向前 ( Onwards )

Today, you've learned how to use map, filter, and reduce to write more readable code.

今天,您已经学习了如何使用mapfilterreduce编写更具可读性的代码。

Not, of course, that there's anything wrong with using for. But raising the level of abstraction with these functions brings immediate benefits to readability, and maintainability, by corollary.

当然,使用for不会有任何问题。 但是,通过这些功能来提高抽象级别,必然会为可读性和可维护性带来直接的好处。

Next time, we'll round out our arsenal of array methods with a couple more favorites: flatten, concat, and flatMap.

下次,我们将使用更多一些收藏夹完善数组方法库: flattenconcatflatMap

Stay tuned!

敬请关注!

翻译自: https://scotch.io/tutorials/list-processing-with-map-filter-and-reduce

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值