最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

JavaScript的Reduce如何分配其价值?

运维笔记admin8浏览0评论

JavaScript的Reduce如何分配其价值?

JavaScript的Reduce如何分配其价值?

问题:我不明白reduce是如何从阵列中分配/减少客户名称的。我需要有人来解释这里发生的事情。

详细说明

在Fun Fun Function的函数式编程系列()的第4集中,Mattias Petter Johansson介绍了为客户及其订单加载简单数据集并通过reduce函数将数据转换为分层对象的示例。

这是示例制表符分隔的数据集 - 我在本地命名为data.txt

mark johannson  waffle iron 80  2
mark johannson  blender 200 1
mark johannson  knife   10  4
Nikita Smith    waffle iron 80  1
Nikita Smith    knife   10  2
Nikita Smith    pot 20  3

这是加载和处理数据集的JavaScript文件 - 我在本地命名为reduce.js

var fs = require("fs");

var output = fs
    .readFileSync("data.txt", "utf-8")
    .trim()
    // NOTE: On Windows, I needed to use \r\n. You may only need \r or \n.
    .split("\r\n")
    .map(line => line.split("\t"))
    .reduce((customers, line) => {
        customers[line[0]] = customers[line[0]] || [];
        customers[line[0]].push({
            name: line[1],
            price: line[2],
            quantity: line[3]
        });
        return customers;
    }, {});

console.log("output", JSON.stringify(output, null, 2));

最后,这是我用来通过NodeJS执行这个js文件的简单命令。

node reduce.js

(或者,如果你想在一个地方看到所有代码和数据,我在这里包含了一个jsFiddle:/)

代码执行的输出看起来像

{
  "mark johannson": [
    {
      "name": "waffle iron",
      "price": "80",
      "quantity": "2"
    },
    {
      "name": "blender",
      "price": "200",
      "quantity": "1"
    },
    {
      "name": "knife",
      "price": "10",
      "quantity": "4"
    }
  ],
  "Nikita Smith": [
    {
      "name": "waffle iron",
      "price": "80",
      "quantity": "1"
    },
    {
      "name": "knife",
      "price": "10",
      "quantity": "2"
    },
    {
      "name": "pot",
      "price": "20",
      "quantity": "3"
    }
  ]
}

让我们快速浏览一下代码中发生的事情。

  • .readFileSync("data.txt", "utf-8")使用utf-8编码读入文件。 (否则,文字难以理解。)

utf-8转换之前......

<Buffer 6d 61 72 6b 20 6a 6f 68 61 6e 6e 73 6f 6e 09 77 61 66 66 6c 65 20 69 72 6f 6e 09 38 30 09 32 0d 0a 6d 61 72 6b 20 6a 6f 68 61 6e 6e 73 6f 6e
09 62 6c ... >

utf-8转换之后......

mark johannson  waffle iron     80      2
mark johannson  blender 200     1
mark johannson  knife   10      4
Nikita Smith    waffle iron     80      1
Nikita Smith    knife   10      2
Nikita Smith    pot     20      3
  • .trim()函数删除文件末尾的任何换行符。
  • .split("\r\n").map(line => line.split("\t"))函数将传入的文件文本转换为数组数组。

.split("\r\n")之后

[ 'mark johannson\twaffle iron\t80\t2',
  'mark johannson\tblender\t200\t1',
  'mark johannson\tknife\t10\t4',
  'Nikita Smith\twaffle iron\t80\t1',
  'Nikita Smith\tknife\t10\t2',
  'Nikita Smith\tpot\t20\t3' ]

.map(line => line.split("\t"))之后

[ [ 'mark johannson', 'waffle iron', '80', '2' ],
  [ 'mark johannson', 'blender', '200', '1' ],
  [ 'mark johannson', 'knife', '10', '4' ],
  [ 'Nikita Smith', 'waffle iron', '80', '1' ],
  [ 'Nikita Smith', 'knife', '10', '2' ],
  [ 'Nikita Smith', 'pot', '20', '3' ] ]

重要的是要指出阵列的每一行中恰好有四个元素。这些对应于customer name [index 0],item name [index 1],price [index 2]和quantity [index 3]。

  • .reduce...函数将数组转换为JavaScript对象。 .reduce((customers, line) => { customers[line[0]] = customers[line[0]] || []; customers[line[0]].push({ name: line[1], price: line[2], quantity: line[3] }); return customers; }, {});

Reduce在这里接受三个参数(虽然我们没有提供另外两个可选参数。)提供的参数是:1)reduce的累加器的当前值由变量customers表示,数组中的当前行由变量line表示,以及reduce将构建的起始值 - 由空对象文字{}提供。

我们的行变量的值是

  1. line[0] =客户名称
  2. line[1] =产品名称
  3. line[2] =价格
  4. line[3] =数量

在reduce函数中,发生了三件事。

  1. 从,customers[line[0]] = customers[line[0]] || [];,我们(不知何故神奇地)提取每个客户。这样可以保留当前客户(如果存在),或者如果没有客户则将客户设置为空数组。 问题:即使我们将线设置为customers[line[0]] = [],那么仍然会返回customer name。这有可能在世界上如何?如果我们将customer设置为空数组,reduce如何返回customer的值?在customers[line[0]] = [],我们没有把任何东西推到阵列上。 如果我们将客户[line [0]]设置为空数组并且永远不会将line [0]的值推送到客户身上,我绝对会对客户如何在此设置并返回。
  2. 项目的namepricequantity的数据被推送到附加到客户的阵列中。 customers[line[0]].push({ name: line[1], price: line[2], quantity: line[3]
  3. 返回customer对象并提供下一次reduce迭代。当阵列中没有更多行可用于处理时,将返回customer数组作为最终结果。
回答如下:

line [0]是客户的名字。这意味着:

customers[line[0]] = customers[line[0]] || []

等于这个:

const customerName = line[0];
customers[customerName] = customers[customerName] || [];

它的作用是确保在尝试将对象推送到客户列表之前,实际存在列表。让我们分配并看看会发生什么:

customers['mark johnson'] = customers['mark johnson'] || [];

因此,当没有任何东西时,客户对象被分配给属性'mark johnson'的空列表,或者当前存在的列表。

所以它等同于:

const customerName = line[0];
if (!customers[customerName]) {
    customers[customerName] = [];
}

之后的代码基本上是破坏'line'列表,并将其映射到对象的属性,然后推送到上面的列表。所以它等同于:

.reduce((customers, line) => {
    const customerName = line[0];
    if (!customers[customerName]) {
        customers[customerName] = [];
    }
    const price = line[2];
    const quantity = line[3];
    const currentLine = {
        name: customerName,
        price: price,
        quantity: quantity
    };

    customers[customerName].push(currentLine);
    return customers;
}, {});

但显然前面的代码更好:)如果使用Destructuring,可能会更清楚一些:

.reduce((customers, [name, price, quantity]) => {
    customers[name] = customers[name] || [];
    customers[name].push({name, price, quantity});
    return customers;
}, {});

希望有所帮助。

发布评论

评论列表(0)

  1. 暂无评论