How does ruby's String .hash method work? - ruby

I'm just a newbie to ruby. I've seen a string method (String).hash .
For example, in irb, I've tried
>> "mgpyone".hash
returns
=> 144611910
how does this method works ?

The hash method is defined for all objects. See documentation:
Generates a Fixnum hash value for this
object. This function must have the
property that a.eql?(b) implies a.hash == b.hash.
The hash value is used by class Hash. Any hash value that
exceeds the capacity of a Fixnum will
be truncated before being used.
So the String.hash method is defined in C-Code. Basically (over-simplified) it just sums up the characters in that string.

If you need to get a consistent hashing output I would recommend NOT to use 'string.hash but instead consider using Digest::MD5 which will be safe in multi-instance cloud applications for example you can test this as mentioned in comment in previous by #BenCrowell
Run this 2x from your terminal, you will get different output each time:
ruby -e "puts 'a'.hash"
But if you run this the output will be consistent:
ruby -e "require 'digest'; puts Digest::MD5.hexdigest 'a'"

Related

reverse method - 1.9.1 v 1.8.7

I wrote some code:
output = File.open(text_file).collect.reverse.join("<BR>")
It seems to work okay on 1.8.7 but throws the error
NoMethodError - undefined method 'reverse' for #<Enumerator: #<File:C:\EduTester\cron\rufus.log>:collect>:
on 1.9.1 (ruby 1.9.3p194 (2012-04-20) [i386-mingw32])
Does somebody know why this happens and how to fix this? (Why is of most interest to me.)
First how to fix it - you should be doing this:
output = File.open(text_file).to_a.reverse.join("<BR>")
This will work on either version of Ruby. Basically you need to turn the file into an array of lines (with .to_a) before reversing them and adding line breaks.
In terms of the why (this gets a little technical): File mixes in the Enumerable module, which gives it methods like collect. Now in Ruby 1.87, if you called Enumberable.collect without a block it would return an Array. But in 1.9, it returns an Enumerator - which doesn't respond to the reverse method.
Here are the 2 versions of the method in question:
http://ruby-doc.org/core-1.8.7/Enumerable.html#method-i-collect
http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-collect
So basically before 1.9 .collect was a (hacky) equivalent to .to_a. But always use .to_a to turn something into an array.
In Ruby 1.8.7 if a block is given or not given with collect method, it returns an array.
But in 1.9 , it will only return array if block is given with collect method. Otherwise it will return enumerator object.
From documentation -
Collect method-
Returns a new array with the results of running block once for every element in enum.
If no block is given, an enumerator is returned instead.
in 1.8.7, the collect method returns an array when applied on a File object whereas in 1.9.3, it returns an enumerator. reverse method can be applied on an array only.
The reason this works in 1.8.7 is that when you call Enumerable#collect without a block in 1.8.7, it uses a default block which just returns its arg, so file.collect is equivalent to file.collect {|x| x} which returns an array of the lines in the file, on which in you can call Array#reverse`.
In 1.9.x, calling Enumerable#collect without a block returns an Enumerator. Enumerator does not natively support #reverse, nor does its mixin Enumerable. So, you get NoMethodError.
If you want to write this expression in a way compatible with either version, use #to_a instead of #collect.
output = File.open(text_file).to_a.reverse.join("<BR>")

ruby symbol as key, but can't get value from hash

