从二维数组中比较和选择值的最优雅的 Ruby 表达式是什么?

What is the most elegant Ruby expression for comparing and selecting values from a 2D Array?

我有一些代码正在处理一组 Rails Active Record 模型,并根据二维数组中的相关值设置属性。

我实际上是在 table 美国各州中设置一个美国州缩写代码,以前只存储全名。一个州名库被用来推导缩写,它包含一个二维数组,每个子数组都有一个全名和一个缩写(即 [['New York', 'NY']['Pennsylvania', 'PA'][etc]])。我将数据库中每条记录的州名称与该数组中的每个全文名称进行比较,然后在匹配时获取相应的兄弟数组单元格。

这段代码工作正常,并产生了正确的结果,但它看起来很简陋,不读很多行就不容易理解:

# For the following code, StatesWithNames is an Active Record model, which is
#  having a new column :code added to its table.
# Sates::USA represents a 2D Array as: [['StateName', 'NY']], and is used to 
#  populate the codes for StatesWithNames. 
# A comparison is made between StatesWithNames.name and the text name found in
#  States::USA, and if there is a match, the abbreviation from States::USA is
#  used
if StatesWithNames.any? 
  StatesWithNames.all.each do |named_state|
    if named_state.code.blank?
      States::USA.each do |s|
        if s[0] == named_state.name
          named_state.update_column(:code, s[1])
          break
        end
      end
    end
  end
end

像这样用逻辑表达赋值的最Ruby风格的方式是什么?我尝试了几个不同的 procs / blocks,但得到了更笨拙的表达,或者不正确的结果。有没有更简单的方法用更少的行来表达这个 and/or if-end 条件?

您可以为此使用不同的数据结构。

使用现有的二维数组,您可以对其调用 to_h 以获得 Hash 其中

a = [['California', 'CA'], ['Oregon', 'OR']].to_h
=> { 'California' => 'CA', 'Oregon' => 'OR' }

然后在你的代码中你可以做

state_hash = States::USA.to_h

if StatesWithNames.any? 
  StatesWithNames.all.each do |named_state|
    if named_state.code.blank?
      abbreviation = state_hash[named_state.name]
      if !abbreviation.nil?
        named_state.update_column(:code, abbreviation)
      end
    end
  end
end

是的,有一些 if 和检查是不需要的。

因为它是 Rails,即使它在问题的标签中没有说明,您可能想要使用 find_each,这是迭代 AR 集合的最有效方法之一:

  StatesWithNames.find_each do |named_state|
    next unless named_state.code.blank?
    States::USA.each do |s|
      named_state.update_column(:code, s[1]) if s[0] == named_state.name       
    end
  end

另请注意,update_column bypasses any validations, and if you wish to keep your objects valid, stick to update!。 最后一件事 - 将其全部包装在 transaction 中,因此如果一路出现任何问题 - 它会回滚任何更改。

    StatesWithNames.transaction do
      StatesWithNames.find_each do |named_state|
        next unless named_state.code.blank?
        States::USA.each do |s|
          named_state.update!(:code, s[1]) if s[0] == named_state.name       
        end
      end
    end

您要做的第一件事是将查找从数组数组转换为散列。

state_hash = States::USA.to_h

if StatesWithNames.any?
  StatesWithNames.all.select{|state| state.code.blank?}.each do |named_state|
    named_state.update_column(:code, state_hash[named_state.name]) if state_hash[named_state.name]
  end
end