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, 5 ]
That is, only the first instance of each matching item in the second array is removed from the first array.
Description. C = A - B subtracts array B from array A by subtracting corresponding elements. The sizes of A and B must be the same or be compatible. If the sizes of A and B are compatible, then the two arrays implicitly expand to match each other.
Ruby arrays can hold objects such as String, Integer, Fixnum, Hash, Symbol, even other Array objects. Ruby arrays are not as rigid as arrays in other languages. Ruby arrays grow automatically while adding elements to them.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With