Лучший способ перебрать последовательность и изменить один объект за раз (Ruby) - PullRequest
2 голосов
/ 05 марта 2009

Я новичок в ruby ​​и чувствую, что до сих пор много чего делаю на C: Предположим, у вас есть массив объектов (Вопрос: has_many =>: ответы). Я хочу перебрать все ответы, и если некоторые ответы соответствуют критериям, измените атрибут ответа. В настоящее время я делаю это следующим образом:

def iterate_and_do_stuff
   for (a in self.answers)
      if(a.somecriteria==true)
         a.some_attr=some_val
      end
   end
end

Каковы другие способы сделать это? Блоки, петли и т. Д.

Пожалуйста, введите.

Спасибо.

Ответы [ 5 ]

12 голосов
/ 05 марта 2009

Использовать Массив # каждый :

self.answers.each {|a| a.some_attr = some_val if a.some_criteria}

4 голосов
/ 05 марта 2009

Я предпочитаю карту! или собирать! в этом случае, потому что вы можете использовать! метод семантического представления, что вы меняете массив на месте.

self.answers.map!{ |a| 
   a.some_criteria ? (a.some_attr = some_val) : a.some_attr  
}

или

self.answers.collect!{ |a| 
   a.some_criteria ? (a.some_attr = some_val) : a.some_attr
}

Таким образом, очень ясно, что вы намереваетесь изменить массив.

2 голосов
/ 05 марта 2009
self.answers.select{|a| a.somecriteria}.each{|a| a.some_attr = some_val}

OR

self.answers.find_all{|a| a.somecriteria}.each{|a| a.some_attr = some_val}

Оба варианта менее эффективны, чем ваш исходный код.

1 голос
/ 06 марта 2009

Если вы работаете с моделями ActiveRecord, не забывайте, что вы можете делать выбор на уровне базы данных, а не в памяти. Чтобы перефразировать, вы можете извлечь из базы данных только те значения, которые вам нужны, и затем просто изменить их.

Вот пример использования пользовательских средств поиска (своего рода более старый способ сделать это):

class Question < ActiveRecord::Base
  has_many :answers do
    def accepted
      find :all, :conditions => { :accepted => true }
    end
  end
end

class Answer < ActiveRecord::Base
  belongs_to :question
end

q = Question.find :first
q.answers.accepted.each { |a| a.do_something! }

Или вы можете сделать это с другой ассоциацией:

class Question < ActiveRecord::Base
  has_many :answers
  has_many :accepted_answers, :class_name => "Answer", :conditions => { :accepted => true }
end

class Answer < ActiveRecord::Base
  belongs_to :question
end

q = Question.find :first
q.accepted_answers.each { |a| a.do_something! }

Вот еще один пример использования именованных областей (немного новее и предпочтительнее, на мой взгляд) в вашем дочернем классе:

class Question < ActiveRecord::Base
  has_many :answers
end

class Answer < ActiveRecord::Base
  belongs_to :question
  named_scope :accepted, :conditions => { :accepted => true }
end

q = Question.find :first
q.answers.accepted.each { |a| a.do_something! }

Но в любом случае вы абстрагировали «подбор», который имеет несколько преимуществ:

  1. Быстрее в случае больших коллекций
  2. Вы абстрагировали критерии выбора более низкого уровня к чему-то с семантическим значением более высокого уровня, что упрощает чтение и поддержку вашего кода
1 голос
/ 05 марта 2009

просто для удовольствия альтернатива

self.answers.select{|a| a.some_criteria}.each{|a| a.some_attr = some_val}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...