Ruby Array Subtraction Without Removing Items More Than Once

Ruby array subtraction without removing items more than once

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

Subtract arrays with frequency

a1 = [1,2,2,2]
a2 = [1,2]
a2.each { |e| (idx = a1.find_index e) && (a1.delete_at idx) }
#⇒ [2, 2]

Here we iterate the second array and delete elements from the first one, once per iteration, if those were found.

The first found element will be deleted.

Subtracting one Array from another in Ruby

You can subtract arrays in Ruby:

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

ary - other_ary → new_ary Array Difference

Returns a new array that is a copy of the original array, removing any
items that also appear in other_ary. The order is preserved from the
original array.

It compares elements using their hash and eql? methods for efficiency.

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

If you need
set-like behavior, see the library class Set.

See the Array documentation.

Remove common elements from two arrays while keeping duplicates in Ruby

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

a.each_with_index {|av,ai|
b.each_with_index {|bv,bi|
if (av == bv)
a[ai] = nil
b.delete_at(bi)
break
end
}
}

# a.compact!: [1,3,3,3,3]

For something a little more robust, see this answer:

https://stackoverflow.com/a/3852809/183181

Or here if order is important:

https://stackoverflow.com/a/21737172/183181

How to subtract values from one array with another ruby

.zip only looks one level down. In your example x and y are not the values in the internal arrays, they're the internal arrays themselves. Subtracting one array from another removes all common elements from the first. That's why the result you see is first array with all zeros (common elements) removed.

If you want to zip the internal arrays, you need to go one level deeper:

values1.zip(values2).map { |x, y| x.zip(y).map { |a, b| a - b } }

Synchronizing two arrays

Here an implementation of TNT's elegant solution for deleting the first matching element of an array

class Array
def delete_first item
delete_at(index(item) || length)
end
def distinct other, own = self.dup
other.each{|e| own.delete_first(e)}
own
end
end

arr1.distinct arr2 # ["a", "a", "f"]
arr2.distinct arr1 # ["b", "d"]

How to find which items in a MASSIVE array appear more than once?

Does something like

items = 30_000_000

array = items.times.map do
rand(10_000_000)
end

puts "Done with seeding"
puts
puts "Checking what items appear more than once. Size: #{array.size}"
puts

t1 = Time.now
def more_than_once(array)
counts = Hash.new(0)
array.each do |item|
counts[item] += 1
end

counts.select do |_, count|
count > 1
end.keys
end

res = more_than_once(array)
t2 = Time.now


p res.size
puts "Took #{t2 - t1}"

work for you?

The duration is about 40s on my machine.

Ruby- Adding/subtracting elements from one array with another array

You could use zip:

a.zip(b).map { |x, y| y - x }
# => [1, 1, 1, 1]

There is also a Matrix class:

require "matrix"

a = Matrix[[1, 2, 3, 4]]
b = Matrix[[2, 3, 4, 5]]
c = b - a
# => Matrix[[1, 1, 1, 1]]


Related Topics



Leave a reply



Submit