用Ruby删除数组中的下一个元素

3

给定一个包含数字的数组,遵循以下规则:

  • 如果有一个0,则会删除所有先前的数字和所有相邻的偶数。
  • 如果有一个1,则会删除所有先前的数字和所有相邻的奇数。
  • 如果数组的第一个元素是1,则可以将其删除。

我正在尝试编写一个算法来缩小数组,但我只能想出一个看起来很糟糕的解决方案:

def compress(array)
  zero_or_one_index = array.rindex { |element| [0,1].include? element }
  array.slice!(0, zero_or_one_index) if zero_or_one_index
  deleting = true
  while deleting
    deleting = false
    array.each_with_index do |element, index|
      next if index.zero?
      previous_element = array[index - 1]
      if (previous_element == 0 && element.even?) || 
         (previous_element == 1 && element.odd?)
        array.delete_at(index)
        deleting = true
        break
      end
    end
  end
  array.shift if array[0] == 1
end

问题在于,如果我在数组迭代时删除元素,delete_if 和类似的函数会开始破坏结果,因此我不得不使用 while 循环。
示例:
compress([3, 2, 0]) #=> [0]
compress([2, 0, 4, 6, 7]) #=> [0,7]
compress([2, 0, 4, 1, 3, 6]) #=> [6]
compress([3, 2, 0, 4, 1, 3, 6, 8, 5]) #=> [6,8,5]

我在对cancancan进行重构以优化规则定义过程中,遇到了这个问题。


我提供了一个包含许多示例的要点:https://gist.github.com/coorasse/8e3b2e9620b2b376afce6b8d274ec6e2 - coorasse
1个回答

3
以下是我解决问题的方法:
def compress(arr)
  return arr unless idx = arr.rindex {|e| e == 0 || e == 1}
  value = arr[idx]
  method_options = [:even?,:odd?]
  arr[idx..-1].drop_while do |n| 
    n.public_send(method_options[value])
  end.tap {|a| a.unshift(value) if value.zero? }
end

首先,我们使用Array#rindex查找0或1的最后出现位置的索引。如果没有找到,则返回Array
然后,我们获取该索引处的值。
接着,我们使用Array#[]从这个索引开始切掉Array尾部的所有元素。
最后,我们使用Array#drop_while删除与value01)对应的连续相邻的:even?:odd?数字。
如果value0,则在返回前将其放回Array的开头。
示例:
compress([3, 2, 0]) 
#=> [0]
compress([2, 0, 4, 6, 7]) 
#=> [0,7]
compress([2, 0, 4, 1, 3, 6]) 
#=> [6]
compress([3, 2, 0, 4, 1, 3, 6, 8, 5]) 
#=> [6,8,5]
compress([4, 5, 6])
#=> [4,5,6]
compress([0])
#=> [0]
compress([1])
#=> []

如果你的目标是要改变,就像你的问题和主旨所暗示的那样,我会坦诚地说,我不会改变我已经做的事情,而是选择采用以下方法:
def compress!(arr)
  arr.replace(compress(arr))
end

例如。
a = [3, 2, 0, 4, 1, 3, 6, 8, 5]
a == compress!(a)
#=> true
a 
#=> [6,8,5]

我不知道drop_while。好的解决方案! - coorasse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接