将数组 and/or 标量的散列转换为标量组合数组的数组
Transform hash of arrays and/or scalars into an array of arrays of combinations of scalars
我觉得这对某些人来说一定是个简单的问题,而我花了惊人的时间试图找到一个解决方案,但找不到我喜欢的解决方案。
我不会尝试用语言说出我需要什么,只是给出一些示例输入及其预期输出作为 Rspec 代码:
方法是:
def explode(hash)
...
end
规格:
describe '#explode' do
it do
expect(explode({:a => 1, :b => 2})).
to eq [[:a, 1, :b, 2]]
end
it do
expect(explode({:a => 1, :b => [2,3,4]})).
to eq [
[:a, 1, :b, 2],
[:a, 1, :b, 3],
[:a, 1, :b, 4]
]
end
it do
expect(explode({:a => [1,2], :b => [3,4]})).
to eq [
[:a, 1, :b, 3],
[:a, 2, :b, 3],
[:a, 1, :b, 4],
[:a, 2, :b, 4]
]
end
it do
expect(explode({:a => 1, :b => [2,3], :c => [4,5,6]})).
to eq [
[:a, 1, :b, 2, :c, 4],
[:a, 1, :b, 3, :c, 4],
[:a, 1, :b, 2, :c, 5],
[:a, 1, :b, 3, :c, 5],
[:a, 1, :b, 2, :c, 6],
[:a, 1, :b, 3, :c, 6]
]
end
end
也欢迎使用 Ruby 以外语言的解决方案。
Array#product 似乎很适合这里。
h1 = {:a => 1, :b => 2}
h2 = {:a => 1, :b => [2,3,4]}
h3 = {:a => [1,2], :b => [3,4]}
h4 = {:a => 1, :b => [2,3], :c => [4,5,6]}
def explode hash
a, *b = hash.transform_values { |v| [*v] }.values.unshift
a.product(*b).map { |ar| hash.keys.zip(ar).flatten }.sort_by(&:last)
end
p explode h1
#[[:a, 1, :b, 2]]
p explode h2
#[[:a, 1, :b, 2],
# [:a, 1, :b, 3],
# [:a, 1, :b, 4]]
p explode h3
#[[:a, 1, :b, 3],
# [:a, 2, :b, 3],
# [:a, 1, :b, 4],
# [:a, 2, :b, 4]]
p explode h4
#[[:a, 1, :b, 2, :c, 4],
# [:a, 1, :b, 3, :c, 4],
# [:a, 1, :b, 2, :c, 5],
# [:a, 1, :b, 3, :c, 5],
# [:a, 1, :b, 2, :c, 6],
# [:a, 1, :b, 3, :c, 6]]
为了使我的方法起作用,我必须重新映射值,以便它们都是数组,这并不理想。但我仍然发布了这个答案,因为它可能会给你或其他人一个起点。
因为我必须让它在 Ruby < 2.4(没有 transform_values)的情况下工作 - 而且,因为我不需要对数组进行排序,所以我最终得到了:
def explode(hash)
hash.each do |k,v|
if not hash[k].is_a?(Array)
hash[k] = [hash[k]]
end
end
a, *b = hash.values.unshift
a.product(*b).map do |arr|
hash.keys.zip(arr).flatten
end
end
您可以使用 Array#product 两次。
def explode(hash)
first, *rest = hash.map { |k,v| [k].product([*v]) }
first.product(*rest).map(&:flatten)
end
h = { :a =>1, :b =>[2,3], :c =>[4,5,6] }
explode h
#=> [[:a, 1, :b, 2, :c, 4], [:a, 1, :b, 2, :c, 5], [:a, 1, :b, 2, :c, 6],
# [:a, 1, :b, 3, :c, 4], [:a, 1, :b, 3, :c, 5], [:a, 1, :b, 3, :c, 6]]
请注意,对于上面的 h
,
first, *rest = h.map { |k,v| [k].product([*v]) }
#=> [[[:a, 1]], [[:b, 2], [:b, 3]], [[:c, 4], [:c, 5], [:c, 6]]]
first
#=> [[:a, 1]]
rest
#=> [[[:b, 2], [:b, 3]], [[:c, 4], [:c, 5], [:c, 6]]]
和
first.product(*rest)
#=> [[[:a, 1], [:b, 2], [:c, 4]], [[:a, 1], [:b, 2], [:c, 5]],
# [[:a, 1], [:b, 2], [:c, 6]], [[:a, 1], [:b, 3], [:c, 4]],
# [[:a, 1], [:b, 3], [:c, 5]], [[:a, 1], [:b, 3], [:c, 6]]]
观察到 [*1] #=> [1]
、[*:a] #=> [:a]
和 [*[1,2]] #=> [1,2]
,这意味着 [*k]
将标量 k
转换为包含该元素的数组,并且 [*k]
等于 k
如果 k
是一个数组。
我觉得这对某些人来说一定是个简单的问题,而我花了惊人的时间试图找到一个解决方案,但找不到我喜欢的解决方案。
我不会尝试用语言说出我需要什么,只是给出一些示例输入及其预期输出作为 Rspec 代码:
方法是:
def explode(hash)
...
end
规格:
describe '#explode' do
it do
expect(explode({:a => 1, :b => 2})).
to eq [[:a, 1, :b, 2]]
end
it do
expect(explode({:a => 1, :b => [2,3,4]})).
to eq [
[:a, 1, :b, 2],
[:a, 1, :b, 3],
[:a, 1, :b, 4]
]
end
it do
expect(explode({:a => [1,2], :b => [3,4]})).
to eq [
[:a, 1, :b, 3],
[:a, 2, :b, 3],
[:a, 1, :b, 4],
[:a, 2, :b, 4]
]
end
it do
expect(explode({:a => 1, :b => [2,3], :c => [4,5,6]})).
to eq [
[:a, 1, :b, 2, :c, 4],
[:a, 1, :b, 3, :c, 4],
[:a, 1, :b, 2, :c, 5],
[:a, 1, :b, 3, :c, 5],
[:a, 1, :b, 2, :c, 6],
[:a, 1, :b, 3, :c, 6]
]
end
end
也欢迎使用 Ruby 以外语言的解决方案。
Array#product 似乎很适合这里。
h1 = {:a => 1, :b => 2}
h2 = {:a => 1, :b => [2,3,4]}
h3 = {:a => [1,2], :b => [3,4]}
h4 = {:a => 1, :b => [2,3], :c => [4,5,6]}
def explode hash
a, *b = hash.transform_values { |v| [*v] }.values.unshift
a.product(*b).map { |ar| hash.keys.zip(ar).flatten }.sort_by(&:last)
end
p explode h1
#[[:a, 1, :b, 2]]
p explode h2
#[[:a, 1, :b, 2],
# [:a, 1, :b, 3],
# [:a, 1, :b, 4]]
p explode h3
#[[:a, 1, :b, 3],
# [:a, 2, :b, 3],
# [:a, 1, :b, 4],
# [:a, 2, :b, 4]]
p explode h4
#[[:a, 1, :b, 2, :c, 4],
# [:a, 1, :b, 3, :c, 4],
# [:a, 1, :b, 2, :c, 5],
# [:a, 1, :b, 3, :c, 5],
# [:a, 1, :b, 2, :c, 6],
# [:a, 1, :b, 3, :c, 6]]
为了使我的方法起作用,我必须重新映射值,以便它们都是数组,这并不理想。但我仍然发布了这个答案,因为它可能会给你或其他人一个起点。
因为我必须让它在 Ruby < 2.4(没有 transform_values)的情况下工作 - 而且,因为我不需要对数组进行排序,所以我最终得到了:
def explode(hash)
hash.each do |k,v|
if not hash[k].is_a?(Array)
hash[k] = [hash[k]]
end
end
a, *b = hash.values.unshift
a.product(*b).map do |arr|
hash.keys.zip(arr).flatten
end
end
您可以使用 Array#product 两次。
def explode(hash)
first, *rest = hash.map { |k,v| [k].product([*v]) }
first.product(*rest).map(&:flatten)
end
h = { :a =>1, :b =>[2,3], :c =>[4,5,6] }
explode h
#=> [[:a, 1, :b, 2, :c, 4], [:a, 1, :b, 2, :c, 5], [:a, 1, :b, 2, :c, 6],
# [:a, 1, :b, 3, :c, 4], [:a, 1, :b, 3, :c, 5], [:a, 1, :b, 3, :c, 6]]
请注意,对于上面的 h
,
first, *rest = h.map { |k,v| [k].product([*v]) }
#=> [[[:a, 1]], [[:b, 2], [:b, 3]], [[:c, 4], [:c, 5], [:c, 6]]]
first
#=> [[:a, 1]]
rest
#=> [[[:b, 2], [:b, 3]], [[:c, 4], [:c, 5], [:c, 6]]]
和
first.product(*rest)
#=> [[[:a, 1], [:b, 2], [:c, 4]], [[:a, 1], [:b, 2], [:c, 5]],
# [[:a, 1], [:b, 2], [:c, 6]], [[:a, 1], [:b, 3], [:c, 4]],
# [[:a, 1], [:b, 3], [:c, 5]], [[:a, 1], [:b, 3], [:c, 6]]]
观察到 [*1] #=> [1]
、[*:a] #=> [:a]
和 [*[1,2]] #=> [1,2]
,这意味着 [*k]
将标量 k
转换为包含该元素的数组,并且 [*k]
等于 k
如果 k
是一个数组。