提问人:ToddT 提问时间:5/12/2023 最后编辑:anothermhToddT 更新时间:5/13/2023 访问量:91
在 Ruby 中识别和更新具有索引值的数组中的重复项
Identify and Update Duplicates in Array with Index Value in Ruby
问:
我有一个数组,它可能包含也可能不包含重复项,但没有 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"]
谢谢
答:
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.tally
counts[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"]
我们首先构造一个由索引组成的数组,对于索引的每个元素,要以所需的方式修改索引。indices
arr
arr[i]
i
indices
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]
然后,我们根据需要通过遍历来修改 的副本。arr
indices
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]
评论