在 Ruby 中识别和更新具有索引值的数组中的重复项

Identify and Update Duplicates in Array with Index Value in Ruby

提问人:ToddT 提问时间:5/12/2023 最后编辑:anothermhToddT 更新时间:5/13/2023 访问量:91

问:

我有一个数组,它可能包含也可能不包含重复项,但没有 nil 或 “” 值。

array = ["Ballerina", "Lagoon", "Black", "Space", "Golden", "Lagoon"]

这确实是我最接近解决问题的方法,但这里的问题是我不知道重复项,所以它并不真正有效:

array.each_with_index.map {|value, index| array[index] == "Lagoon" ? index : ""}.uniq

我希望返回的是数组中的任何重复项,以将索引值连接到数组值的末尾。因此,最终结果将是:

array = ["Ballerina", "Lagoon 1", "Black", "Space", "Golden", "Lagoon 5"]

谢谢

数组 Ruby 排序

评论


答:

2赞 Arty.Simon 5/12/2023 #1

一种方法是使用 Enumerable#tally 方法按值存储总计数的哈希值。

array = ["Ballerina", "Lagoon", "Black", "Space", "Golden", "Lagoon"]

# Generates hash for faster lookup
# {"Ballerina"=>1, "Lagoon"=>2, "Black"=>1, "Space"=>1, "Golden"=>1}
array_tally = array.tally

new_array = array.map.with_index do |value, index| 
    value_duplicated = array_tally[value] > 1
    
    if value_duplicated 
        "#{value} #{index}"
    else
       value 
    end
end

puts new_array.inspect
# output: ["Ballerina", "Lagoon 1", "Black", "Space", "Golden", "Lagoon 5"]

评论

1赞 Stefan 5/13/2023
您可能希望使用和检查 via 以避免一次又一次地遍历整个数组来计算每个值。counts = array.tallycounts[value] > 1
1赞 Dogbert 5/13/2023
此外,可以缩写为 ..each_with_index.map.map.with_index
1赞 Cary Swoveland 5/13/2023
“替代方案”显然比你原来的答案要好得多,所以你为什么不干脆删除(并且不参考)你原来的答案呢?我建议在定义答案后开始,“第一步是使用 Enumerable#tally 按值存储总计数的哈希值。在给出答案时,请考虑写一篇可能需要多次编辑的文章或书籍,其中一些可能是审稿人建议的。如果需要,您可以通过在评论中感谢审稿人来感谢他们,就像您所做的那样。array
0赞 Arty.Simon 5/13/2023
@CarySwoveland,非常感谢这个提示。谢谢。
1赞 Rajagopalan 5/12/2023 #2

输入

array = %w[Ballerina Lagoon Black Space Golden Lagoon]

法典

# create a hash of element counts

counts = array.each_with_object(Hash.new(0)) { |element, counts| counts[element] += 1 }
# iterate through array and append index to duplicates
array.each_with_index do |element, index|
  if counts[element] > 1
    array[index] = "#{element} #{index}"
  end
end

p array

输出

#=> ["Ballerina", "Lagoon 1", "Black", "Space", "Golden", "Lagoon 5"]

评论

0赞 dawg 5/13/2023
counts = array.tally而不是你的array.each_with_object...
0赞 dawg 5/13/2023 #3

我会做:

arr = %w[Ballerina Lagoon Black Space Golden Lagoon]

cnt=arr.tally
arr2=arr.map.with_index{|e, i| cnt[e]>1 ? "#{e} #{i}" : e }

p arr2
# ["Ballerina", "Lagoon 1", "Black", "Space", "Golden", "Lagoon 5"]
0赞 Cary Swoveland 5/13/2023 #4

此答案适用于数组较大且重复项数量相对较少的情况,因为建议的计算只需要通过数组进行一次传递。

请注意,我并不是说它一定比第一步使用 Enumerable#tally 方法的方法更快,只是它只需要通过数组进行一次传递。


假设数组如下所示。

arr = ["Ballerina", "Space", "Lagoon", "Black", "Space", "Golden",
       "Lagoon", "Space"]

我们首先构造一个由索引组成的数组,对于索引的每个元素,要以所需的方式修改索引。indicesarrarr[i]iindices

indices = arr.each_with_index.
              with_object(Hash.new { |h,k| h[k] = [] }) do |(s,i),h|
                h[s] << i
              end.
              values.
              select { |a| a.size > 1 }.
              flatten
  #=> [1, 4, 7, 2, 6]

然后,我们根据需要通过遍历来修改 的副本。arrindices

indices.each_with_object(arr.dup) { |i,a| a[i] = "%s_%i" % [a[i], i] }
  #=> ["Ballerina", "Space_1", "Lagoon_2", "Black", "Space_4", "Golden",
  #    "Lagoon_6", "Space_7"]

通过检查 Kernel#dup 的源代码,不出所料地发现,该方法(用 C 语言实现)复制了一个内存块,因此不构成 元素的第二个枚举。arr


计算步骤如下。indices

h = arr.each_with_index.
        with_object(Hash.new { |h,k| h[k] = [] }) do |(s,i),h|
          h[s] << i
        end
  #=> {"Ballerina"=>[0], "Space"=>[1, 4, 7], "Lagoon"=>[2, 6],
       "Black"=>[3], "Golden"=>[5]}
a = h.values
  #=> [[0], [1, 4, 7], [2, 6], [3], [5]]
b = a.select { |a| a.size > 1 }
  #=> [[1, 4, 7], [2, 6]]
b.flatten
  #=> [1, 4, 7, 2, 6]