我怎样才能重构这个Ruby方法以更快地运行?

时间:2017-11-21 04:33:19

标签: arrays ruby

下面的方法应该采用数组a并返回第二个索引值最低的重复整数。该数组仅包含1和a.length之间的整数。通过这个例子,

firstDuplicate([1,2,3,2,4,5,1])

该方法返回2

def firstDuplicate(a)
    num = 1
    big_num_array = []
    a.length.times do
        num_array = []
        if a.include?(num)
            num_array.push(a.index(num))
            a[a.index(num)] = "x"
            if a.include?(num)
                num_array.unshift(a.index(num))
                num_array.push(num)
            end
            big_num_array.push(num_array) if num_array.length == 3
         end        
         num += 1   
     end
     if big_num_array.length > 0
         big_num_array.sort![0][2]
     else
         -1
     end  
end

代码可以工作,但似乎比必要的时间长,并且运行速度不够快。我正在寻找重构的方法。

3 个答案:

答案 0 :(得分:3)

您可以随时计算条目,并在您再次找到某个内容时使用Enumerable#find停止迭代:

h = { }
a.find do |e|
  h[e] = h[e].to_i + 1 # The `to_i` converts `nil` to zero without a bunch of noise.
  h[e] == 2
end

你也可以说:

h = Hash.new(0) # to auto-vivify with zeros
a.find do |e|
  h[e] += 1
  h[e] == 2
end

或使用Hash#fetch使用默认值:

h = { }
a.find do |e|
  h[e] = h.fetch(e, 0) + 1
  h[e] == 2
end

find会在找到构成该块true的元素后立即停止,因此这应该是合理有效的。

答案 1 :(得分:3)

这有两种方法可以很简单地完成。

使用

require 'set'

def first_dup(arr)
  st = Set.new
  arr.find { |e| st.add?(e).nil? }
end

first_dup [1,2,3,2,4,5,4,1,4]
  #=> 2
first_dup [1,2,3,4,5]
  #=> nil

请参阅Set#add?

使用Array#difference

def first_dup(arr)
  arr.difference(arr.uniq).first
end

first_dup [1,2,3,2,4,5,4,1,4]
  #=> 2
first_dup [1,2,3,4,5]
  #=> nil

我发现Array#difference足够有用proposed it be added to the Ruby core(但它似乎并没有获得牵引力)。它如下:

class Array
  def difference(other)
    h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
    reject { |e| h[e] > 0 && h[e] -= 1 }
  end
end

如链接所述,它与Array#-的区别如下:

a = [1,2,2,3,3,2,2]
b = [2,2,3]

a - b
  #=> [1]
a.difference(b)
  #=> [1,3,2,2]

difference"删除" 2a 2 b3一个a(类似于a),保留arr = [1,2,3,2,4,5,4,1,4] a = arr.uniq #=> [1,2,3,4,5] b = arr.difference(a) #=> [2, 4, 1, 4] b.first #=> 2 左侧的顺序}。但是,iFrame并未发生变异。

上面针对本问题给出的示例中的步骤如下。

.load()

答案 2 :(得分:2)

如果您正在寻找超级性能,ruby可能不是最佳选择语言。如果您正在寻找可读性,请转到:

[1,2,3,2,4,5,1].
  map.               # or each (less readable, probably faster) 
  with_index.
  group_by(&:shift). # or group_by(&:first)
  min_by { |v, a| a[1] && a[1].last || Float::INFINITY }.
  first
#⇒ 2