DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

Random Key From Ruby Hash (faster)

05.03.2005
| 11755 views |
  • submit to reddit
        I needed a Ruby Hash class that could extract a random key from the keys that have not been extracted before. Plus that the class should be re-entrant... 

I found baby's class, but it was not working (at least not in Ruby 1.8.x). I made it work (slight changes), and I added the keys_not_used to make it work faster (baby's random would cycle almost forever on large lists). But I would say all credit should go to the original author, who did all the hard work. 

Without further ado, here is the new version:
class RandomHash < Hash 

   @keys_used = nil 
  
   def random_key 
      if @keys_used == nil then
         @keys_used = Hash.new 
      else
         @keys_used = Hash.new if @keys_used.size == self.size
      end
      
      if (@keys_used.size == 0) then
         pos = rand(self.size)
         key = self.keys[pos]
      else
         @keys_not_used = Hash.new
     	 self.keys.each{ |key|
         	if (! @keys_used.include?(key)) then
            	    @keys_not_used[key] = 1
         	end
	     }
	     pos = rand(@keys_not_used.size)
	     key = @keys_not_used.keys[pos]
	  end
	  
      @keys_used[key] = 1
      return key 
   end 
end 

# Test code
$sources = Hash.new
$RandomSources = RandomHash.new

begin

   (1..10).each{ |val|
      $sources["#{val}"] = 1
   }
   
   $sources.each { |src|
      puts "Src=#{src}"
      $RandomSources[src] = 1
   }

   RandomKeys = Hash.new
   (0..$sources.length).each{ |index| # one more than all values
      key = $RandomSources.random_key
      print "Extracted #{key} ... It's a"
      if (!RandomKeys.has_key?(key)) then
         puts " NEW key!"
         RandomKeys[key] = 1
      else
         puts "n OLD key!"
      end
   }
end
    

Comments

Valery Kalaydzhiev replied on Thu, 2013/02/21 - 8:32am

you are fucking idiots

think about this variant

I'm from few months playing with Ruby and can find this, no fucking classes, nothing.. just a simple:

hash.invert.values[rand(hash.length)]

or even
the most simple:

hash.invert.values.sample

Snippets Manager replied on Tue, 2009/08/18 - 2:20pm

I think something like this would be cleaner and probably more efficient: class RandomHash < Hash @keys_not_used = nil def random_key if @keys_used == nil or @keys_used.size == self.size @keys_not_used = Hash.new self.keys.each {|k| @keys_not_used[k] = true} end key, val = @keys_not_used.delete(rand(@keys_not_used.keys.length)) return key end end Cheers!