从 Ruby 中的嵌套哈希和数组中提取值
Extracting value from nested hashes and arrays in Ruby
我有一个看起来像这样的散列:
h = {
a: [ ["c", "1"],["d","2"],["e","3"],["f","4"] ],
b: [ ["g","5"],["h","6"],["i","7"],["j","8"] ],
c: [ ["k","9"],["l","10"],["m","11"],["n","12"] ]
}
从中提取数字以使其看起来像这样的最佳方法是什么?
[1,2,3,4,5,6,7,8,9,10,11,12]
我尝试了一些不同的方法,但它总是需要一个外部数组,我必须从 each
命令链中将其推入。
对数字 \d
使用 flatten
、select
和 regexp
的组合:
=> a = {
a: [ ["c", "1"],["d","2"],["e","3"],["f","4"] ],
b: [ ["g","5"],["h","6"],["i","7"],["j","8"] ],
c: [ ["k","9"],["l","10"],["m","11"],["n","12"] ]
}
=> a.values.flatten.select { |x| x =~ /\d/ }.map(&:to_i)
#> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
其他方式 flat_map
、map
和 ( )
:
reach inside the structure with parentheses to make things more explicit
=> a = {
a: [ ["c", "1"],["d","2"],["e","3"],["f","4"] ],
b: [ ["g","5"],["h","6"],["i","7"],["j","8"] ],
c: [ ["k","9"],["l","10"],["m","11"],["n","12"] ]
}
=> a.flat_map { |_, (n, z, i, x)| [n, z, i, x] }.map { |_, i| i.to_i }
#> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
这可以用正则表达式来完成。
hash.values.flatten.select { |v| v.match(/\d/) }.map(&:to_i)
要获取值,请使用 values
方法
要使数组成为一维数组,请使用flatten
方法。
要过滤,请使用 select
方法,并找到与数字正则表达式匹配的字符串。
最后映射这个数组,将元素转换为整数。
代码
def pull_numbers(h)
h.values.flat_map { |a| a.map { |_,e| Integer(e) } }
end
例子
您的哈希,h[:a][0][0]
稍作修改:
h = {
a: [["8c", "1"],["d","2"],["e","3"],["f","4"]],
b: [["g","5"],["h","6"],["i","7"],["j","8"]],
c: [["k","9"],["l","10"],["m","11"],["n","12"]]
}
pull_numbers(h)
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
说明
步骤,如上例:
c = h.values
#=> [[["8c", "1"], ["d", "2"], ["e", "3"], ["f", "4"]],
# [["g", "5"], ["h", "6"], ["i", "7"], ["j", "8"]],
# [["k", "9"], ["l", "10"], ["m", "11"], ["n", "12"]]]
Enumerable#flat_map 传递 c
的第一个元素并设置块变量 a
:
a = [["8c", "1"],["d","2"],["e","3"],["f","4"]]
然后:
a.map { |_,e| Integer(e) }
#=> [1, 2, 3, 4]
我选择使用 Integer(e)
而不是 e.to_i
,以便在 e
不是整数的字符串表示时引发异常:
Integer("cat")
#=> ArgumentError: invalid value for Integer(): "cat"
鉴于:
"cat".to_i
#=> 0
实际上,Integer
在进行转换之前执行数据检查。
c
的其他两个元素处理类似。
变体
可以这样写:
def pull_numbers(h)
h.values.flatten.each_slice(2).map { |_,e| Integer(e) }
end
我会做:
h.values.flatten(1).map{|x,y| y.to_i }
这是一种方法:
h = {
a: [ ["c", "1"],["d","2"],["e","3"],["f","4"] ],
b: [ ["g","5"],["h","6"],["i","7"],["j","8"] ],
c: [ ["k","9"],["l","10"],["m","11"],["n","12"] ]
}
h.values.flatten(1).collect(&:last).map(&:to_i)
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
或者您可以这样做:
h.to_a.flatten.select { |x| x =~ /\d/ }.map(&:to_i)
使用 Array#transpose
方法的简短替代方法:
> h.values.flatten(1).transpose.last
=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"]
# with to number conversion
> h.values.flatten(1).transpose.last.map(&:to_i)
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
基准测试
require 'benchmark'
h = {
a: [ ["c", "1"],["d","2"],["e","3"],["f","4"] ],
b: [ ["g","5"],["h","6"],["i","7"],["j","8"] ],
c: [ ["k","9"],["l","10"],["m","11"],["n","12"] ]
}
Benchmark.bm(10) do |x|
x.report("transpose") do
1000.times { h.values.flatten(1).transpose.last.map(&:to_i) }
end
x.report("collect/map") do
1000.times { h.values.flatten(1).collect(&:last).map(&:to_i) }
end
x.report("regexp") do
1000.times { h.values.flatten.select { |v| v.match(/\d/) }.map(&:to_i) }
end
x.report("Integer") do
1000.times { h.values.flat_map { |a| a.map { |_,e| Integer(e) } } }
end
end
结果
user system total real
transpose 0.000000 0.000000 0.000000 ( 0.006971)
collect/map 0.010000 0.000000 0.010000 ( 0.007490)
regexp 0.030000 0.010000 0.040000 ( 0.031939)
Integer 0.010000 0.000000 0.010000 ( 0.006832)
我有一个看起来像这样的散列:
h = {
a: [ ["c", "1"],["d","2"],["e","3"],["f","4"] ],
b: [ ["g","5"],["h","6"],["i","7"],["j","8"] ],
c: [ ["k","9"],["l","10"],["m","11"],["n","12"] ]
}
从中提取数字以使其看起来像这样的最佳方法是什么?
[1,2,3,4,5,6,7,8,9,10,11,12]
我尝试了一些不同的方法,但它总是需要一个外部数组,我必须从 each
命令链中将其推入。
对数字 \d
使用 flatten
、select
和 regexp
的组合:
=> a = {
a: [ ["c", "1"],["d","2"],["e","3"],["f","4"] ],
b: [ ["g","5"],["h","6"],["i","7"],["j","8"] ],
c: [ ["k","9"],["l","10"],["m","11"],["n","12"] ]
}
=> a.values.flatten.select { |x| x =~ /\d/ }.map(&:to_i)
#> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
其他方式 flat_map
、map
和 ( )
:
reach inside the structure with parentheses to make things more explicit
=> a = {
a: [ ["c", "1"],["d","2"],["e","3"],["f","4"] ],
b: [ ["g","5"],["h","6"],["i","7"],["j","8"] ],
c: [ ["k","9"],["l","10"],["m","11"],["n","12"] ]
}
=> a.flat_map { |_, (n, z, i, x)| [n, z, i, x] }.map { |_, i| i.to_i }
#> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
这可以用正则表达式来完成。
hash.values.flatten.select { |v| v.match(/\d/) }.map(&:to_i)
要获取值,请使用
values
方法要使数组成为一维数组,请使用
flatten
方法。要过滤,请使用
select
方法,并找到与数字正则表达式匹配的字符串。最后映射这个数组,将元素转换为整数。
代码
def pull_numbers(h)
h.values.flat_map { |a| a.map { |_,e| Integer(e) } }
end
例子
您的哈希,h[:a][0][0]
稍作修改:
h = {
a: [["8c", "1"],["d","2"],["e","3"],["f","4"]],
b: [["g","5"],["h","6"],["i","7"],["j","8"]],
c: [["k","9"],["l","10"],["m","11"],["n","12"]]
}
pull_numbers(h)
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
说明
步骤,如上例:
c = h.values
#=> [[["8c", "1"], ["d", "2"], ["e", "3"], ["f", "4"]],
# [["g", "5"], ["h", "6"], ["i", "7"], ["j", "8"]],
# [["k", "9"], ["l", "10"], ["m", "11"], ["n", "12"]]]
Enumerable#flat_map 传递 c
的第一个元素并设置块变量 a
:
a = [["8c", "1"],["d","2"],["e","3"],["f","4"]]
然后:
a.map { |_,e| Integer(e) }
#=> [1, 2, 3, 4]
我选择使用 Integer(e)
而不是 e.to_i
,以便在 e
不是整数的字符串表示时引发异常:
Integer("cat")
#=> ArgumentError: invalid value for Integer(): "cat"
鉴于:
"cat".to_i
#=> 0
实际上,Integer
在进行转换之前执行数据检查。
c
的其他两个元素处理类似。
变体
可以这样写:
def pull_numbers(h)
h.values.flatten.each_slice(2).map { |_,e| Integer(e) }
end
我会做:
h.values.flatten(1).map{|x,y| y.to_i }
这是一种方法:
h = {
a: [ ["c", "1"],["d","2"],["e","3"],["f","4"] ],
b: [ ["g","5"],["h","6"],["i","7"],["j","8"] ],
c: [ ["k","9"],["l","10"],["m","11"],["n","12"] ]
}
h.values.flatten(1).collect(&:last).map(&:to_i)
#=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
或者您可以这样做:
h.to_a.flatten.select { |x| x =~ /\d/ }.map(&:to_i)
使用 Array#transpose
方法的简短替代方法:
> h.values.flatten(1).transpose.last
=> ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"]
# with to number conversion
> h.values.flatten(1).transpose.last.map(&:to_i)
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
基准测试
require 'benchmark'
h = {
a: [ ["c", "1"],["d","2"],["e","3"],["f","4"] ],
b: [ ["g","5"],["h","6"],["i","7"],["j","8"] ],
c: [ ["k","9"],["l","10"],["m","11"],["n","12"] ]
}
Benchmark.bm(10) do |x|
x.report("transpose") do
1000.times { h.values.flatten(1).transpose.last.map(&:to_i) }
end
x.report("collect/map") do
1000.times { h.values.flatten(1).collect(&:last).map(&:to_i) }
end
x.report("regexp") do
1000.times { h.values.flatten.select { |v| v.match(/\d/) }.map(&:to_i) }
end
x.report("Integer") do
1000.times { h.values.flat_map { |a| a.map { |_,e| Integer(e) } } }
end
end
结果
user system total real
transpose 0.000000 0.000000 0.000000 ( 0.006971)
collect/map 0.010000 0.000000 0.010000 ( 0.007490)
regexp 0.030000 0.010000 0.040000 ( 0.031939)
Integer 0.010000 0.000000 0.010000 ( 0.006832)