【问题标题】:Ruby on Rails 4: Pluck results to hashRuby on Rails 4:提取结果进行哈希处理
【发布时间】:2016-03-15 12:24:14
【问题描述】:

我该如何转:

Person.all.pluck(:id, :name)

[{id: 1, name: 'joe'}, {id: 2, name: 'martin'}]

不必对每个值进行 .map(因为当我从 .pluck 添加或删除时,我必须对 .map 做同样的事情)

【问题讨论】:

    标签: ruby ruby-on-rails-4


    【解决方案1】:

    你可以map结果:

    Person.all.pluck(:id, :name).map { |id, name| {id: id, name: name}}
    

    正如@alebian 所说: 这比

    Person.all.as_json(only: [:id, :name])
    

    原因:

    • pluck 只返回使用的列 (:id, :name) 而其他解决方案返回所有列。根据表格的宽度(列数),这会产生很大的不同
    • pluck 解决方案不实例化Person 对象,不需要为模型分配属性等等。相反,它只返回一个包含一个整数和一个字符串的数组。
    • as_json 再次比简单的 map 开销更大,因为它是将模型转换为哈希的通用实现

    【讨论】:

    • 它使用 '.map' 这意味着我必须在进行更改时更改 pluck 和 map。
    • 是的,不是很好。 @Rohit Jangid 的 select 解决方案似乎更好。
    • 这个版本效率最高,应该是选的那个
    • @pascalbetz 我希望你没有侮辱你自己的答案,因为你给出的第一个选项比接受的答案更有效。
    • 正如其他人所提到的,我不喜欢在pluckmap 中重复自己,但是与as_json(only:) 相比,巨大的性能提升远远超过了我使用中的代码复杂性案例。
    【解决方案2】:

    你可以这样做

    Person.select(:id,:name).as_json
    

    你也可以试试

    Person.all.as_json(only: [:id, :name])
    

    【讨论】:

    • Person.select(:id,:name).as_json 是最短的,你只需要在一个地方更改字段你能添加一种方法来使用像 Person.all.as_json 这样的聚合函数吗(仅:['sum(some_field)'])?
    • @LucasSteffen 可以通过定义一个方法并调用它来实现聚合。例如,您可以在Person 模型中定义方法名称sum_of_fields,这将返回所需字段的总和。然后将该方法称为Person.all.as_json(methods: [:sum_of_fields])。有关更多详细信息,您可以阅读此documentation
    • 提供的两种可能性并不相同,因为第一种可能会将 id 添加到哈希中,即使它没有被要求,例如Person.select(:name).as_json 返回 [{id: nil, name: 'joe'}, {id: nil, name: 'martin'}] 而 Person.all.as_json(only: [:name]) 返回 [{name: 'joe'}, {name: 'martin'}] 所以对于不想要 id 的更一般的情况,第二种方法是更好。
    • @Obromios 我完全同意你的观点,并会提出同样的建议。但如果需要 id,那么 Person.select(:id,:name).as_json 会更干净。
    • pascal betz 的响应比这个效率高得多。这个基本上是在 SQL 查询结果之后创建 N 个 Person 对象并迭代它们以返回 JSON。 pluck+map 版本遍历查询结果并创建哈希对象。一个简单的基准测试可以验证这一点,在我的 1000 次测试的机器结果中,as_json 版本(所有运行)返回 395.65 秒,pluck+map 版本(所有运行)仅返回 26.63 秒,我的数据库有 2.2k 行跨度>
    【解决方案3】:

    我看到三个选项:

    1) pluckmap

    Person.pluck(:id, :name).map { |p| { id: p[0], name: p[1] } }
    

    2) pluck plus map plus zip 和一个使其干燥的变量:

    attrs = %w(id name)
    Person.pluck(*attrs).map { |p| attrs.zip(p).to_h }
    

    3) 或者您可能根本不使用pluck,尽管这样的性能要差得多:

    Person.all.map { |p| p.slice(:id, :name) }
    

    【讨论】:

    • 这太棒了!我从没想过将它与.map().zip().to_h 结合起来!感谢分享@spickermann
    【解决方案4】:

    如果你使用postgresql,你可以在pluck方法中使用json_build_object函数: https://www.postgresql.org/docs/9.5/functions-json.html

    这样,你可以让 db 创建哈希。

    Person.pluck("json_build_object('id', id, 'name', name)")
    #=> [{id: 1, name: 'joe'}, {id: 2, name: 'martin'}]
    

    【讨论】:

    • 虽然这种方法有效,但请注意使用它很危险。不应使用用户提供的值(例如请求参数或模型属性)调用此方法。可以通过将已知安全值包装在 Arel.sql() 中来传递它们
    【解决方案5】:

    可以在 pluck 之后以 ID 为键,名称为值进行散列:

    Person.all.pluck(:id, :name).to_h
    
    { 1 => 'joe', 2 => 'martin' }
    

    不确定这是否符合您的需求,但作为一种选择。

    【讨论】:

    • 本意是做一个以字段名为键的散列
    • 我明白了。一切都取决于你用它做什么。有时您更改数据以适应执行,有时您更改执行以适应数据。
    【解决方案6】:

    您可以为此使用恰当命名的 pluck_to_hash gem: https://github.com/girishso/pluck_to_hash

    它将使用 pluck_to_hash 方法扩展 AR,其工作原理如下:

    Post.limit(2).pluck_to_hash(:id, :title)
    #
    # [{:id=>213, :title=>"foo"}, {:id=>214, :title=>"bar"}]
    #
    
    Post.limit(2).pluck_to_hash(:id)
    #
    # [{:id=>213}, {:id=>214}]
    

    它声称比使用 AR selectas_json 快​​几倍

    【讨论】:

      【解决方案7】:

      pluck_all gem 的作用与pluck_to_hash 几乎相同。它声称它的速度提高了 30%。 (参见基准here)。

      用法:

      Person.pluck_all(:id, :name)
      

      【讨论】:

      • 考虑到pluck 经常用于性能,我想说的最有用的答案比这里的任何其他答案都更有效。
      【解决方案8】:

      如果你有多个属性,你可以这样做以保持整洁:

      Item.pluck(:id, :name, :description, :cost, :images).map do |item|
        {
          id:          item[0],
          name:        item[1],
          description: item[2],
          cost:        item[3],
          images:      item[4]
        }
      end
      

      【讨论】:

      • 谢谢,这对我的情况很有帮助。
      【解决方案9】:

      最简单的方法是将pluck 方法与zip 方法结合使用。

      attrs_array = %w(id name)
      Person.all.pluck(attrs_array).map { |ele| attrs_array.zip(ele).to_h }
      

      如果您在整个应用程序中使用此方法,您也可以创建一个辅助方法。

      def pluck_to_hash(object, *attrs)
        object.pluck(*attrs).map { |ele| attrs.zip(ele).to_h }
      end
      

      考虑通过将self 声明为默认接收者而不是将Person.all 作为object 变量进行修改。

      阅读更多关于zip的信息。

      【讨论】:

      • 我从一个表中得到 3 列。 Person.pluck(:id, :first_name, :last_name) 但我想把它放在一个地图中,其中 id 作为键,名称作为值。其中 name 是 first_name + " " + last_name。如何以优雅的方式在 ruby​​ 中做到这一点?
      【解决方案10】:

      这是一种对我来说效果很好的方法:

          def pluck_to_hash(enumerable, *field_names)
            enumerable.pluck(*field_names).map do |field_values|
              field_names.zip(field_values).each_with_object({}) do |(key, value), result_hash|
                result_hash[key] = value
              end
            end
          end
      

      【讨论】:

        【解决方案11】:

        我知道这是一个旧线程,但万一有人正在寻找更简单的版本

        Hash[Person.all(:id, :name)]
        

        在 Rails 5 中测试。

        【讨论】:

          猜你喜欢
          • 2014-09-08
          • 1970-01-01
          • 1970-01-01
          • 2014-04-09
          • 1970-01-01
          • 1970-01-01
          • 2012-01-28
          • 2010-12-06
          • 2013-08-21
          相关资源
          最近更新 更多