ES6中的Array.reduce(),隐式返回和语法糖。实际情况是什么?

时间:2019-06-24 06:42:51

标签: javascript arrays ecmascript-6

我有兴趣了解此算法内部发生的情况。让我们考虑以下数据集:

const data = [
  {
    emp_code: "a001",
    company_code: "company_a",
    name: "abx",
    details: [],
    details_dtypes: []
  },
  {
    emp_code: "b002",
    company_code: "company_b",
    name: "xbz ",
    details: [],
    details_dtypes: []
  },
  {
    emp_code: "a002",
    company_code: "company_a",
    name: "xbz ",
    details: [],
    details_dtypes: []
  },
  {
    emp_code: "b003",
    company_code: "company_b",
    name: "xbz ",
    details: [],
    details_dtypes: []
  }
];

现在,如果我想将此数据压缩为object {},其中每个键是唯一的company_code,并且其对应值是array []的{​​{1}}公司,我可以做类似的事情:

emp_codes

在上面,我们显式地返回最终对象,这看起来很清楚。

现在,我最近发现您可以使用一些语法糖来缩短它,例如:

let result = data.reduce((r, c) => {
  r[c.company_code] = [...(r[c.company_code] || []), c.emp_code];
  return r;
}, {});

结果是相同的,但是我不确定这怎么可能。我对let result = data.reduce( (r, c) => (r[c.company_code] = [...(r[c.company_code] || []), c.emp_code]) && r, {} ); 的理解是,有一个内部循环遍历每个项目,并允许您应用某些逻辑来制定最终结果。 我看到他们正在使用reduce()运算符。我的假设是,括号&&之间的逻辑像传统的()方法一样执行,当完成时,我们仅返回结果,因此返回reduce()

但是那件事似乎是错误的。内环还在发生吗?如果是这样,他们能够像这样隔离它,而不必在第一次迭代后简单执行条件,因此返回&& r。他们是否为循环的每次迭代都返回r

这是一个沙箱,显示两种算法都有效:https://codesandbox.io/s/hooks-with-reduce-nested-data-goee1。非常感谢您的贡献:)

3 个答案:

答案 0 :(得分:3)

对于&&,如果涉及的所有表达式都是真实的,则它将求值为最终表达式的值-在&&之后的表达式。因此,如果您有以下代码:

function foo() {
  <some expression>;
  return bar;
}

您也可以这样写

function foo() {
  return <some expression> && bar;
}

或者,带有箭头函数的隐式返回:

const foo = () => <some expression> && bar;

等效于

const foo = () => {
  <some expression>;
  return bar;
};

是的,对于您的代码,在箭头函数的末尾带有&& r,这意味着r在每次迭代时都会隐式返回。

总而言之,这几乎与语法糖相反。如果您通过 minifier 运行代码,这是您经常会看到的事情,其目的是尽可能减少Javascript中的文本量。但是,精简代码通常很难阅读,正如您的问题所示,这也不例外。如果使用隐式return要求逗号运算符或利用&&来评估除布尔值以外的其他符号,则代码可能太容易混淆而难以理解,而显式{{1 }}可能是更好的选择。

您还可以考虑推送到累加器对象中的现有数组,从而避免创建许多不必要的中间数组,这些中间数组只会在以后传播:

return

那是我想要的代码。它更长一些,但是当每个语句/逻辑分支都位于自己的行时,它更容易阅读很多

答案 1 :(得分:1)

让我们反汇编此行:

(r[c.company_code] = [...(r[c.company_code] || []), c.emp_code]) && r

JS always return assigned element中的赋值和逻辑AND运算符(&&)遵循以下规则:

expr1 && expr2

如果expr1可以转换为true,则返回expr2;否则,返回expr1

所以基本上和

相同
if(r[c.company_code] = [...(r[c.company_code] || []), c.emp_code])
  return r

并且由于JS中的数组([...(r[c.company_code] || []), c.emp_code])在解析为布尔值(顺便说一下,与对象,函数和符号相同)时会转换为true,因此它总是返回r。

答案 2 :(得分:1)

这里的问题很简洁。如果过多减少代码,则会降低可读性。这就是问题。让我们逆转过程并使您的代码更传统。

let result = data.reduce(
  (r, c) => (r[c.company_code] = [...(r[c.company_code] || []), c.emp_code]) && r,
  {}
);

第1、3和4行非常简单。棘手的部分是第2行。

(r, c) => (r[c.company_code] = [...(r[c.company_code] || []), c.emp_code]) && r,

这等效于:

function processData(accumulator, currentItem) {
  if ( accumulator[currentItem.compony_code] === undefined ) {
    accumulator[currentItem.compony_code] = [];
  }

  accumulator[currentItem.compony_code].push(currentItem.emp_code);
  return accumulator;
}

现在开始假设(),它是一种黑客,可以创建表达式并允许您执行非表达式(在这种情况下为赋值)任务。