对 Ruby 哈希映射进行排序

3
我有以下一行代码。
<% map = options_for_select(User.all.map {|u| [u.first_name+" "+u.last_name, u.id]}) %>

这段代码提取了用户的名字和姓氏,并将其提交到表单中。现在我添加了一些用户,他们并不按照字母顺序排序。我该如何通过名字将这个映射排序?

4个回答

4
您可以使用order从数据库中获取已经排序的行:
<% map = options_for_select(User.all.order(:first_name).map {|u| [u.first_name+" "+u.last_name, u.id]}) %>

这里可能更好的方法,尤其是如果数据库在用于排序的属性上有索引,则很可能更有效率。 - Daniël Knippers
1
@DaniëlKnippers 是的,非常正确。有更好的方法(适用于任何事情)。但是只有在证明它们更好时才更好。你会如何证明呢?我很想看到证明,因为我愿意采纳它。 - Bala
@Bala 如果数据库在name属性上有索引,它已经知道顺序(据我所知,索引总是排序的,否则就没什么意义了),因此它可以按排序顺序从磁盘检索数据并返回。你的哈希表必须通过将所有元素相互比较来进行排序,因此速度应该会慢一些。在小量数据上可能不会注意到,但如果可能的话,应该优先在数据库层面进行排序。不过,如果没有索引的情况可能不太重要。 - Daniël Knippers
1
@DaniëlKnippers 如果数据库是NoSQL,比如Neo4j呢?OP没有提出这些限制。有时候过度思考会限制简单的解决方案。你同意吗? - Bala
@Bala 我认为Rails实际上不支持在Activerecord模型中使用NoSQL数据库,我相信它只能与关系型数据库一起使用。也许有解决方法。此外,我知道有一些支持索引的NoSQL数据库,例如MongoDB。无论如何,我并不是说你的答案是错误的,只是指出在数据库层面使用order很可能是相同或更快的。 - Daniël Knippers
@DaniëlKnippers: 我想从你的方法中学习,这就是我提出那些评论的原因。没有对错之分,一切都取决于我们如何看待它。顺便说一下,Neo4j支持索引......(我与Neo4j没有任何关系....) - Bala

2

可能是...

   <% map = options_for_select(User.all.map {|u| [u.first_name+" "+u.last_name, u.id]}.sort) %>

太好了...我在括号后面尝试了.sort...抱歉,我是Rails的新手...谢谢你! - Killerpixler
1
根据我的经验(我开始学习Rails时对Ruby一无所知...),学习Ruby可以解决我们在开发Rails应用程序时遇到的大部分问题。因此,尝试这种方法可以节省我们很多时间,并让我们对Ruby更加好奇。 - Bala
1
非常好!是的,我目前正在学习Lynda的Ruby课程。问题在于有时候不知道该把方法放在什么顺序里,比如在这里放置排序方法... - Killerpixler
小心在视图中直接使用那行代码。虽然它能工作,但同时也违反了几个Rails最佳实践 :) 请参考这个答案 - raviolicode

1

您不应该在视图中使用查询。您应该仅将视图用于展示,所有逻辑都应在模型和/或控制器中处理。

此外,请遵守Fat Models, Skinny Controllers Best Practice

实际上,这可能需要进行各种不同类型的重构,但所有的想法都归结为一点:通过将任何与响应无关的逻辑(例如设置闪存消息或选择是重定向还是呈现视图)移动到模型(而不是控制器),您不仅可以促进可能的重用,而且还可以在请求的上下文之外测试代码。

最后,在这种情况下,最好使用作用域以便稍后重用。

在用户模型上使用作用域,并拥有一个name方法:

class User < ActiveRecord::Base
  scope :order_by_name, ->(first_name, last_name) { order("#{first_name} ASC, #{ last_name} ASC") }

  def name
    "#{first_name} #{last_name}"
  end
end

如果您从 users/index 中调用您的线路,请创建一个实例变量来加载用户集合,如下所示:
class UsersController < ApplicationController
  def index
    @users = User.order_by_name
  end
end

然后,您需要使用options_from_collection_for_select调用视图,如下所示:
<% map = options_from_collection_for_select(@users, :id, :name) %>

0
我建议出于性能原因,在数据库中同时执行 CONCAT 和 ORDER。
User.select("CONCAT(u.first_name, ' ', u.last_name), u.id").order("u.first_name")

User.all.map可能成为您应用程序的瓶颈


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