I'm doing some update on other one's code and now I have a hash, it's like:
{"instance_id"=>"74563c459c457b2288568ec0a7779f62", "mem_quota"=>536870912, "disk_quota"=>2147483648, "mem_usage"=>59164.0, "cpu_usage"=>0.1, "disk_usage"=>6336512}
and I want to get the value by symbol as a key, for example: :mem_quota, but failed.
The code is like:
instance[:mem_usage].to_f
but it returns nothing. Is there any reason can cause this problem?
Use instance["mem_usage"] instead since the hash is not using symbols.
The other explanations are correct, but to give a broader background:
You are probably used to working within Rails where a very specific variant of Hash, called HashWithIndifferentAccess, is used for things like params. This particular class works like a standard ruby Hash, except when you access keys you are allowed to use either Symbols or Strings. The standard Ruby Hash, and generally speaking, Hash implementations in other languages, expect that to access an element, the key used for later access should be an object of the same class and value as the key used to store the object. HashWithIndifferentAccess is a Rails convenience class provided via the Active Support libraries. You are free to use them yourself, but they have first be brought in by requiring them.
HashWithIndifferentAccess just does the conversion for you at access time from string to symbol.
So, for your case, instance["mem_usage"].to_f should work.
You need HashWithIndifferentAccess.
require 'active_support/core_ext'
h1 = {"instance_id"=>"74563c459c457b2288568ec0a7779f62", "mem_quota"=>536870912,
"disk_quota"=>2147483648, "mem_usage"=>59164.0, "cpu_usage"=>0.1,
"disk_usage"=>6336512}
h2 = h1.with_indifferent_access
h1[:mem_usage] # => nil
h1["mem_usage"] # => 59164.0
h2[:mem_usage] # => 59164.0
h2["mem_usage"] # => 59164.0
Also, there are the symbolize_keys and stringify_keys options that may be of help. The method names are self-descriptive enough, I believe.
Clearly the keys of your hash are strings because they have double quotes around them. Therefore you will need to access the keys with instance["mem_usage"] or you will need to build a new hash with symbols as the keys first.
If you use Rails with ActiveSupport, then do use HashWithIndifferentAccess for flexibility in accessing hash with either string or symbol.
hash = HashWithIndifferentAccess.new({
"instance_id"=>"74563c459c457b2288568ec0a7779f62",
"mem_quota"=>536870912, "disk_quota"=>2147483648,
"mem_usage"=>59164.0,
"cpu_usage"=>0.1,
"disk_usage"=>6336512
})
hash[:mem_usage] # => 59164.0
hash["mem_usage"] # => 59164.0

What is the difference between these two method parameter definitions?

I'm starting to learn Ruby. I read that arguments where passed by reference to a method,
however I don't understand the difference between these two methods.
def print(text)
puts text
end
and
def print(*text)
puts text
end
Using a * means that we are passing a pointer like in C?
The *text is what's called the splat operator in Ruby. It basically means if you pass multiple arguments to the second print they will get slurped into the single text variable.
See The Splat Operator in Ruby
The * before a parameter name in a Ruby parameter list is used for variable length arguments, so they are similar to the ... in C/C++ for varargs.
def vlaFunc(*args)
puts args
end
vlaFunc(1,2,3)
# output is [1,2,3]
There are no pointers in Ruby, * in this context is generally referred to as the "splat" operator:
http://4loc.wordpress.com/2009/01/16/the-splat-operator-in-ruby/
http://theplana.wordpress.com/2007/03/03/ruby-idioms-the-splat-operator/
In this case the method can take an arbitrary number of arguments, which will be available in the array text.
First you have two nice methods started there. But I would say try to avoid using puts inside them. You don't need it anyway. A method will always yield the last statement evaluated. something = text would get the job done. And I don't need to answer now about the differences.
Your first two replies are very good there. But you may want to try something like this
j = *[] #=> nil in 1.8 but [] in 1.9
It's been the new kid on the block for a time now. Guess what it does?

What does :this means in Ruby on Rails?

