In Ruby, what's the difference between {} and []?
{} seems to be used for both code blocks and hashes.
Are [] only for arrays?
The documention isn't very clear.
It depends on the context:
When on their own, or assigning to a variable, [] creates arrays, and {} creates hashes. e.g.
a = [1,2,3] # an array
b = {1 => 2} # a hash
[] can be overridden as a custom method, and is generally used to fetch things from hashes (the standard library sets up [] as a method on hashes which is the same as fetch)
There is also a convention that it is used as a class method in the same way you might use a static Create method in C# or Java. e.g.
a = {1 => 2} # create a hash for example
puts a[1] # same as a.fetch(1), will print 2
Hash[1,2,3,4] # this is a custom class method which creates a new hash
See the Ruby Hash docs for that last example.
This is probably the most tricky one -
{} is also syntax for blocks, but only when passed to a method OUTSIDE the arguments parens.
When you invoke methods without parens, Ruby looks at where you put the commas to figure out where the arguments end (where the parens would have been, had you typed them)
1.upto(2) { puts 'hello' } # it's a block
1.upto 2 { puts 'hello' } # syntax error, ruby can't figure out where the function args end
1.upto 2, { puts 'hello' } # the comma means "argument", so ruby sees it as a hash - this won't work because puts 'hello' isn't a valid hash
Another, not so obvious, usage of [] is as a synonym for Proc#call and Method#call. This might be a little confusing the first time you encounter it. I guess the rational behind it is that it makes it look more like a normal function call.
E.g.
proc = Proc.new { |what| puts "Hello, #{what}!" }
meth = method(:print)
proc["World"]
meth["Hello",","," ", "World!", "\n"]
Broadly speaking, you're correct. As well as hashes, the general style is that curly braces {} are often used for blocks that can fit all onto one line, instead of using do/end across several lines.
Square brackets [] are used as class methods in lots of Ruby classes, including String, BigNum, Dir and confusingly enough, Hash. So:
Hash["key" => "value"]
is just as valid as:
{ "key" => "value" }
The square brackets [ ] are used to initialize arrays.
The documentation for initializer case of [ ] is in
ri Array::[]
The curly brackets { } are used to initialize hashes.
The documentation for initializer case of { } is in
ri Hash::[]
The square brackets are also commonly used as a method in many core ruby classes, like Array, Hash, String, and others.
You can access a list of all classes that have method "[ ]" defined with
ri []
most methods also have a "[ ]=" method that allows to assign things, for example:
s = "hello world"
s[2] # => 108 is ascii for e
s[2]=109 # 109 is ascii for m
s # => "hemlo world"
Curly brackets can also be used instead of "do ... end" on blocks, as "{ ... }".
Another case where you can see square brackets or curly brackets used - is in the special initializers where any symbol can be used, like:
%w{ hello world } # => ["hello","world"]
%w[ hello world ] # => ["hello","world"]
%r{ hello world } # => / hello world /
%r[ hello world ] # => / hello world /
%q{ hello world } # => "hello world"
%q[ hello world ] # => "hello world"
%q| hello world | # => "hello world"
a few examples:
[1, 2, 3].class
# => Array
[1, 2, 3][1]
# => 2
{ 1 => 2, 3 => 4 }.class
# => Hash
{ 1 => 2, 3 => 4 }[3]
# => 4
{ 1 + 2 }.class
# SyntaxError: compile error, odd number list for Hash
lambda { 1 + 2 }.class
# => Proc
lambda { 1 + 2 }.call
# => 3
Note that you can define the [] method for your own classes:
class A
def [](position)
# do something
end
def #rank.[]= key, val
# define the instance[a] = b method
end
end
Related
I understand that **args is interpreted as a hash containing all key value pairs passed to a function but I don't understand why that would be preferred over a typical parameter. For example, I have the following two functions.
def test(some_string, hash)
puts hash
puts hash.class # => Hash
end
def test_two(some_string, **hash)
puts hash
puts hash.class # => Hash
end
calling test("test string", a: 1, b: 2) or test_two("test string", a: 1, b: 2) produces the exact same result. What is the benefit of using ** as a parameter value?
Ruby 2.7 started more clearly differentiating between keyword arguments and regular hashes. **args is for keyword arguments. Some implications:
def test3(some_string, foo:, **args)
puts args
end
test3('a', foo: 'b', bar: 'c') # => {:bar=>"c"}
works as expected, however
def test3(some_string, foo:, hash)
puts args
end # => syntax error
def test3(some_string, hash, foo:)
puts args
end # works so far
test3('a', foo: 'b', bar: 'c')
# warning: Passing the keyword argument as the last hash parameter is deprecated
# ArgumentError (missing keyword: :foo)
Once you upgrade to ruby 3, the warnings turn to errors.
The benefit of having the double splat operator ** as argument is mainly that you can avoid passing any argument at all. Much like the same of what happens when you use the single splat operator *.
Using your examples and calling:
test "s"
# raises ArgumentError (wrong number of arguments (given 1, expected 2))
test_two "s"
# works, prints `{} Hash`
This is useful in methods where you want to have a "main" argument, and usually some options, without cluttering the arguments space.
For example, imagine a CSV row parser:
def parse_row_1(row, **options)
separator = options.fetch :separator, ","
quote_char = options.fetch :quote_char, null
# ...
end
def parse_row_2(row, separator = ",", quote_char = null)
# ...
end
# To parse a string like this:
s = "'ABC','123','DEF'"
# With ** method you can do just this:
parse_row_1 s, quote_char: "'"
# Without ** you must specify every time the arguments, because they are positional:
parse_row_2 s, ",", "'"
I noticed if you type:
object &, you get the object back.
For example:
1.class # => Integer
1 &.class # => Integer
'hello'.then { |x| x.equal?(x &.itself) } # => true
[1, 2, 3] &.map(&:next) # => [2, 3, 4]
I am unable to find a documentation for the syntax for object &.method
How does this syntax work?
There are 2 seperate operators here:
Safe navigation operator &. - It is safe navigation operator which was introduced in Ruby 2.3.0. It basically returns nil if the callee is nil instead of raising excecption undefined method called for Nil class. eg:
a = 1
a.next
# => 2
a&.next
# => 2
a = nil
a.next
# => NoMethodError (undefined method `next' for nil:NilClass)
a&.next
# => nil ## No exception, returns nil
You can read about it more here and documentation
Unary & : This operator is a little more complex. It is almost equivalent to calling #to_proc but not quite that. But for this discussion let us think like that. So, if you have a Proc, calling with & in front of it will call #to_proc on the Proc and convert it into a block
multiply_by_2 = Proc.new { |x| x * 2 }
# => #<Proc:0x00007fb4771cf560>
# &multiply_by_2 almost equivalent to { |x| x * 2 } but is not correct syntax
[1, 2].map(&multiply_by_2)
# => [2, 4]
# equivalent to [1, 2].map { |x| x * 2 }
But what happens if we give a symbol like :abc to & operator instead of a proc. It will try to call #to_proc on the symbol and ruby has defined Symbol#to_proc which roughly translates to something like this:
def to_proc
# this will return some block like { |x| x.send(:abc) }
lambda { |x| x.send(self) }
end
So &:abc roughly translates to this block { |x| x.abc } using the below transformation
&:abc =====> :abc.to_proc =====> { |x| x.send(:abc) } ====> { |x| x.abc }
So, instead of doing [1, 2, 3].map { |x| x.next }, you could do [1, 2, 3].map(&:next) as &:next is roughly equivalent to the block { |x| x.next }.
See unary & (which is the main source of what I have written here) for more reading.
It's ruby syntax, & calls to_proc on the object and passes the result as a block to the method.
An explanation from the pickaxe book, programming Ruby 1.9 and 2.0
Blocks Can Be Objects
Blocks are like anonymous methods, but there’s
more to them than that. You can also convert a block into an object,
store it in variables, pass it around, and then invoke its code later.
Remember we said that you can think of blocks as being like an
implicit parameter that’s passed to a method? Well, you can also make
that parameter explicit. If the last parameter in a method definition
is prefixed with an ampersand (such as &action ), Ruby looks for a
code block whenever that method is called. That code block is
converted to an object of class Proc and assigned to the parameter.
You can then treat the parameter as any other variable. Here’s an
example where we create a Proc object in one instance method and store
it in an instance variable. We then invoke the proc from a second
instance method.
class ProcExample
def pass_in_block(&action)
#stored_proc = action
end
def use_proc(parameter)
#stored_proc.call(parameter)
end
end
Use it like so
eg = ProcExample.new
eg.pass_in_block { |param| puts "The parameter is #{param}" }
eg.use_proc(99)
produces:
The parameter is 99
I have a rather simple regexp, but I wanted to use named regular expressions to make it cleaner and then iterate over results.
Testing string:
testing_string = "111x222b333"
My regexp:
regexp = %r{
(?<width> [0-9]{3} ) {0}
(?<height> [0-9]{3} ) {0}
(?<depth> [0-9]+ ) {0}
\g<width>x\g<height>b\g<depth>
}x
dimensions = regexp.match(testing_string)
This work like a charm, but heres where the problem comes:
dimensions.each { |k, v| dimensions[k] = my_operation(v) }
# ERROR !
undefined method `each' for #<MatchData "111x222b333" width:"111" height:"222" depth:"333">.
There is no .each method in MatchData object, and I really don't want to monkey patch it.
How can I fix this problem ?
I wasn't as clear as I thought: the point is to keep names and hash-like structure.
If you need a full Hash:
captures = Hash[ dimensions.names.zip( dimensions.captures ) ]
p captures
#=> {"width"=>"111", "height"=>"222", "depth"=>"333"}
If you just want to iterate over the name/value pairs:
dimensions.names.each do |name|
value = dimensions[name]
puts "%6s -> %s" % [ name, value ]
end
#=> width -> 111
#=> height -> 222
#=> depth -> 333
Alternatives:
dimensions.names.zip( dimensions.captures ).each do |name,value|
# ...
end
[ dimensions.names, dimensions.captures ].transpose.each do |name,value|
# ...
end
dimensions.names.each.with_index do |name,i|
value = dimensions.captures[i]
# ...
end
So today a new Ruby version (2.4.0) was released which includes many new features, amongst them feature #11999, aka MatchData#named_captures. This means you can now do this:
h = '12'.match(/(?<a>.)(?<b>.)(?<c>.)?/).named_captures
#=> {"a"=>"1", "b"=>"2", "c"=>nil}
h.class
#=> Hash
So in your code change
dimensions = regexp.match(testing_string)
to
dimensions = regexp.match(testing_string).named_captures
And you can use the each method on your regex match result just like on any other Hash, too.
I'd attack the whole problem of creating the hash a bit differently:
irb(main):052:0> testing_string = "111x222b333"
"111x222b333"
irb(main):053:0> hash = Hash[%w[width height depth].zip(testing_string.scan(/\d+/))]
{
"width" => "111",
"height" => "222",
"depth" => "333"
}
While regex are powerful, their siren-call can be too alluring, and we get sucked into trying to use them when there are more simple, or straightforward, ways of accomplishing something. It's just something to think about.
To keep track of the number of elements scanned, per the OPs comment:
hash = Hash[%w[width height depth].zip(scan_result = testing_string.scan(/\d+/))]
=> {"width"=>"111", "height"=>"222", "depth"=>"333"}
scan_result.size
=> 3
Also hash.size will return that, as would the size of the array containing the keys, etc.
#Phrogz's answer is correct if all of your captures have unique names, but you're allowed to give multiple captures the same name. Here's an example from the Regexp documentation.
This code supports captures with duplicate names:
captures = Hash[
dimensions.regexp.named_captures.map do |name, indexes|
[
name,
indexes.map { |i| dimensions.captures[i - 1] }
]
end
]
# Iterate over the captures
captures.each do |name, values|
# name is a String
# values is an Array of Strings
end
If you want to keep the names, you can do
new_dimensions = {}
dimensions.names.each { |k| new_dimensions[k] = my_operation(dimensions[k]) }
I'm getting to grips with rails and whilst I feel I am progressing there is one thing that I am struggling to get to grips with and it's very basic. I am trying to understand the different usage of [] {} and () Are there any good sources of their usage and are there any tips you can give to a beginner in recognizing when to use one or the other, or as I seem to see in some cases when they are not required at all?
I know this is extremely basic but I have struggled to find literature which explains concisely the interplay between them and Ruby or specifically RoR
It has nothing to do with RoR; the various brackets are Ruby language constructs.
[] is the array operator, for arrays and other classes that implement it (like a string taking a range to get substrings, or hashes to look up a key's value):
a = [1, 2, 3]
a.each { |n| puts n }
s = "ohai"
puts s[1..-1]
h = { foo: "bar", baz: "plugh" }
puts h[:foo]
{} is for hashes, and one of two ways of delimiting blocks (the other being begin/end). (And used with # for string interpolation.)
h = { foo: "bar", baz: "plugh" }
h.each { |k, v| puts "#{k} == #{v}" }
() is for method parameters, or for enforcing evaluation order in an expression.
> puts 5 * 3 + 5 # Normal precedence has * ahead of +
=> 20
> puts 5 * (3 + 5) # Force 3+5 to be evaluated first
=> 40
def foo(s)
puts(s)
end
They're sometimes optional if the statement has no ambiguity:
def foo s
puts s
end
(They're not always optional, and putting a space between the method call and its parenthetical parameter list can cause issues--best not to, IMO.)
(I probably missed something, too, but there's the nutshell.)
[] are used to access objects within a hash (via a key) or within an array (via an index).
hash[:key] # returns a value
array[0] # returns the first array element
[] is used to describe an array.
array = ['a', 'b', 'c']
Of course this can be nested.
nested = [['a','b','c'], [1,2,3]]
[] can be used to declare a hash, but that's because the Hash class can accept an array.
hash = Hash[['a',1], ['b',2]] # { 'a' => 1, 'b', => 2 }
{} is used to declare a hash.
hash = { 'a' => 1, 'b' => 2 }
This too can be nested.
hash = { 'a' => { 'c' => 3 }, 'b' => { 'd' => 4 } }
{} is also used to delimit blocks. The .each method is a common one. The following two blocks of code are equivalent.
array.each do |n|
puts n
end
array.each { |n| puts n }
The () is just used for grouping in cases where ambiguity needs clarification. This is especially true in methods that take many arguments, some of which may be nil, some of which may be obejcts, etc. You'll see a lot of code that omit them entirely as no grouping is needed for clarity.
puts(string)
puts string
I recommend firing up the rails console and start declaring variables and accessing them.
I am trying to understand Ruby in more depth and was reading:
http://www.zenspider.com/Languages/Ruby/QuickRef.html#25
However, I dont understand what the following means in that definition:
parameters := ( [param]* [, hashlist] [*array] [&aProc] )
I know "param" is any number of parameters specified, and then i get lost what the remainder means?
For example, I have:
def doIt(param1, param2, param3)
end
and in this case [param]* is equal to param1, param2, param3...so where does hashlist come in? and *array and &aProc?
Could someone please clarify this for me
If the last argument of a method is a non-empty hash literal, you can pass it like this
def foo(x, y, the_hash)
p the_hash['key2']
end
foo(0, 0, :key1 => 'val1', 'key2' => 42) # 42
instead of the normal way:
foo(0, 0, { :key1 => 'val1', 'key2' => 42 }) # 42
Usually, the hash is defaulted to {} (def foo(x, y, the_hash = {})) so passing an empty hash fits to this scheme.
Additionally, you can specify one "catch-all" (splat) argument which will become an array of all arguments not already assigned to normal arguments:
def foo(p1, *rest)
p rest
end
foo(0) # "[]"
foo(0, 23, 42) # "[23, 42]"
Or, e.g.
def foo(p1, *rest, p2)
p rest
end
foo(0, 100) # "[]"
foo(0, 100, 23, 42) # "[100, 23]"
You cannot have splat arguments before arguments with default value. Therefore, the hash argument syntax and the splat argument are rarely used in combination.
Finally, in your method definition you can have as last argument an identifier prefixed with & which will point to the block at the method invocation (its Proc object) or be nil if there is none. This is normally not needed if you just want to invoke the block -- you can use yield for that.
def foo(&p)
p.call
end
foo { puts "hello" } # hello
vs.
def foo
yield
end
foo { puts "hello" } # hello