按索引映射两个数组

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,不过它可以迭代来自 arr1arr2 的对应值对,即 [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没有密钥ch[c]返回nil,所以h[c] ||= [],或者h[c] = h[c] || [],变成h[c] = nil || [],所以h[c] = [],之后h[c] &lt;&lt; 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] &lt;&lt; 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 #=&gt; [["a",0],["b",1],["a",2],["d",3]arr2.each.with_index(12).to_a #=&gt; [["a",12],["b",13],["a",14],["d",15] @Stefan 从 2.7 开始,我们可以使用 with_index arr2[_2]

以上是关于按索引映射两个数组的主要内容,如果未能解决你的问题,请参考以下文章

如何按相同的索引对两个数组进行排序?

将二维数组映射到一维数组

创建按顺序索引的有序映射

我正在尝试映射一个数组并在数组中找到对象的索引

如何在反应中映射索引范围?

如何通过敲除映射(ko.utils)向数组中的每个对象添加新属性(索引)?