用Ruby思考

Ker*_*nic 8 ruby activerecord ruby-on-rails

我目前正在忙于学习Ruby和Rails,因为我有基于C语言的背景,所以Ruby的一些概念是新的,有点陌生.对我来说特别具有挑战性的是适应接近常见问题的"Ruby方式",因此我经常发现自己在Ruby中编写C语言,这不是我想要实现的.

想象一下这样的架构:

ActiveRecord::Schema.define(:version => 20111119180638) do
    create_table "bikes", :force => true do |t|
        t.string   "Brand"
        t.string   "model"
        t.text     "description"
    end
end
Run Code Online (Sandbox Code Playgroud)

该数据库已包含几种不同的自行车.我的目标是获得数据库中所有品牌的数组.

这是我的代码:

class Bike < ActiveRecord::Base
    def Bike.collect_brands
        temp_brands = Bike.find_by_sql("select distinct brand from bikes")
        brands = Array.new
        temp_brands.each do |item|
          brands.push(item.brand)
        end
        brands
    end
end
Run Code Online (Sandbox Code Playgroud)

Ruby大师如何编写代码来实现这一目标?

mea*_*gar 26

tl; dr:你的整个方法都可以替换Bike.uniq.pluck(:brand).


此功能已经存在(请参阅我的答案结尾),但首先,让我们逐步完成您的代码并使其更具惯用性:

首先,每个级别的缩进使用两个空格,而不是四个,而不是八个,而不是制表符.使用两个空格.这不是个人偏好,这是Ruby社区中非常强大的惯例,如果您打算参与,则非常需要.

接下来,几乎没有充分的理由在Ruby中使用这种模式:

 brands = Array.new
 temp_brands.each do |item|
   brands.push(item.brand)
 end
Run Code Online (Sandbox Code Playgroud)

当您想通过将一些代码应用于输入数组中的每个值时,将一个数组转换为另一个数组(实际上,一个Enumerable转换为另一个Enumerable)时,请使用mapcollect(这是同义词):

brands = temp_brands.map { |item| item.brand }
Run Code Online (Sandbox Code Playgroud)

接下来,您可以利用symbol#to_proc以上代码更清晰:

brands = temp_brands.map &:brand 
Run Code Online (Sandbox Code Playgroud)

这看起来很奇怪的门外汉,但它,一旦你习惯使用的工作更加清晰map&:field.一点点经验会使这行代码的意图非常明显:它将brand方法应用于数组中的每个元素,并且它与先前{ |item| item.brand }版本完全等效.

现在,您的整个方法可以变得非常简单:

def Bike.collect_brands
  Bike.find_by_sql("select distinct brand from bikes").map &:brand
end
Run Code Online (Sandbox Code Playgroud)

内联选择/ SQL分明是一种丑陋,尤其是ActiveRecord的已经让我们选择特定字段select,结果不同的使用使uniq:

def Bike.collect_brands
  Bike.select(:brand).uniq.map &:brand
end
Run Code Online (Sandbox Code Playgroud)

作为最后的迭代,我们可以使用pluck而不是map仅从我们感兴趣的结果中提取字段.但是,因为pluck实际修改生成的SQL只包括被拔除的字段,我们可以省略该select(:brand)部分,并且我们的代码归结为包含两个链式方法的令人难以置信的短单行:

def Bike.collect_brands
  Bike.uniq.pluck(:brand)
end
Run Code Online (Sandbox Code Playgroud)

请注意,顺序很重要,因为pluck始终返回一个数组,而不是准备好进行其他方法链接的ActiveRecord关系.Bike.pluck(:brand).uniq将从每个记录(select brand from bikes)中选择品牌,然后在Ruby中将数组减少为唯一的项目.可能是非常昂贵的操作.

就是这样,Bike.uniq.pluck(:brand).作为一名C程序员,您会发现许多用于处理小循环的重复性任务实际上已经通过语言本身或支持库来解决.一旦你学会了编写惯用的Ruby和Rails代码,那么你写的代码量就会非常令人惊讶.

  • "从来没有因为你读'从来没有'而只是投票." (3认同)