8

我目前正忙于学习 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

该数据库已经包含几种不同的自行车。我的目标是获取数据库中代表的所有品牌的数组。

这是我的代码:

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

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

4

1 回答 1

26

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


此功能已经存在(请参阅我的答案的结尾),但首先,让我们单步执行您的代码并使其更加惯用:

首先,每级缩进使用两个空格,而不是四个,不是八个,也不是制表符。使用两个空格。这不是个人喜好,这是 Ruby 社区中一个非常强大的约定,如果您打算参与,这几乎是必需的。

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

 brands = Array.new
 temp_brands.each do |item|
   brands.push(item.brand)
 end

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

brands = temp_brands.map { |item| item.brand }

接下来,您可以利用symbol#to_proc使上面的代码更清晰一点:

brands = temp_brands.map &:brand 

对于外行来说,这看起来很奇怪,但是一旦你习惯了使用and就会更清楚。稍微体验一下,这行代码的意图就很明显了:就是将方法应用到数组中的每个元素上,和之前的版本完全等价。map&:fieldbrand{ |item| item.brand }

现在,你的整个方法可以变成一个非常简单的单行:

def Bike.collect_brands
  Bike.find_by_sql("select distinct brand from bikes").map &:brand
end

内联 select/distinct SQL 有点难看,特别是因为 ActiveRecord 已经允许我们使用选择特定字段select并使用 使结果不同uniq

def Bike.collect_brands
  Bike.select(:brand).uniq.map &:brand
end

作为最终迭代,我们可以使用pluck而不是map仅从我们感兴趣的结果中提取字段。但是,由于pluck实际上将生成的 SQL 修改为仅包含正在提取的字段,我们可以省略该select(:brand)部分,并且我们的代码归结为包含两个链接方法的非常短的单行:

def Bike.collect_brands
  Bike.uniq.pluck(:brand)
end

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

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

于 2012-10-23T16:49:58.183 回答