【问题标题】:A more elegant way to write this .map call?编写此 .map 调用的更优雅的方式?
【发布时间】:2015-11-12 15:02:25
【问题描述】:

我有一些代码,它本质上是一个对象数组,只是为每个项目添加一个额外的键。我希望能够像实验一样简洁地表达这一点。

let fruits = [
   {"type" : "orange"},
   {"type" : "apple"},
   {"type" : "banana"}
];
console.log(fruits.map((fruit) => {
         fruit.price = "$1.00";
         return fruit;
}));

目前,这是可行的,但它肯定没有一个衬里,return 语句仍然在那里,我觉得有一种方法可以摆脱它,因为胖箭头语法。

【问题讨论】:

  • 在调试了一个奇怪的问题 4 小时后,我痛苦地发现,赋予 map 函数的对象是原始数组中的实际对象。这意味着在这种情况下,您实际上可能不必在函数中返回。原来的数组会被修改
  • “只是添加了一个额外的键” - 这意味着不涉及映射。所以map 不是开始的正确工具。
  • 我不知道为什么 Felix Kling 删除了 ES6 标签……它本质上是在询问如何围绕粗箭头编写代码。 @Suppen:感谢您指出这一点。就我而言,我确实想返回,因为我想对结果使用方法链接。
  • 简单来说:map是你在做变换的时候用的,输入的大小就等于输出的大小。在这种情况下,我不会修改fruits 的大小,所以map 是正确的工具。

标签: javascript functional-programming ecmascript-6


【解决方案1】:

一种方法是使用Object.assign 扩展对象并返回结果新创建的对象:

console.log(fruits.map(fruit => Object.assign(fruit, { price: "1.00" })));

Babel REPL Example

这消除了对return 关键字的需求,但它几乎不是最大的节省空间的方法。它也等同于您已经拥有的(因为修改了原始 fruit 对象。正如 joews 在下面指出的那样,如果您想保持原始数组保持原样,您可以使用一个空的目标对象,如下所示:

Object.assign({}, fruit, { price: "1.00"});

这将确保您的原始数组未修改(可能是也可能不是您想要的)。

最后,将它与展开运算符结合起来:

console.log(fruits.map(fruit => ({...fruit, price: "1.00" })));

【讨论】:

  • 这确实改变了fruit。要创建一个新对象,您可以Object.assign({}, fruit, { price: "1.00"})
  • Object.assign 看起来不错。就我而言,可变性/不变性不是问题,所以我想我最终会选择你的第一个例子。
【解决方案2】:

如果不需要fruits的原版,也可以用.forEach()代替.map()直接修改fruits

fruits.forEach((fruit) => fruit.price = "$1.00");

http://www.es6fiddle.net/igwdk0gk/

【讨论】:

  • forEach 是有道理的,但在更大的脚本范围内,我想将水果传回另一段代码。不幸的是,forEach 什么都不返回,所以它不支持好的方法链接(这就是我想使用 .map 的原因)
  • 这个信息应该是问题的一部分
  • 我用函数式编程标签暗示了它,但我想这可能更明确。
【解决方案3】:

可以做这样的事情,不推荐阅读,但从技术上讲是一行。

fruits.map(fruit => (fruit.price = "$1.00") && fruit);

正如其他人所提到的,此方法只是向对象添加一个属性,而不是复制它。将其保留为单行、使用地图并实际创建副本的一种简单方法是:

fruits.map(fruit => Object.assign({price: "$1.00"}, fruit));

Object.assign() 会将fruit 的所有属性分配给对象{ price: "$1.00" } 并返回。

现场示例:

"use strict";

let log = function() {
  output.textContent += [].join.call(arguments, ' ') + '\n\n';
};

log('# MAP (OR FOREACH) WITHOUT ASSIGN');

let fruits = [
   {"type" : "orange"},
   {"type" : "apple"},
   {"type" : "banana"}
];
let newfruits = fruits.map(fruit => (fruit.price = "$1.00") && fruit);

log('fruits', JSON.stringify(fruits));
log('newfruits', JSON.stringify(newfruits));
log('^-- Both are modified since newfruits its a new array with the same objects');

log('# MAP WITH ASSIGN');

fruits = [
   {"type" : "orange"},
   {"type" : "apple"},
   {"type" : "banana"}
];
newfruits = fruits.map(fruit => Object.assign({price: "$1.00"}, fruit));

log('fruits', JSON.stringify(fruits));
log('newfruits', JSON.stringify(newfruits));
log('^-- Only newfruits is modified since its a new array with new objects');
pre {
  word-wrap: break-word;
}
<pre id="output"></pre>

【讨论】:

    【解决方案4】:

    有很多方法可以做到这一点:

    逗号运算符的副作用

    如果你想内联,你可以使用逗号运算符(虽然它有点晦涩):

    fruits.map((fruit) => (fruit.price = "$1.00", fruit))
    

    我们也可以使用&&,因为赋值返回分配的值,"$1.00" 是真实的,但逗号运算符更通用,因为我们还可以设置false0,让一切继续工作。

    高阶函数

    不过,创建一个辅助函数可能会更好:

    // We're currying manually here, but you could also make the signature
    // setter(name, value) and use your function of choice to curry when you need to.
    function setter(name) {
      return (value) => (obj) => {
        obj[name] = value;
        return obj;
      }
    }
    

    那么你可以使用:

    fruits.map(setter("price")("$1.00"))
    

    拥抱可变性

    正如@Suppen 在他们的评论中指出的那样,因为普通的 JavaScript 对象是可变的,您也可以避免使用 map 并改用 forEach

    fruits.forEach(fruit => fruit.price = "$1.00");
    // Each element in fruits has been modified in-place.
    

    【讨论】:

    • 那么为什么有人会选择前两个解决方案而不是forEach?毕竟他们不映射任何东西。
    • @zeroflagL - 因为您实际上想要fruits 数组的副本,而不是由于某种原因而不是同一个数组?
    【解决方案5】:

    映射函数应该几乎总是纯的。如果你只打算修改对象,一个简单的循环会更好(for (let fruit of fruits) fruit.price = …; console.log(fruits);)。

    因此,当您返回一个新对象时,单行将很容易:

    console.log(fruits.map(({type}) => ({type, price:"$1.00"})));
    

    如果您有很多属性,或者您不知道的属性,那么Object.assign({}, …) 是您的朋友(如@joews 对@RGraham 的回答的评论)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-31
      • 2022-10-25
      • 2019-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多