【问题标题】: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', ...
  • “我正在使用循环和索引来创建哈希” - 您可能想分享您的代码。

标签: arrays ruby dictionary hash


【解决方案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_bywith_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]}
猜你喜欢
  • 2020-06-02
  • 1970-01-01
  • 2021-08-23
  • 1970-01-01
  • 2023-03-27
  • 2012-03-21
  • 1970-01-01
  • 2018-02-22
  • 1970-01-01
相关资源
最近更新 更多