How does defining [square bracket] method in Ruby work?

Go To StackoverFlow.com

42

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 0...@songs.length
        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

2012-04-04 20:40
by oFca


37

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" 
2012-04-04 20:43
by Gazler
I think the OP was asking why we wouldn't call it: my_array.[]("key") instead and how my_array["key"] could possibly work.. - Amokrane Chentir 2012-04-04 20:46
so by definition, whenever I create [] method for some class in ruby, it knows that it is being used on some kind of array and expects (key) parameter wich it later puts in [] - oFca 2012-04-04 20:47
@oFca If you define a method called [], then it will allow you to call ["key"] on an instance of that class. An Array or Hash are two examples of where ruby uses this internally - Gazler 2012-04-04 20:49
wow, ruby is the man :) thank - oFca 2012-04-04 20:52
@Gazler one more question... can i specify key without (). like this: def [] key ... - oFca 2012-04-04 20:53
@oFca Yeah, you are allowed to omit the braces in the definition of a ruby method - Gazler 2012-04-04 20:55
@Gazler just noticed... it isn't a problem that SongList class from the example isn't inheriting from Array class? I can still create [] method - oFca 2012-04-04 21:00
@oFca Not at all. This was purely for my demonstration. The Array implementation just happens to be the one that utilizes the [] method well that people are most familiar with. Try and disassociate [] from an Array and just treat it as another method. Although this can be difficult (as my answer proves! - Gazler 2012-04-04 21:03
There's an infinite recursion in your code. I think you meant to pass the call to super - tsherif 2012-04-04 21:03
@Gazler Fix your code. super[key] inside a method won't work. super(key).reverse would. super[key] = blah won't work. super(key, value) would. You should refresh yourself on how to use super in a ruby method - Benjamin 2013-07-11 04:12
@Ben You are quite correct. I have updated the answer with super calls - Gazler 2013-07-11 09:38


41

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)
2012-04-05 01:19
by Jörg W Mittag
Your list got me thinking about Ruby's sugar. It's interesting that the string interpolator #{} (e.g. "say hi, #{my_name}") isn't sugar for a method call . The first page from the index of the pick axe book has a nice list - SooDesuNe 2012-09-19 15:09
@SooDesuNe: It does however call to_s - Jörg W Mittag 2012-09-19 15:34


8

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.

2012-04-04 21:00
by jigfox


0

It's an operator overloader, it overrides or supplements the behavior of a method inside a class you have defined, or a class the behavior of which you are modifying. You can do it to other operators different from []. In this case you are modifying the behavior of [] when it is called on any instances of class SongList.

If you have songlist = SongList.new and then you do songlist["foobar"] then your custom def will come into operation and will assume that "foobar" is to be passed as the parameter (key) and it will do to "foobar" whatever the method says should be done to key.

Try

class Overruler
    def [] (input)
          if input.instance_of?(String)
            puts "string"
          else
            puts "not string"
          end
     end
end
foo = Overruler.new
foo["bar"].inspect
foo[1].inspect
2018-01-26 22:09
by codeSeeker
Ads