How does defining [square bracket] method in Ruby work?
I am going through Programming Ruby - a pragmatic programmers guide and have stumbled on this piece of code:
class SongList
def [](key)
if key.kind_of?(Integer)
return @songs[key]
else
for i in [email protected]
return @songs[i] if key == @songs[i].name
end
end
return nil
end
end
I do not understand how defining [ ] method works?
Why is the key outside the [ ], but when the method is called, it is inside [ ]?
Can key be without parenthesis?
I realize there are far better ways to write this, and know how to write my own method that works, but this [ ] method just baffles me... Any help is greatly appreciated, thanks
It's just syntactic sugar. There are certain syntax patterns that get translated into message sends. In particular
a + b
is the same as
a.+(b)
and the same applies to ==
, !=
, <
, >
, <=
, >=
, <=>
, ===
, &
, |
, *
, /
, -
, %
, **
, >>
, <<
, !==
, =~
and !~
as well.
Also,
!a
is the same as
a.!
and the same applies to ~
.
Then,
+a
is the same as
a.+@
and the same applies to -
.
Plus,
a.(b)
is the same as
a.call(b)
There is also special syntax for setters:
a.foo = b
is the same as
a.foo=(b)
And last but not least, there is special syntax for indexing:
a[b]
is the same as
a.[](b)
and
a[b] = c
is the same as
a.[]=(b, c)
Methods in ruby, unlike many languages can contain some special characters. One of which is the array lookup syntax.
If you were to implement your own hash class where when retrieving an item in your hash, you wanted to reverse it, you could do the following:
class SillyHash < Hash
def [](key)
super.reverse
end
end
You can prove this by calling a hash with the following:
a = {:foo => "bar"}
=> {:foo=>"bar"}
a.[](:foo)
=> "bar"
a.send(:[], :foo)
=> "bar"
So the def [] defined the method that is used when you do my_array["key"]
Other methods that may look strange to you are:
class SillyHash < Hash
def [](key)
super.reverse
end
def []=(key, value)
#do something
end
def some_value=(value)
#do something
end
def is_valid?(value)
#some boolean expression
end
end
Just to clarify, the definition of a []
method is unrelated to arrays or hashes. Take the following (contrived) example:
class B
def []
"foo"
end
end
B.new[]
=> "foo"
the square brackets are the method name like Array#size
you have Array#[]
as a method and you can even use it like any other method:
array = [ 'a', 'b', 'c']
array.[](0) #=> 'a'
array.[] 1 #=> 'b'
array[2] #=> 'c'
the last one is something like syntactic sugar and does exactly the same as the first one. The Array#+
work similar:
array1 = [ 'a', 'b' ]
array2 = [ 'c', 'd' ]
array1.+(array2) #=> [ 'a', 'b', 'c', 'd' ]
array1.+ array2 #=> [ 'a', 'b', 'c', 'd' ]
array1 + array2 #=> [ 'a', 'b', 'c', 'd' ]
You can even add numbers like this:
1.+(1) #=> 2
1.+ 1 #=> 2
1 + 1 #=> 2
the same works with /
, *
, -
and many more.