按索引映射两个数组
Posted
技术标签:
【中文标题】按索引映射两个数组【英文标题】:Map two arrays by index 【发布时间】:2021-12-28 00:15:51 【问题描述】:我有以下数组:
arr1 = [1, 2, 3, 4]
arr2 = ['a', 'b', 'a', 'c']
我想要以下输出:
'a' => [1, 3], 'b'=> [2], 'c' => [4]
在 Ruby 中是否有一种简单的方法可以做到这一点?目前,我正在使用循环和索引来创建哈希。
【问题讨论】:
对不起,我所说的索引是指 arr1 中给定索引处的元素应该映射到 arr2 中相同给定索引处的元素(即 a=>1,b=>2,c =>4)arr2.zip(arr1).group_by(&:shift).tranform_values(&:flatten)
这正是我想要的,谢谢!
您的arr2
包含变量 a
, b
, ...,但您想要的输出包含字符串 'a'
, 'b'
,...
我已根据预期输出将a, b, ...
更改为'a', 'b', ...
。
“我正在使用循环和索引来创建哈希” - 您可能想分享您的代码。
【参考方案1】:
你可以写以下。
arr1 = [1, 2, 3, 4]
arr2 = ['a', 'b', 'a', 'c']
arr1.zip(arr2).each_with_object(Hash.new |h,k| h[k] = [] ) do |(n,c),h|
h[c] << n
end
#=> "a"=>[1, 3], "b"=>[2], "c"=>[4]
让我从一个简单的过程方法开始解释这个表达式,然后通过几个步骤来改进代码。
首先创建一个空哈希,它将成为您想要的返回值:
h =
我们可以这样写
(0..arr1.size - 1).each do |i|
n = arr1[i]
c = arr2[i]
h[c] = [] unless h.key?(c)
h[c] << n
end
h #=>"a"=>[1, 3], "b"=>[2], "c"=>[4]
它更像 Ruby,不过它可以迭代来自 arr1
和 arr2
的对应值对,即 [1, 'a']
、[2, 'b']
等。为此我们使用Array#zip的方法:
pairs = arr1.zip(arr2)
#=> [[1, "a"], [2, "b"], [3, "a"], [4, "c"]]
然后
h =
pairs.each do |pair|
n = pair.first
c = pair.last
h[c] = [] unless h.key?(c)
h[c] << n
end
h #=> "a"=>[1, 3], "b"=>[2], "c"=>[4]
我们可以做的一个小改进是将array decomposition 应用于pair
:
h =
pairs.each do |n,c|
h[c] = [] unless h.key?(c)
h[c] << n
end
h #=> "a"=>[1, 3], "b"=>[2], "c"=>[4]
接下来的改进是将each
替换为Enumerable#each_with_object,以避免需要h =
开头和h
结尾:
pairs.each_with_object() do |(n,c),h|
h[c] = [] unless h.key?(c)
h[c] << n
end
#=> "a"=>[1, 3], "b"=>[2], "c"=>[4]
注意我是如何编写块变量的,h
持有返回的 object(最初为空的哈希)。这是数组分解的另一种用法。有关该主题的更多信息,请参阅this 文章。
前面的表达式很好,读起来也很好,但是经常看到下面的调整:
pairs.each_with_object() do |(n,c),h|
(h[c] ||= []) << n
end
#=> "a"=>[1, 3], "b"=>[2], "c"=>[4]
如果h
没有密钥c
,h[c]
返回nil
,所以h[c] ||= []
,或者h[c] = h[c] || []
,变成h[c] = nil || []
,所以h[c] = []
,之后h[c] << n
执行。
没有比前面的表达更好或更差,你可能还看到我在开头提供的代码:
arr1.zip(arr2).each_with_object(Hash.new |h,k| h[k] = [] ) do |(n,c),h|
h[c] << n
end
这里块变量h
被初始化为定义的空哈希
h = Hash.new |h,k| h[k] = []
这采用了Hash::new 的形式,它接受一个块并且没有参数。当这样定义一个哈希h
时,如果h
没有keyc
,执行h[c]
会导致h[c] = []
在h[c] << n
执行之前被执行。
【讨论】:
一如既往地彻底,但我认为您会真正欣赏竞争帖子的优雅。 @engineersmnky,感谢您的提醒。【参考方案2】:假设你的意思
arr1 = [1, 2, 3, 4]
arr2 = %w[a b a c] # ["a", "b", "a", "d"]
所以你的第二个数组是一个字符串数组而不是变量
您可以使用group_by 和with_index 枚举器来指向您的变量索引并使用第二个数组对其进行分组
arr1.group_by.with_index |_, index| arr2[index]
【讨论】:
group_by
返回的枚举器的出色使用从未考虑过这一点。这是最干净和最有创意的答案(以至于我什至在帖子下删除了我的 cmets)唯一的建议是 Enumerable#with_index
而不是 each_with_index
谢谢@engineersmnky,我不知道枚举器有那些“别名方法”,听起来更干净!!!
不幸的是,Enumerator
/ Enumerable
没有 each_index
- 它会进一步缩短您的代码。
@saviu-u 我喜欢with_index
,因为您可以指定each_with_index
不支持的“开始”。例如arr2.each_with_index.to_a #=> [["a",0],["b",1],["a",2],["d",3]
但arr2.each.with_index(12).to_a #=> [["a",12],["b",13],["a",14],["d",15]
@Stefan 从 2.7 开始,我们可以使用 with_index arr2[_2]
以上是关于按索引映射两个数组的主要内容,如果未能解决你的问题,请参考以下文章