我有一个具有许多属性的对象数组roads
。我想做的是找到哪个district_code
(一个属性)有多个state
(另一个属性)
为简单起见省略其他属性-例如:
roads = [['1004', 'VIC'], ['1004', 'BRI'], ['1011', 'VIC'], ['1010', 'ACT'], ['1000', 'ACT'], ['1019', 'VIC'], ['1004', 'VIC']]
如果我使用roads.group_by { |ro| ro[0] }
,我得到的结果:
=> {"1004"=>[["1004", "VIC"], ["1004", "BRI"], ["1004", "VIC"]], "1011"=>[["1011", "VIC"]], "1010"=>[["1010", "ACT"]], "1000"=>[["1000", "ACT"]], "1019"=>[["1019", "VIC"]]}
我想要的是哈希值只显示state
有多个唯一值的地方,像这样:
=> {"1004"=>["VIC", "BRI"]}
关于如何根据值的数量或值中的特定属性进行group_by或映射,有什么想法吗?
谢谢!
让我添加一个额外的元素roads
这样所需的散列将有多个关键。
roads = [['1004', 'VIC'], ['1004', 'BRI'], ['1011', 'VIC'],
['1010', 'ACT'], ['1000', 'ACT'], ['1019', 'VIC'],
['1004', 'VIC'], ['1000', 'VIC]]
require 'set'
roads.each_with_object({}) do |(dc,st),h|
h[dc] = Set.new unless h.key?(dc)
h[dc] << st
end.select {|k,v| v.size > 1}
.transform_values(&:to_a)
#=> {"1004"=>["VIC", "BRI"], "1000"=>["ACT", "VIC"]}
参见hash# select和hash# transform_values。
让我们看一下这些步骤。
f = roads.each_with_object({}) do |(dc,st),h|
h[dc] = Set.new unless h.key?(dc)
h[dc] << st
end
#=> {"1004"=>#<Set: {"VIC", "BRI"}>, "1011"=>#<Set: {"VIC"}>,
# "1010"=>#<Set: {"ACT"}>, "1000"=>#<Set: {"ACT", "VIC"}>,
# "1019"=>#<Set: {"VIC"}>}
将块变量写入|(dc,st),h|
使用了数组分解。这是一项强大的技术,可用于许多应用。
这里,假设我们写:
enum = roads.each_with_object({})
#=> #<Enumerator: [["1004", "VIC"], ["1004", "BRI"],...,
# ["1000", "VIC"]]:each_with_object({})>
当生成第一个元素并传递给块时,执行以下命令为块变量赋值。
(dc,st),h = enum.next
#=> [["1004", "VIC"], {}]
导致:
dc #=> "1004"
st #=>"VIC"
h #=> {}
看到枚举器#。本文是提供数组分解更全面解释的几篇文章之一。
在继续,
g = f.select {|k,v| v.size > 1}
#=> {"1004"=>#<Set: {"VIC", "BRI"}>, "1000"=>#<Set: {"ACT", "VIC"}>}
g.transform_values(&:to_a)
#=> {"1004"=>["VIC", "BRI"], "1000"=>["ACT", "VIC"]}
计算f
有两种常见的变体。第一个是
f = roads.each_with_object({}) do |(dc,st),h|
(h[dc] ||= Set.new) << st
end
h[dc] ||= Set.new
扩展为h[dc] = h[dc] || Set.new
。如果h
有一个密钥dc
,它就变成了h[dc] = h[dc]
;否则变成h[dc] = nil || Set.new = Set.new
。返回s
集合,然后执行s << st
。只要h
没有等于nil
的值,此操作就有效。
第二个变体是
roads.each_with_object(Hash.new {|h,k| h[k]=Set.new}) do |(dc,st),h|
h[dc] << st
end
这使用了Hash::new的形式,它接受一个块,不带参数。如果定义了h
h = Hash.new {|h,k| h[k]=Set.new}
则,可能在键值对添加之后,如果h
没有键k
h[k] = Set.new
执行。在这种情况下,当遇到h[dc] << st
,而h
没有dc
键时,h[k] = Set.new
在h[dc] << st
之前执行。
初始解的变体如下:
roads.each_with_object(Hash.new {|h,k| h[k]=[]}) {|(dc,st),h| h[dc] << st}
.transform_values(&:uniq)
.select {|k,v| v.size > 1}
输入
roads = [['1004', 'VIC'], ['1004', 'BRI'], ['1011', 'VIC'], ['1010', 'ACT'], ['1000', 'ACT'], ['1019', 'VIC'], ['1004', 'VIC']]
代码p Hash[roads.group_by(&:first)
.transform_values(&:uniq)
.filter_map { |k, v| [k, v.map(&:last)] if v.length > 1 }]
输出{"1004"=>["VIC", "BRI"]}
如果你已经可以得到:
{"1004"=>[["1004", "VIC"], ["1004", "BRI"], ["1004", "VIC"]], "1011"=>[["1011", "VIC"]], "1010"=>[["1010", "ACT"]], "1000"=>[["1000", "ACT"]], "1019"=>[["1019", "VIC"]]}
:
roads.group_by { |ro| ro[0] }
那么您只需要选择长度大于1的条目。
roads.group_by { |ro| ro[0] }.select { |k, v| v.length > 1 }
得到:
{"1004"=>[["1004", "VIC"], ["1004", "BRI"], ["1004", "VIC"]]}
然后我们可以把它映射到名字。可以是一行,但为了演示而分开。
roads.group_by { |r| r[0] }
.select { |k, v| v.length > 1 }
.map { |k, v| [k, v.map { |x| x[1] }] }
.to_h
结果是:
{"1004"=>["VIC", "BRI", "VIC"]}