用红宝石删除数组中的下一个元素

时间:2018-07-11 11:39:11

标签: arrays ruby

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

  • 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进行一些重构以优化规则定义的情况下出现的。

1 个答案:

答案 0 :(得分: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的尾端。

然后使用:even?删除与:odd?value0)对应的所有连续相邻1Array#drop_while号。

最后,如果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]