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)


この機能は既に存在します (私の回答の最後を参照してください) が、まず、コードをステップ実行して、より慣用的なものにします。

何よりもまず、インデントのレベルごとに 2 つのスペースを使用してください。4 つでも 8 つでもなく、タブでもありません。2 つのスペースを使用します。これは個人的な好みではありません。これは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 

これは初心者には奇妙に見えるかもしれませんが、 と の使用に慣れるより明確にmapなり&:fieldます。少し経験を積めば、このコード行の意図が非常に明白にbrandなります。配列内の各要素にメソッドを適用しており、以前の{ |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)部分を省略できます。コードは、2 つの連鎖したメソッドを含む信じられないほど短い 1 行に要約されます。

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 に答える