hash | Dmytro Shteflyuk's Home https://kpumuk.info In my blog I'll try to describe about interesting technologies, my discovery in IT and some useful things about programming. Wed, 15 Jul 2009 14:12:01 +0000 en-US hourly 1 https://wordpress.org/?v=6.7.1 Memo #5: Use ary.uniq method carefully in Ruby https://kpumuk.info/ruby-on-rails/memo-5-use-ary-uniq-method-carefully-in-ruby/ https://kpumuk.info/ruby-on-rails/memo-5-use-ary-uniq-method-carefully-in-ruby/#comments Wed, 15 Jul 2009 10:55:17 +0000 http://kpumuk.info/?p=721 Today my friend asked me to help him with an unexpected behavior of Ruby’s Hash.uniq method. Here is an example: 123456[{"id"=>667824693}, {"id"=>667824693}].uniq # => [{"id"=>667824693}, {"id"=>667824693}] [{"id"=>66782469}, {"id"=>66782469}].uniq # => [{"id"=>66782469}] [{"id"=>6678246931}, {"id"=>6678246931}].uniq # => [{"id"=>6678246931}] Check the first command result. Very disappointing, right? So what happen? Quick looking through the Ruby code completely explained […]

The post Memo #5: Use ary.uniq method carefully in Ruby first appeared on Dmytro Shteflyuk's Home.]]>
Today my friend asked me to help him with an unexpected behavior of Ruby’s Hash.uniq method. Here is an example:

1
2
3
4
5
6
[{"id"=>667824693}, {"id"=>667824693}].uniq
# => [{"id"=>667824693}, {"id"=>667824693}]
[{"id"=>66782469}, {"id"=>66782469}].uniq
# => [{"id"=>66782469}]
[{"id"=>6678246931}, {"id"=>6678246931}].uniq
# => [{"id"=>6678246931}]

Check the first command result. Very disappointing, right? So what happen? Quick looking through the Ruby code completely explained it. Here is how this method works internally (this is just prototype in Ruby, original code is in C, but works in the same way):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def ary_make_hash(ary, val)
  ary.inject({}) do |hash, el|
    hash[el] = val
    hash
  end
end

def uniq(ary)
  ary = ary.dup
  ary.uniq!
  ary
end

def uniq!(ary)
  hash = ary_make_hash(ary, 0)
  return nil if ary.length == hash.length

  j = 0
  (0...ary.length).each do |idx|
    if hash.delete(ary[idx])
      ary[j] = ary[idx]
      j += 1
    end
  end
  ary.slice!(0, j)
  ary
end

Let’s test it:

1
2
3
4
5
6
uniq([{"id"=>667824693}, {"id"=>667824693}])
# => [{"id"=>667824693}, {"id"=>667824693}]
uniq([{"id"=>66782469}, {"id"=>66782469}])
# => [{"id"=>66782469}]
uniq([{"id"=>6678246931}, {"id"=>6678246931}])
# => [{"id"=>6678246931}]

And just to make sure our conclusions are correct:

1
2
3
4
5
6
[{"id"=>667824693}, {"id"=>667824693}].map { |el| el.hash }
# => [29793216, 29793156]
[{"id"=>66782469}, {"id"=>66782469}].map { |el| el.hash }
# => [255119887, 255119887]
[{"id"=>6678246931}, {"id"=>6678246931}].map { |el| el.hash }
# => [482552381, 482552381]

So the idea behind the Hash.uniq method is the method Hash.hash, which produces different results for hashes in the first example. Be careful when doing obj.uniq on complex objects.

Update: There is a good research on Hash.hash method here.

The post Memo #5: Use ary.uniq method carefully in Ruby first appeared on Dmytro Shteflyuk's Home.]]>
https://kpumuk.info/ruby-on-rails/memo-5-use-ary-uniq-method-carefully-in-ruby/feed/ 4