将项目与项目列表匹配

时间:2011-09-23 20:07:28

标签: ruby list find

假设我想将国家分为几个区域。例如,西南地区可能是:德克萨斯州,俄克拉荷马州,科罗拉多州,新墨西哥州,犹他州,亚利桑那州,内华达州。

不知何故,我需要创建状态列表并定义区域分组。我还需要能够查找给定状态名称的区域,例如region_for('Texas'),它将返回'Southwest'

最好,最干净,“Ruby Way”做这样的事情是什么?我想使用普通的'ol ruby​​,没有数据库或框架。

5 个答案:

答案 0 :(得分:1)

'纯'ruby方式只是使用哈希,然后有键来进行查找。有一种宝石可以做这样的事情:ruport。查看源代码可能是值得的。对于您已经说明的用例,我有类似的内容:

class RegionMapper
  #potentially put this in a config file 
  REGIONS = Hash[[['California', 'Southwest'], ...]]

  def initialize
    @region_map = REGIONS.inject({}) {|r, e| r[e.second] ||= []; r[e.second] << e.first; r}
  end 

  def region_for_state(state)
    REGIONS[state]
  end 
  def states_for_region(region) 
    @region_map(region)
  end 
end 

关键是,为了提高效率,您希望使用哈希来对要搜索的每个键执行查找。但是你不想公开数据复制,所以你把它全部放在一个类中。

如果你有多个值/键,那么你真的有一张表。如果你想保持恒定的时间查找,那么你为每一列构建一个哈希(比如@region_map)

答案 1 :(得分:1)

你几乎可以直接在Ruby中输入这个数据结构......

result = {
  'Southwest' => %W{Texas Oklahoma Colorado New\ Mexico Utah Arizona Nevada},
  'West'      => %W{California Oregon Washington},
}.inject({}) do |m, (k, v)|
  m[k] = v
  v.each { |s| m[s] = k }
  m
end

这会产生一个Hash,它既有状态又有区域作为彼此识别的键。数据结构类似于:

{"Colorado"   => "Southwest",
 "New Mexico" => "Southwest",
 "Oklahoma"   => "Southwest",
 "California" => "West",
 "Oregon"     => "West",
 "Texas"      => "Southwest",
 "Washington" => "West",
 "Utah"       => "Southwest",
 "Nevada"     => "Southwest",
 "Arizona"    => "Southwest"
 "Southwest"  => 
   ["Texas", "Oklahoma", "Colorado", "New Mexico", "Utah", "Arizona", "Nevada"],
 "West" => 
   ["California", "Oregon", "Washington"],
}

另一种方法是为状态创建单独的哈希。然后,您可以使用Hash#keys获取区域或州的列表,但您也可以根据值的类型使用Enumerable#selectEnumerable#reject来执行此操作。

答案 2 :(得分:0)

尝试:

class State
  attr_accessor :name, :region

  def initialize(name, region=nil)
    @name   = name
    @region = region
  end
end

class Region
  attr_accessor :name, :states

  def initialize(name, states)
    @name   = name
    @states = states
  end

  def set_state_regions
    self.states.each {|state| state.region = self.name}
  end
end

mo = State.new("missouri")
il = State.new("illionois")
oh = State.new("ohio")

midwest = Region.new("midwest", [mo, il, oh])
midwest.states.each {|state| puts state.name}
midwest.set_state_regions

我可能会稍后回过头来反思,我认为它违反了一些OO原则。

答案 3 :(得分:0)

我建立了一个非常相似的答案,如Caley

主要区别:我将数据存储在yaml结构中。

require 'yaml'

class Region
  @@all = {}
  def self.[](key)
    @@all[key]
  end
  def initialize(name)
    @name = name
    @states = []
    @@all[@name] = self
  end
  def <<(state)
    @states << state
    state.region = state
  end
  def each_state
    @states.each{|state| yield state } if block_given?
    @states
  end
  attr_reader :name
end

class State
  @@all = {}
  def self.[](key)
    @@all[key]
  end
  def initialize(name, region = nil)
    @name = name
    @region = region
    @@all[@name] = self
  end
  attr_accessor :name
  attr_accessor :region
end

YAML.load(DATA).each{|region,states|
  r = Region.new(region)
  states.each{|state| r << State.new(state) }
}

p Region['Southwest Region']
p Region['Southwest Region'].each_state
Region['Southwest Region'].each_state{|state|
  p state.name
}


__END__
Southwest Region:
- Texas
- Oklahoma
- Colorado
- New Mexico
- Utah
- Arizona
- Nevada.
Pacific:
- California
- Oregon
- Washington

答案 4 :(得分:0)

Hash很好,你不需要任何更好的东西。

region = {
  "Maine" => "New England",
  "New Hampshire" => "New England",
  etc
}

然后使用

region["Maine"]

或者如果你想更紧凑地设置它,就像这样:

regions = {
  "Southwest" => ["Texas", "Oklahoma", "Colorado", "New Mexico", "Utah", "Arizona", "Nevada"],
  "New England" => ["Maine", "New Hampshire", "Vermont", "Massachusetts","Rhode Island", "Connecticut"],
  etc
}
region = {}
regions.each do |r,states|
  states.each do |state|
    region[state] = r
  end
end