在coffeescript中有效地减少数组

时间:2013-01-29 15:43:13

标签: coffeescript

如果我在var中有一个对象数组。我想减少它,以便按特定属性进行分组。这是我的代码

array = tracks.reduce (x,y,i) ->
    x[y.album] = []
    x
, {}

albums = tracks.reduce (x,y,i) ->
    array[y.album].push {'name':y.name, 'mp3':y.mp3}
    array
, {}


console.log(albums)

它输出我想要的东西,但是我想知道是否有更好的方法来编写它,而不必进行第一次循环,为组创建空数组。

感谢。

1 个答案:

答案 0 :(得分:4)

是的,您可以使用or=?=运算符仅在尚未初始化时指定array[y.ambum];因此只使用一个循环。顺便说一下,我认为array变量是一个对象有点令人困惑。使用CoffeeScript循环而不是reduce对此进行编码的另一种方法是:

albums = {}
for {album, name, mp3} in tracks
  (albums[album] or= []).push {name, mp3}

请注意,我正在使用destructuring一次获取曲目属性。

或者,如果您想使用reduce

albums = tracks.reduce (albums, {album, name, mp3}) ->
  (albums[album] or= []).push {name, mp3}
  albums
, {}

但我认为CS循环版本读得更好一些:)

奖金跟踪(双关语):如果您碰巧拥有Underscore.js,我强烈建议使用groupBy,这正是这种分组工作:

albums = _.groupBy tracks, (track) -> track.album

请注意,albums中每个专辑名称的曲目将是“完整”曲目(不仅仅是名称和mp3属性)。


更新:关于性能的评论:当被要求“有效”地做某事时,我将其解释为尽可能以最直接和最干净的方式进行(我正在考虑程序员在阅读时的效率)代码);但许多人会明确地将效率与绩效联系起来。

关于性能,所有这三个解决方案都是O(n),n是轨道的数量,复杂程度;所以两者都不比其他人差。

It seems原始for循环在现代JS引擎上的运行速度比同等的高阶兄弟快得多:forEachreduce等等(IMO非常令人伤心:(。 ..)。所以第一个版本应该比第二个版本运行得快。

在Underscore版本的情况下,我不会做任何预测,因为已知Underscore使用更高阶函数很多而不是原始for循环,但是同时,该版本不会为每个轨道创建新对象。

在任何情况下,在将解决方案更改为可能性能更高但可读性较差的解决方案之前,您应始终对代码进行概要分析。如果您注意到该特定循环是一个瓶颈,并且您有一组很好的数据可以对其进行基准测试,那么jsPerf可能非常有用:)