JavaScript如何减少分配其价值?

时间:2017-12-17 15:33:05

标签: javascript arrays node.js mapreduce reducers

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

详细说明

Fun Fun Function 的函数式编程系列(https://www.youtube.com/watch?v=1DMolJ2FrNY)的第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:https://jsfiddle.net/anthonygatlin/27vpoa13/

代码执行的输出类似于

{
  "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 [索引0],item name [索引1],price [索引2]和quantity [索引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] =数量
  5. 在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数组将作为最终结果返回。

1 个答案:

答案 0 :(得分:2)

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;
}, {});
希望有所帮助。