开发者

Ruby array subtraction without removing items more than once

开发者 https://www.devze.com 2023-01-19 01:42 出处:网络
The canonical Array difference example in Ruby is: [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ]#=>[ 3, 3, 5 ]

The canonical Array difference example in Ruby is:

[ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ]  #=>  [ 3, 3, 5 ]

What's the best way to get the following behavior instead?

[ 1, 1, 2, 2, 3, 3, 4, 5 ].subtract_once([ 1, 2, 4 ])  #=>  [ 1, 2, 3, 3, 开发者_JAVA百科5 ]

That is, only the first instance of each matching item in the second array is removed from the first array.


Subtract values as many times as they appear in the other array, or any Enumerable:

class Array
  # Subtract each passed value once:
  #   %w(1 2 3 1).subtract_once %w(1 1 2) # => ["3"]
  #   [ 1, 1, 2, 2, 3, 3, 4, 5 ].subtract_once([ 1, 2, 4 ]) => [1, 2, 3, 3, 5]
  # Time complexity of O(n + m)
  def subtract_once(values)
    counts = values.inject(Hash.new(0)) { |h, v| h[v] += 1; h }
    reject { |e| counts[e] -= 1 unless counts[e].zero? }
  end

Subtract each unique value once:

require 'set'
class Array
  # Subtract each unique value once:
  #   %w(1 2 2).subtract_once_uniq %w(1 2 2) # => [2]
  # Time complexity of O((n + m) * log m)
  def subtract_once_uniq(values)
    # note that set is implemented 
    values_set = Set.new values.to_a 
    reject { |e| values_set.delete(e) if values_set.include?(e) }
  end
end


class Array
  def subtract_once(b)
    h = b.inject({}) {|memo, v|
      memo[v] ||= 0; memo[v] += 1; memo
    }
    reject { |e| h.include?(e) && (h[e] -= 1) >= 0 }
  end
end

I believe this does what I want. Many thanks to @glebm


This is all I can think of so far:

[1, 2, 4].each { |x| ary.delete_at ary.index(x) }


Similar to @Jeremy Ruten's answer but accounting for the fact that some elements may not be present:

# remove each element of y from x exactly once
def array_difference(x, y)
  ret = x.dup
  y.each do |element|
    if index = ret.index(element)
      ret.delete_at(index)
    end
  end
  ret
end

This answer also won't modify the original array as it operates, so:

x = [1,2,3]
y = [3,4,5]
z = array_difference(x, y) # => [1,2]
x == [1,2,3]               # => [1,2,3]
0

精彩评论

暂无评论...
验证码 换一张
取 消