I'm new to the Ruby and Ruby on Rails world. I've read some guides, but i've some trouble with the following syntax.
I think that the usage of :condition syntax is used in Ruby to define a class attribute with some kind of accessor, like:
class Sample
attr_accessor :condition
end
that implicitly declares the getter and setter for the "condition" property.
While i was looking at some Rails sample code, i found the following examples that i don't fully understand.
For example:
#post = Post.find(params[:id])
Why it's accessing the id attribute with this syntax, instead of:
#post = Post.find(params[id])
Or, for example:
#posts = Post.find(:all)
Is :all a constant here? If not, what does this code really means? If yes, why the following is not used:
#posts = Post.find(ALL)
Thanks
A colon before text indicates a symbol in Ruby. A symbol is kind of like a constant, but it's almost as though a symbol receives a unique value (that you don't care about) as its constant value.
When used as a hash index, symbols are almost (but not exactly) the same as using strings.
Also, you can read "all" from :all by calling to_s on the symbol. If you had a constant variable ALL, there would be no way to determine that it meant "all" other than looking up its value. This is also why you can use symbols as arguments to meta-methods like attr_accessor, attr_reader, and the like.
You might want to read up on Ruby symbols.
:all is a symbol. Symbols are Ruby's version of interned strings. You can think of it like this: There is an invisible global table called symbols which has String keys and Fixnum values. Any string can be converted into a symbol by calling .to_sym, which looks for the string in the table. If the string is already in the table, it returns the the Fixnum, otherwise, it enters it into the table and returns the next Fixnum. Because of this, symbols are treated at run-time like Fixnums: comparison time is constant (in C parlance, comparisons of symbols can be done with == instead of strcmp)
You can verify this by looking at the object_id of objects; when two thing's object_ids are the same, they're both pointing at the same object.
You can see that you can convert two strings to symbols, and they'll both have the same object id:
"all".to_sym.object_id == "all".to_sym.object_id #=> true
"all".to_sym.object_id == :all.object_id #=> true
But the converse is not true: (each call to Symbol#to_s will produce a brand new string)
:all.to_s.object_id == :all.to_s.object_id #=> false
Don't look at symbols as a way of saving memory. Look at them as indicating that the string ought to be immutable. 13 Ways of Looking at a Ruby Symbol gives a variety of ways of looking at a symbol.
To use a metaphor: symbols are for multiple-choice tests, strings are for essay questions.
This has nothing to do with Rails, it's just Ruby's Symbols. :all is a symbol which is effectively just a basic string.

defined? in Ruby works in irb but not in my class file

I was writing a small Heap implementation and upon creating my Node class I noticed some weird behaviour. I wanted to call defined?(x) to ensure x was defined, then check if x was an Integer, before storing it in the Node's value class variable. In IRB I can call
defined?(x) and the result is nil.
However, in the class, I try this:
def change_value value
#value = value if defined?(value)
end
and the result when I call the change_value with a random letter, let's say 'e', is the standard undefined local variable or method error. Again, in IRB it seems to work fine and I am wondering if I have some kind of environment issue or if this is not the 'best' way to check if value is really there.
Thanks.
(Edit: DigitalRoss has since cleaned up the question's formatting, so the comment on formatting and the initial re-write may no longer apply. I'll leave them in until I get some feedback from the OP.)
That's a nearly unreadable way to write a method and it doesn't even parse on my machine (Ruby 1.8.7).
I'm assuming you mean the following:
def change_value(value)
#value = value if defined?(value)
end
This works fine when I call it, but it's incorrect. nil and undefined are two different beasts in Ruby; value will always be defined in that context because it's a declared method parameter. I suspect what you are really after is:
def change_value(value)
#value = value unless value.nil?
end
Note that some people would simply write this as:
def change_value(value)
#value = value if value
end
because nil is "falsy". However, this form conflates nil and false, so it's not a good habit to get into.
This isn't the best way to check if it's really there. If change_value is called without providing any parameters, you would get:
ArgumentError: wrong number of arguments (0 for 1)
Instead, you might want to check to make sure value is not nil. Of course you can do this a variety of ways:
if !value.nil?
#...
end
if value
# this will be exeuted if value is either not `nil` or not `false`
end
Best of luck!
Would it have to do with calling the method from the class instance and not the object instance?

Resources