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

Requirements/Inspiration came from these tweets by https://twitter.com/coreyhaines/status/428289594035929088 and https://twitter.com/coreyhaines/status/428297286456578048

A more straightforward way of accomplishing this is with ruby's delete_at command, but my main goal in this was to try and avoid any destructive methods. So although b.each { |x| a.delete_at(a.index(x)) } works and is very concise. I wanted to try and come up with a method that would not alter arrays a and b while also not spraying dups everywhere.

@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