【问题标题】:Ruby on Rails: Sorting collection of objectsRuby on Rails:对对象集合进行排序
【发布时间】:2011-12-01 04:07:14
【问题描述】:

我有一个方法可以提供一个模型对象数组。通过使用 SQL 的帮助,可以很容易地对这些模型的一些属性进行排序。我的意思是,使用 .find(:condition => {})

问题是其他一些属性不是。它必须在显示之前进行计算、修改或执行其他操作。然后,我找到了使用集合排序对其进行排序的方法。也就是说,collection.sort{|a,b| a.something_to_sort b.something_to_sort}

好的!那是有效的。但问题是,是否有可能使 something_to_sort 部分成为动态变量?例如,我想从 UI 中获取一个参数并将其分配给 something_to_sort,如下所示,

HTML

<select name="sort">
    <option value="name">Name</option>
    <option value="age">Age</option>
    <option value="activity.category">Activity</option>
    <option value="tax.calculate_personal_income_tax">Income Tax</option>
    <option value="tax.calculate_withholding_tax">Withholding Tax</option>
    <option value="doctor.name">Doctor's Name</option>
</select>

导轨

params[:sort] ||= "Age"
@people = Person.find(:all)
@people.sort{|a,b| a.params[:sort] <=> b.params[:sort]}    #Note that this is an incorrect syntax

有没有什么方法可以不用为每个排序选项编写一个排序块?

附加 #1

我刚刚尝试过,它适用于某些排序选项

@people.sort{|a,b| a.send(params[:sort]) <=> b.send(params[:sort])}

这适用于姓名和年龄,因为它们是人们的属性(它也适用于人们的方法)。但是对于诸如tax.calculate_personal_income_tax 之类的协会,它不是。

所以,我的同事告诉我创建新的人员方法,calculate_personal_income_tax,然后将选项值从 tax.calculate_personal_income_tax 更改为 calculate_personal_income_tax 所以发送方法将能够找到此方法...

def calculate_personal_income_tax
    tax.calculate_personal_income_tax
end

但是,这并不是我真正想要的。因为我相信必须有一种方法可以访问这个关联方法,而不必定义新的模型方法。如果我选择这样做,并且有一天人的模型会变得更大、更多属性、更多要排序和显示的信息。那么,就会有很多这样的方法。

另一个原因是,如果我必须定义一个新方法,该方法应该做一些逻辑、计算或其他,而不仅仅是从其他模型中检索数据。

补充 #2

最后,我刚刚找到了访问其他模型属性的方法,方法是在 find 方法、:join:select 中添加 args >.

@people = Person.find(:all, :select => "persons.*, tax.tax_rate AS tax_rate", :joins => :tax, :condition => {some conditions})

结果将是 persontax 连接的表格。然后,我使用 send(params[:sort]) 东西来帮助我对我想要的属性进行排序。

【问题讨论】:

  • 所以你不能使用 SQL 来排序结果?
  • 是的,有些显示列不能通过SQL语句排序(或者可能我还不知道怎么做)。其中一些过于复杂,因为它涉及许多相关模型

标签: ruby-on-rails ruby-on-rails-3


【解决方案1】:
@people.sort {|a, b| a.send(params[:sort]) <=> b.send(params[:sort])}

【讨论】:

  • 谢谢伙计,我也找到了。问题是,它适用于人们的属性或方法。但不是因为它的关联(例如tax.calculate_personal_income_tax)......无论如何,再次感谢队友。
【解决方案2】:
params[:sort] ||= "age"
method_chain = params[:sort].split(".")
@people = Person.find(:all)
@sorted_people = 
  @people.sort_by do |person|
    method_chain.inject(person) do |memo,method|
      memo.send(method)
    end
  end

这假定 params[:sort] 中传递的值始终是有效的方法名称,可以按照指定的顺序链接在一起。您需要记住,任何东西都可以在 params 哈希中传递,因此如果暴露给不受信任的用户(例如 params[:sort] = "destroy"。哎呀),这将是非常不安全的。

此外,如果您可以在 SQL 中执行此操作,您将获得更好的性能。

事实上,我越看这个,就越觉得这是个坏主意。

【讨论】:

    【解决方案3】:

    我有一个战利品系统,它根据计算值执行此操作,该值基于可以更改的参考表。在计算值时,它可能来自任何地方。

    我的 player/index.html.rb 看起来像这样。

    <h1>Players</h1>
    <table>
      <tr>
        <th>Name</th>
        <%= generate_headings(params) %>
      </tr>
    
      <% players.each do |player| %>
        <tr>
          <td><%= player.name %></td>
          <% LootType.all.each do |loot_type| %>
            <td><%= player.loot_rate(loot_type.name) %></td>
          <% end %>
        <tr>
      <% end %>
    </table>
    

    loot_rate 函数在两个相关表(player.raids 和 player.items)上计算为“玩家袭击/战利品类型 + 1”。

    generate_headings 函数在players_helper.rb 中,它看起来像:

    def generate_headings(params = {})
      sort = params[:sort]
      headings = ""
      LootType.all.each do |loot_type|
        heading_text = "#{loot_type.name} Rate"
        if (sort == loot_type.name)
          column_heading = "<th>#{heading_text}</th>"
        else
          heading_url = "/players?sort=#{loot_type.name}"
          column_heading = "<th>#{link_to heading_text, heading_url}</th>"
        end
        headings += column_heading
      end
      headings.html_safe
    end
    

    在我的 player_controller.rb 索引操作中,我有:

    def index
      sort = params[:sort]
      if !sort
        @players.sort! { |a,b| a.name <=> b.name }
      else
        @players.sort! { |a,b| b.loot_rate(sort) <=> a.loot_rate(sort) } # Reverse sort (for my purposes)
      end
    
      respond_to do |format|
        format.html
        format.json {render :json => @players }
      end
    end
    

    这意味着当我在系统中添加或删除“战利品类型”时,玩家列表会自动允许用户按这些类型进行排序。

    冗长的答案,但希望它有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多