我有一个简单的方法,它迭代一个数组并返回一个副本。 (或重复)
def find_dup(array)
duplicate = 0
array.each { |element| duplicate = element if array.count(element) > 1}
duplicate
end
它有效,但我想更优雅地表达它。
它是三行的原因是方法必须返回的变量“duplicate”,如果我在块中引入它,该方法是不可见的,即
def find_dup(array)
array.each { |element| duplicate = element if array.count(element) > 1}
duplicate
end
我尝试了几种方法来定义“重复”作为块的结果,但无济于事。
有什么想法吗?
答案 0 :(得分:1)
在单行中干净利落地做太多了,但这是更多 有效的解决方案。
def find_dups(arr)
counts = Hash.new { |hash,key| hash[key] = 0 }
arr.each_with_object(counts) do |x, memo|
memo[x] += 1
end.select { |key,val| val > 1 }.keys
end
Hash.new
调用实例化一个默认值为0的哈希。
each_with_object
修改此哈希以跟踪arr
中每个元素的计数,然后在
结束filter
用于仅选择计数大于一的那些。
这种方法相对于使用Array#includes?
或Array#count
的解决方案的好处是它只扫描一次数组。因此,它是O(N)时间而不是O(N ^ 2)。
答案 1 :(得分:1)
您的方法只是找到数组中的最后一个副本。如果你想要所有重复项,我会做这样的事情:
def find_dups(arr)
dups = Hash.new { |h, k| h[k] = 0 }
arr.each { |el| dups[el] += 1 }
dups.select { |k, v| v > 1 }.keys
end
答案 2 :(得分:1)
如果你真正想要的是一个不关心大O复杂性的单线程,并且只返回数组中的最后一个副本,我会这样做:
def find_last_dup(arr)
arr.reverse_each { |el| return el if arr.count(el) > 1 }
end
答案 3 :(得分:0)
你可以将它作为一行进行,它会更好地流动。虽然这会找到重复的第一个实例,而你的代码却返回了副本的最后一个实例,但不确定这是否是你需求的一部分。
def find_dup(array)
array.group_by { |value| value }.find { |_, groups| groups.count > 1 }.first
end
另外,请注意,制作一行并不严格意味着更好。我会发现代码更易读,分成更多行,但这只是我的意见。
def find_dup(array)
array.group_by { |value|
value
}.find { |_, groups|
groups.count > 1
}.first
end
答案 4 :(得分:0)
假设:
> a
=> [8, 5, 6, 6, 5, 8, 6, 1, 9, 7, 2, 10, 7, 7, 3, 4]
您可以将重复组合在一起:
> a.uniq.each_with_object(Hash.new(0)) {|e, h| c=a.count(e); h[e]=c if c>1}
=> {8=>2, 5=>2, 6=>3, 7=>3}
或者,
> a.group_by{ |e| e}.select{|k,v| v if v.length>1}
=> {8=>[8, 8], 5=>[5, 5], 6=>[6, 6, 6], 7=>[7, 7, 7]}
在每种情况下,结果的顺序都基于a
中具有重复项的元素的顺序。如果你只想要第一个:
> a.group_by{ |e| e}.select{|k,v| v if v.length>1}.first
=> [8, [8, 8]]
或者说:
> a.group_by{ |e| e}.select{|k,v| v if v.length>1}.to_a.last
=> [7, [7, 7, 7]]
如果你想'快进'到第一个有重复的值,你可以使用drop_while:
> b=[1,2,3,4,5,4,5,6]
> b.drop_while {|e| b.count(e)==1 }[0]
=> 4
或者最后一次:
> b.reverse.drop_while {|e| b.count(e)==1 }[0]
=> 5
答案 5 :(得分:0)
只想添加一种混合方法。
def find_last_dup(arr)
arr.reverse_each.detect { |x| arr.count(x) > 1 }
end
或者,您可以在两行中获得线性时间复杂度。
def find_last_dup(arr)
freq = arr.each_with_object(Hash.new(0)) { |x, obj| obj[x] += 1 }
arr.reverse_each.detect { |x| freq[x] > 1 }
end
为了论证,后一种方法也可以减少到一行,但这可能是单一的,令人困惑。
def find_last_dup(arr)
arr.each_with_object(Hash.new(0)) { |x, obj| obj[x] += 1 }
.tap do |freq| return arr.reverse_each.detect { |x| freq[x] > 1 } end
end
答案 6 :(得分:0)
def find_duplicates(array)
array.dup.uniq.each { |element| array.delete_at(array.index(element)) }.uniq
end
上述方法 find_duplicates 复制了输入数组并删除了所有元素的第一次出现,使数组只剩下重复元素的剩余次数。
示例:
array = [1, 2, 3, 4, 3, 4, 3]
=> [1, 2, 3, 4, 3, 4, 3]
find_duplicates(array)
=> [3, 4]