Skip to content

Instantly share code, notes, and snippets.

@jah2488
Last active August 29, 2015 13:55
Show Gist options
  • Save jah2488/8689010 to your computer and use it in GitHub Desktop.
Save jah2488/8689010 to your computer and use it in GitHub Desktop.
Non-destructively perform a difference operation on two arrays
def diff(a,b)
hash_by_count(a)
.merge(hash_by_count(b)) { |_, av, bv| av - bv }
.flat_map { |k, v| [k] * v }
end
private
def hash_by_count(arr)
Hash[arr.group_by { |x| x }.map { |k, v| [k, v.count] }]
end
if $0 !~ /rspec/
p diff([1,1,1], [1,1])
p diff([1,2,1], [1,1])
p diff([1,2,1], [2])
p diff([1,2,1], [1])
else
describe 'diff' do
it 'removes difference between arrs' do
expect(diff([1,1,1], [1,1])).to eq [1]
expect(diff([1,2,1], [1,1])).to eq [2]
expect(diff([1,2,1], [2])).to eq [1,1]
expect(diff([1,2,1], [1])).to eq [1,2]
end
it 'should work with non ints' do
expect(diff([:foo, :bar, :foo], [:foo])).to eq [:foo, :bar]
end
end
end
class Array
def -(other)
hash_by_count(self)
.merge(hash_by_count(other)) { |_, selfv, otherv| selfv - otherv }
.flat_map { |k, v| [k] * v }
end
private
def hash_by_count(arr)
Hash[arr.group_by { |x| x }.map { |k, v| [k, v.count] }]
end
end
[1,1,1] - [1,1] == [1]
[1,2,1] - [1] == [1,2]
@jah2488
Copy link
Author

jah2488 commented Jan 29, 2014

Someone was wondering what was going on with arr.group_by { |x| x } and thats probably worth explaining a little. This is simply a way of telling ruby to group by self. This isn't normally very useful unless you are dealing with duplicates, where it will group them together into an array. This effect can be duplicated with group_by(&:to_s), but converts all your keys to strings in the process and doesn't work well if you want to preserve the original type.

[1, 2, 3, 4].group_by { |x| x }
#=> { 1=> [1], 2 => [2], 3 => [3], 4 => [4] }

[1, 2, 3, 4, 4].group_by { |x| x }
#=> { 1 => [1], 2 => [2], 3 => [3], 4=>[4, 4] }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment