如何使用CoffeeScript对数组进行排序?

21

有一个像这样的数组:

users = [ 
   { id: 1, fname: 'Fred', lname: 'Flinstone', state: 'CA' }, 
   { id: 2, fname: 'George', lname: 'Winston', state: 'FL' },
   { id: 3, fname: 'Luke', lname: 'Skywalker', state: 'CA' }
]

如果你想使用 Coffeescript 按姓氏排序,可以这样做:

users.sort (a,b) ->
  return if a.lname.toUpperCase() >= b.lname.toUpperCase() then 1 else -1

我尝试使用了如下的函数:

sortBy = (field, reverse, primer) ->
    key = (x) ->
      return if primer? then primer x[field] else x[field]
    return (a,b) ->
      A = key a
      B = key b
      return (A < B ? -1 : (A > B ? 1 : 0)) * [1,-1][+!!reverse]

这是如何调用的:

users.sort sortBy "lname", false, (a) -> 
  return a.toUpperCase()

但是那样无法正确地对数组进行排序。

有没有一种方法可以按多个字段进行排序,例如先按州份排序,然后再按姓氏排序?我希望改进上面的“sortBy”函数,并添加按至少两个字段进行排序的功能。


2
有什么东西会比那个“更好”?(实际上,我会使用<=而不是 <来帮助保持排序稳定。) - Pointy
1
“better”和“failed”是什么意思? - Robert Harvey
2
@LightnessRacesinOrbit:条件运算符是C语言家族中唯一的三元运算符,包括JavaScript在内。因此,“三元运算符”这个术语长期以来被认为是“条件运算符”的同义词。“条件”更可取,但“三元”也是完全有效的。 - Marcelo Cantos
@muistooshort,说得好,我忘记了CoffeeScript已经将这些符号用于其他目的。 我是在考虑纯JavaScript方面的。 - Ben Lee
3
以“咳嗽声”开始一句话会带有强烈的暗示,“你说错了”。虽然你可能没有“说”出来,但你肯定是在暗示着这个意思。 - Marcelo Cantos
显示剩余8条评论
3个回答

21
有一种更简单的方法。只需重复使用您的通用排序函数,并使用 || 连接它们即可:
sortBy = (key, a, b, r) ->
    r = if r then 1 else -1
    return -1*r if a[key] > b[key]
    return +1*r if a[key] < b[key]
    return 0

users.sort (a,b) ->
    sortBy('id', a, b, true) or
    sortBy('lname', a, b) or
    sortBy('fname', a, b)

函数很廉价。你可以为此构建一个抽象:

sortByMultiple = (a, b, keys) ->
    return r if (r = sortBy key, a, b) for key in keys
    return 0

users.sort (a,b) -> sortByMultiple a, b, ['id', 'lname', 'fname']

但是这样做会失去对它们中的每一个设置顺序或其他参数的能力。


这真的很棒。我正在尝试理解如何使用“或”来链接函数。这是因为它只会在前一个函数返回“0”时才移动到下一个sortBy函数吗?或者换句话说,它会一直排序直到满足第一个键(即“0”),然后再移动到下一个键吗? - jiy
测试使用或连接sortBy函数非常好!但是将sortByMultiple混合在一起并不成功,没有正确排序。感谢您提供非常有帮助的答案! - jiy
@jiy 是的,基本上就是这样。继续执行直到其中一个条件返回非零值。 - Ricardo Tomasi
修复了 sortByMultiple 的代码,我忘记传递参数了。 - Ricardo Tomasi

16

或者,您可能希望考虑使用UnderscoresortBy函数,这样您就不必自己实现它:

_(users).sortBy (u) -> [u.state, u.lname.toUpperCase()]

谢谢!看起来非常有前途。正是我所寻找的。 - jiy
你的答案中提供的链接已失效,新的链接为http://underscorejs.org/和http://underscorejs.org/#sortBy。 - Mathieu Brouwers

3

我觉得使用lodash时默认应该实现的东西,但实际上并没有。

所以我们来做吧!

sortByLowercase: (collection, key) ->
    collection.sort (a, b) ->
      [av, bv] = [a[key], b[key]]
      [av, bv] = [av.toLowerCase(), bv.toLowerCase()]
      if av >= bv then 1 else if av <= bv then -1 else 0

sortByLowercase(users,'lname')

Based on a more generic example I found here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接