按数组属性元素对对象组进行分组

时间:2015-09-01 20:19:36

标签: ruby

我有这一系列的城市:

footer

名称,州和州际公路数组是每个城市的属性。

我想通过州际公路对它们进行分组,以便最终结果看起来像这样:

Bakersfield, California
Interstates: ["I-5"]

Oakland, California
Interstates: ["I-80"]

Atlanta, Georgia
Interstates: ["I-20", "I-75", "I-86"]

Cleveland, Ohio
Interstates: ["I-71", "I-77", "I-80", "I-90"]

Arlington, Texas
Interstates: ["I-20", "I-30"]

有快速的方法吗?

编辑:这是真正的数组,就像@wurde一样。

I-20: [Arlington, Atlanta]
I-5: [Bakersfield]
I-86: [Atlanta]
...

5 个答案:

答案 0 :(得分:1)

试试这个

mapping = {}

cities.each do |city|
    city.interstates.each do |interstate|
      mapping[interstate] ||= []
      mapping[interstate] << city
    end
end

答案 1 :(得分:1)

假设假设@ wurde的Hash结构为真,我会做这样的事情

cities = {
  'Bakersfield' => {
    state: 'California',
    interstate: ['I-5']
  },
  'Oakland' => {
    state: 'California',
    interstate: ['I-80']
  },
  'Atlanta' => {
    state: 'Georgia',
    interstate: ["I-20", "I-75", "I-86"]
  },
  'Cleveland' => {
    state: 'Ohio',
    interstate: ["I-71", "I-77", "I-80", "I-90"]
  },
  'Arlington' => {
    state: 'Texas',
    interstate: ["I-20", "I-30"]
  }
}


cities.each_with_object(Hash.new {|h,k| h[k] = []}) do |(city_name,data),h|
  data[:interstate].each do |interstate|
    h[interstate] << "#{city_name}, #{data[:state]}"
  end
end
#=> {"I-5"=>["Bakersfield, California"], 
     "I-80"=>["Oakland, California", "Cleveland, Ohio"], 
     "I-20"=>["Atlanta, Georgia", "Arlington, Texas"], 
     "I-75"=>["Atlanta, Georgia"], 
     "I-86"=>["Atlanta, Georgia"], 
     "I-71"=>["Cleveland, Ohio"], 
     "I-77"=>["Cleveland, Ohio"], 
     "I-90"=>["Cleveland, Ohio"], 
     "I-30"=>["Arlington, Texas"]}

答案 2 :(得分:0)

cities = {
  'Bakersfield' => {
    state: 'California',
    interstate: ['I-5']
  },
  'Oakland' => {
    state: 'California',
    interstate: ['I-80']
  },
  'Atlanta' => {
    state: 'Georgia',
    interstate: ["I-20", "I-75", "I-86"]
  },
  'Cleveland' => {
    state: 'Ohio',
    interstate: ["I-71", "I-77", "I-80", "I-90"]
  },
  'Arlington' => {
    state: 'Texas',
    interstate: ["I-20", "I-30"]
  }
}

interstates = {}
cities.each do |city|
  city[1][:interstate].each do |road|
    interstates[road] ||= []
    interstates[road] << city[0]
  end
end

puts interstates.inspect
#=> {
#=>   "I-5"=>["Bakersfield"],
#=>   "I-80"=>["Oakland", "Cleveland"],
#=>   "I-20"=>["Atlanta", "Arlington"],
#=>   "I-75"=>["Atlanta"],
#=>   "I-86"=>["Atlanta"],
#=>   "I-71"=>["Cleveland"],
#=>   "I-77"=>["Cleveland"],
#=>   "I-90"=>["Cleveland"],
#=>   "I-30"=>["Arlington"]
#=> }

答案 3 :(得分:0)

我们有:

cities = {
  'Bakersfield' => ['I-5'],
  'Oakland' => ['I-80'],
  'Atlanta' => ["I-20", "I-75", "I-86"],
  'Cleveland' => ["I-71", "I-77", "I-80", "I-90"],
  'Arlington' => ["I-20", "I-30"]
}

更长的版本我们可以通过此操作获得您想要的内容:

require 'set'
interstates = cities.inject(Set.new){|all,item| all+item[1]} # => #<Set: {"I-5", "I-80", "I-20", "I-75", "I-86", "I-71", "I-77", "I-90", "I-30"}>
result = interstates.map{|inter| [inter, cities.select{|_,interstates| interstates.include?(inter)}.keys]}.to_h # => {"I-5"=>["Bakersfield"], "I-80"=>["Oakland", "Cleveland"], "I-20"=>["Atlanta", "Arlington"], "I-75"=>["Atlanta"], "I-86"=>["Atlanta"], "I-71"=>["Cleveland"], "I-77"=>["Cleveland"], "I-90"=>["Cleveland"], "I-30"=>["Arlington"]}

我们使用Set因为它在给定设置中有效(更优雅的语法,请参阅下面的注释)。在第2行,我们使用interstates的{​​{1}}方法获取所有Enumerable。最终结果在第3行获得,我们使用映射和过滤(injectmap方法)。注意,Ruby 2.1提供了select方法。如果您使用较旧的Ruby,则可以使用to_h将映射的数组转换为Hash

单行它甚至可以写成一行代码:

Hash[array]

注意我在此处添加了cities.inject(Set.new){|all,item| all+item[1]}.map{|inter| [inter, cities.select{|_,interstates| interstates.include?(inter)}.keys]}.to_h 以提高性能。如果您更喜欢可读性,那么只需编写Set就可以了,如@engineersmnky在评论中所述。

答案 4 :(得分:0)

Using @wurde's example (thank you, thank you, thank you):

cities.each_with_object({}) do |(k,g),h|
  g[:interstate].each { |i| h.update(i=>[k]) { |_,o,n| o+n } }
end
  #=> {"I-5" =>["Bakersfield"],
  #    "I-80"=>["Oakland", "Cleveland"],
  #    "I-20"=>["Atlanta", "Arlington"],
  #    "I-75"=>["Atlanta"],
  #    "I-86"=>["Atlanta"],
  #    "I-71"=>["Cleveland"],
  #    "I-77"=>["Cleveland"],
  #    "I-90"=>["Cleveland"],
  #    "I-30"=>["Arlington"]} 

This uses the form of Hash#update (aka merge!) that uses the block:

{ |_,o,n| o+n }

to determine values of keys that are present in both hashes being merged. The key is the intestate (written _ because it is not be used), o is an array of cities for the key in the hash h being constructed and n is an array of a single city for the merging hash { i=>[k] }.