What are all the different uses of the [square brackets] in Ruby? - ruby

I'm coming across the square bracket [] syntax quite a bit in Ruby, but it never seems to be doing the same thing. Can anyone list all the different uses for the square brackets [] in Ruby so my mind can get a handle on this seemingly endlessly versatile little symbol? (How is it possible that one symbol can do so much without the Ruby interpreter getting confused?)
Examples:
[] and []= methods
%q[...]
[1,2,3][0]
hash["a"] = 3
ary = []
/[^A-Fa-f0-9]/
"Is a string"[5,3]

The square brackets are in two strict contexts and one optional one:
Defining Arrays
Arrays, i.e. a data structure providing and ordered list of elements can be specified in code by using a syntax like [1,2,3]. This creates an array with the three elements 1, 2, and 3 in exactly that order. you can then iterate over the array using on of the iterator functions like each or map or you can directly access a specific elements by its index id as shown below.
Accessing Elements in Arrays and Hashes
Hashes (also called hashmaps, dictionaries, or associative arrays in other languages) also contain elements similar to arrays. The are different from this in the way that they store their data unordered. Data is not accessed by an integer id as is the case by arrays but with an arbitrary key (commonly a symbol or a string). This is different from e.g. PHP where the same Array type is used for both.
This access to the data is facilitated by methods called [] and []= for both hashes and arrays.
my_array = [:a, :b, :c]
second_element = my_array[1]
# => :b
# notice that the first element in arrays always has the index 0
my_hash = {:a => 1, :b => 2, :c => 3}
element_of_b = my_hash[:b]
# => 2
This is the common use case for the brackets. In Ruby code, you might sometimes see other classes implementing the bracket functions. They do so to allow an access similar to either arrays or hashes and it is then generally expected that these classes behave similar to those but this is in no way enforced. See also Duck Typing.
% Notation
Ruby has a third syntax to create strings (and other objects) apart from the common. Using this syntax, the literal string in code are not enclosed by " or ' but use a special delimiter. It starts with a percent sign, a single character specifying the object to be created and almost any character to chose as a delimiter:
a = %w[foo bar baz]
b = %w{foo bar baz}
c = %wxfoo bar bazx
d = ["foo", "bar", "baz"]
All three example create the same array. Please see the some documentation on how to use this syntax and which other modifier characters are available in Ruby.
While it is common to use brackets here, it is on no way required and can be substituted if required. It is just advisory here as the most common usage of this notation is to create an array of elements from a whitespace-seperated string (as seen above). As such, the usage of brackets makes it further clear that an array is returned as the syntax looks similar to the basic array specification.

Okay, just for my own notes I have gone and had a closer look at this and, building on Holger Just's answer, come up with the following: the use of square brackets in Ruby can be divided into 6 uses, 3 of them a part of Ruby's method definitions and 3 of them semantic constructs.
Method definition
Object creation via class methods Array::[], Hash::[]
Array.[](1,2,3) #=> [1,2,3] #Un-sugared notation
Array["a","b","c"] #=> ["a","b","c"] #Sugared equivalent
Hash["a", 100, "b", 200] #=> {"a"=>100, "b"=>200}
Nothing to do with literal constructors, although it does the same thing.
Element reference via instance methods Array#[], Bignum#[], Continuation#[], Fixnum#[], Hash#[], MatchData#[], Method#[], Proc#[], String#[], Struct#[], Symbol#[], Thread#[], and class methods Dir::[], ENV::[]
ary = [1,2,"abc", [15,16,[26,27]]]
ary.[](2) #=> "abc" #Un-sugared notation
ary[2] #=> "abc" #Sugared equivalent
ary[0,2] #=> [1,2]
ary[3][2][1] #=> 26
[1,2,3][0] #=> 1
"Is a string"[7,3] #=> "rin"
Element assignment via instance methods Array#[]=, Hash#[]=, String#[]=, Struct#[]=, Thread#[]=, and class method ENV::[]=
ary = [1,2,3]
ary.[]=(1,"abc") #=> [1,"abc",3] #un-sugared notation
ary[2] = "def" #=> [1,"abc","def"] #Sugared equivalent
hash = {"a"=>1, "b"=>2}
hash["a"] = 3 #=> {"a"=>3, "b"=>2}
Semantic constructs
Object creation via the array literal constructor
ary = []
There are a bunch of literal constructors in Ruby that create an object of the relevant class via the use of (usually) a simple symbol pair, square brackets being the literal constructor for array objects: Array [], Hash {}, Proc ->(){}, Range .. and ..., Regexp //, String "" and '', Symbol : and :"".
Object creation via the % notation
%q[hello there you] #=> "hello there you" # String % notation
%w[hello there you] #=> ["hello", "there", "you"] # Array % notation
It is not, strictly speaking, square-bracket notation, but rather two-symbol-pair notation of which you can use square brackets if you wish. So %q#hello there you# is equally valid.
Ruby's regular expressions
/[^A-Fa-f0-9]/
Square brackets indicate character classes in Ruby regular expressions.
I did find another use of the [], as a pattern for use in the Dir::glob method, but its supposed to act exactly as it does in regular expressions. Still, it indicates that there are possibly more uses hidden away in Ruby's 1500+ methods.

handy syntax for instantiating structs with square brackets
irb(main):001:0> Point = Struct.new(:x, :y)
=> Point
irb(main):002:0> point = Point[1,2]
=> #<struct Point x=1, y=2>
irb(main):003:0> point.x
=> 1
irb(main):004:0> point.y
=> 2

Related

Coercing a scalar or an array to become an array

Sometimes I want a variable to always be an array, whether its a scalar or already an array.
So I normally do:
[variable].flatten
which is compatible with ruby-1.8.5, 1.8.7, 1.9.x.
With this method when variable is a string (variable = "asdf"), it gives me ["asdf"]. If it's already an array (variable = ["asdf","bvcx"]), it gives me: ["asdf","bvcx"].
Does anyone have a better way? "Better" meaning more readable, more performant, succinct or more effective in other ways.
Array(variable)
should do the trick. It uses the little known Kernel#Array method.
The way I do, and think is the standard way, is using [*...]:
variable1 = "string"
variable2 = ["element1", "element2"]
[*variable1] #=> ["string"]
[*variable2] #=> ["element1", "element2"]
You might need something like Array.eat. Most other methods either call #to_a or #to_ary on the object. If you where using [ obj ].flatten that might give surprising results. #flatten will also mangle nested arrays unless called with a level parameter and will make an extra copy of the array.
Active support provides Array.wrap, but that also calls #to_ary, which might or might not be to your liking, depending on your needs.
require 'active_support/core_ext/array/wrap'
class Array
# Coerce an object to be an array. Any object that is not an array will become
# a single element array with object at index 0.
#
# coercing nil returns an empty array.
#
def self.eat( object )
object.nil? and return []
object.kind_of?( Array ) and return object
[object]
end
end # class Array
a = { a: 3 }
p [a].flatten # => [{:a=>3}]
p [*a] # => [[:a, 3]] -> OOPS
p Array a # => [[:a, 3]] -> OOPS
p Array.wrap a # => [{:a=>3}]
p Array.eat a # => [{:a=>3}]

Is it safe to add this #to_proc method to String to be able to map over Ruby hashes?

You can't use Enumerable#map to look up the same value from each element an array of
hashes using the &:method_name shortcut:
# INVALID:
[{a:'bar', b:'world'}, {a:'baz', b:'boston'}].map &:[:a]
But you can get around this by adding a #to_proc method to String. You can write this new #to_proc method so that
you use pass &"key" to the enumerator to look up a value by the key.
res = [{a:'bar', b:'world'}, {a:'baz', b:'boston'}].map &":a"
puts res.inspect
#=> ["bar", "baz"]
Compare to what you have to write otherwise:
res = [{a:'bar', b:'world'}, {a:'baz', b:'boston'}].map {|x| x[:a]}
Here is how you would patch the String class to make this work:
class String
def to_proc
->(x) { x.send :[], (self[0] == ':' ? self[1..-1].to_sym : self) }
end
end
Note that this will
only work with hashes with string or symbol keys.
My question: Is this safe to do and OK as far as good Ruby practices go? It's
a rather global change, but I'm not aware of any side effects this could have,
and it would arguably make my code more concise in a lot of places.
Instead of using hashes, consider using OpenStruct:
require 'ostruct'
a = [
OpenStruct.new(a:'bar', b:'world'),
OpenStruct.new(a:'baz', b:'boston'),
]
p a.map(&:a)
# => ["bar", "baz"]
You pay the price up front by having to wrap hashes in OpenStruct, but later use becomes easier, and without the potentially confounding effects of amending the behavior of base classes.
If the hashes have behavior, consider making them regular ol' classes.

How to convert a ruby integer into a symbol

I have a Ruby array like this
q_id = [1,2,3,4,5,...,100]
I want to iterate through the array and convert into a hash like this
{
:1 => { #some hash} ,
:2 => { #another hash},
...
:100 => {#yet another hash}
}
What is the shortest and most elegant way to accomplish this?
[EDIT : the to_s.to_sym while being handy is not how I want it. Apologies for not mentioning it earlier.]
For creating a symbol, either of these work:
42.to_s.to_sym
:"#{42}"
The #inspect representation of these shows :"42" only because :42 is not a valid Symbol literal. Rest assured that the double-quotes are not part of the symbol itself.
To create a hash, there is no reason to convert the keys to symbols, however. You should simply do this:
q_id = (1..100).to_a
my_hash_indexed_by_value = {}
q_id.each{ |val| my_hash_indexed_by_value[val] = {} }
Or this:
my_hash = Hash[ *q_id.map{ |v| [v,{}] }.flatten ]
Or this:
# Every time a previously-absent key is indexed, assign and return a new hash
my_hash = Hash.new{ |h,val| h[val] = {} }
With all of these you can then index your hash directly with an integer and get a unique hash back, e.g.
my_hash[42][:foo] = "bar"
Unlike JavaScript, where every key to an object must be a string, Hashes in Ruby accept any object as the key.
To translate an integer into a symbol, use to_s.to_sym .. e.g.,:
1.to_s.to_sym
Note that a symbol is more related to a string than an integer. It may not be as useful for things like sorting anymore.
Actually "symbol numbers" aren't a thing in Ruby (try to call the to_sym method on a number). The benefit of using symbols in a hash is about performance, since they always have the same object_id (try to call object_id on strings, booleans, numbers, and symbols).
Numbers are immediate value and, like Symbol objects, they always have the same object_id.
Anyway, using the new hash syntax implies using symbols as keys, but you can always use the old good "hash rocket" syntax
awesome_hash = { 1 => "hello", 2 => "my friend" }
Read about immediate values here:
https://books.google.de/books?id=jcUbTcr5XWwC&pg=PA73&lpg=PA73&dq=immediate+values+singleton+method&source=bl&ots=fIFlAe8xjy&sig=j7WgTA1Cft0WrHwq40YdTA50wk0&hl=en&sa=X&ei=0kHSUKCVB-bW0gHRxoHQAg&redir_esc=y#v=onepage&q&f=false
If you are creating a hard-coded constant numeric symbol, there's a simpler way:
:'99'
This produces the same results as the more complex methods in other answers:
irb(main):001:0> :'99'
=> :"99"
irb(main):002:0> :"#{99}"
=> :"99"
irb(main):003:0> 99.to_s.to_sym
=> :"99"
Of course, this will not work if you're dynamically creating a symbol from a variable, in which case one of the other two approaches is required.
As already stated, :1 is not a valid symbol. Here's one way to do what you're wanting, but with the keys as strings:
Hash[a.collect{|n| [n.to_s, {}] }]
An array of the objects you want in your hash would be so much easier to use, wouldn't it? Even a hash of integers would work pretty well, wouldn't it?
u can use
1.to_s.to_sym
but this will make symbols like :"1"
You can make symbolic keys with Hash[]:
a = Hash[(1..100).map{ |x| ["#{x}".to_sym, {}] }]
Check type of hash keys:
puts a.keys.map(&:class)
=>
Symbol
...
Symbol
Symbol

Inconsistent implicit hash creation in Ruby?

Ok, so I was comparing some stuff in my own DSL to Ruby. One construct they both support is this
x=["key" => "value"]
Knowing the difference between arrays and hashes, I would think this to be illegal, but the result in Ruby is
[{"key" => "value"}]
Why is this? And with this kinda syntax why can't you do
x=("key" => "value")
Why is an array a special case for implicitly created hashes?
Another special case is in a function call, consider:
def f(x)
puts "OK: #{x.inspect}"
end
f("foo" => "bar")
=> OK: {"foo"=>"bar"}
So in some contexts, Hashes can be built implicitly (by detecting the => operator?). I suppose the answer is just that this was Matz's least-surprising behavior.
With this apparent inconsistency in implicit hash creation, ruby achieves consistency in this regard:
func(whatever...)
can always be substituted with:
args = [whatever...]
func(*args)
You can convert between argument lists and arrays, and therefore it is logical that they have the same syntax.
I would say that the interpreter figures out that "key" => "value" is a hash, the same way it would figure out that 5 is a number when you put it into an array.
So if you write:
x = [5]
The interpreter is not going to think that it is a string, and return:
x = ["5"]
It seems that ruby implicitly creates hashes in some instances.

Ruby: How to loop through an object that may or may not be an array?

I have an each method that is run on some user-submitted data.
Sometimes it will be an array, other times it won't be.
Example submission:
<numbers>
<number>12345</number>
</numbers>
Another example:
<numbers>
<number>12345</number>
<number>09876</number>
</numbers>
I have been trying to do an each do on that, but when there is only one number I get a TypeError (Symbol as array index) error.
I recently asked a question that was tangentally similar. You can easily force any Ruby object into an array using Array.
p Array([1,2,3]) #-> [1,2,3]
p Array(123) #-> [123]
Of course, arrays respond to each. So if you force everying into an array, your problem should be solved.
A simple workaround is to just check if your object responds to :each; and if not, wrap it in an array.
irb(main):002:0> def foo x
irb(main):003:1> if x.respond_to? :each then x else [x] end
irb(main):005:1> end
=> nil
irb(main):007:0> (foo [1,2,3]).each { |x| puts x }
1
2
3
=> [1, 2, 3]
irb(main):008:0> (foo 5).each { |x| puts x }
5
=> [5]
It looks like the problem you want to solve is not the problem you are having.
TypeError (Symbol as array index)
That error tells me that you have an array, but are treating it like a hash and passing in a symbol key when it expects an integer index.
Also, most XML parsers provide child nodes as array, even if there is only one. So this shouldn't be necesary.
In the case of arguments to a method, you can test the object type. This allows you to pass in a single object or an array, and converts to an array only if its not one so you can treat it identically form that point on.
def foo(obj)
obj = [obj] unless obj.is_a?(Array)
do_something_with(obj)
end
Or something a bit cleaner but more cryptic
def foo(obj)
obj = [*obj]
do_something_with(obj)
end
This takes advantage of the splat operator to splat out an array if it is one. So it splats it out (or doesn't change it) and you can then wrap it an array and your good to go.
I was in the same position recently except the object I was working with was either a hash or an array of hashes. If you are using Rails, you can use Array.wrap because Array(hash) converts hashes to an array.
Array({foo: "bar"}) #=> [[:foo, "bar"]]
Array.wrap({foo: "bar"}) #=> [{:foo=>"bar"}]
Array.wrap(123) #=> [123]
Array.wrap([123]) #=> [123]
I sometimes use this cheap little trick:
[might_be_an_array].flatten.each { |x| .... }
Use the splat operator:
[*1] # => [1]
[*[1,2]] # => [1,2]
Like Mark said, you're looking for "respond_to?" Another option would be to use the conditional operator like this:
foo.respond_to? :each ? foo.each{|x| dostuff(x)} : dostuff(foo);
What are you trying to do with each number?
You should try to avoid using respond_to? message as it is not a very object oriented aproach.
Check if is it possible to find in the xml generator code where it is assigning an integer value when there is just one <"number"> tag and modify it to return an array.
Maybe it is a complex task, but I would try to do this in order to get a better OO design.
I don't know much anything about ruby, but I'd assume you could cast (explicitly) the input to an array - especially given that if the input is simply one element longer it's automatically converted to an array.
Have you tried casting it?
If your input is x, use x.to_a to convert your input into an array.
[1,2,3].to_a
=> [1, 2, 3]
1.to_a
=> [1]
"sample string".to_a
=> ["sample string"]
Edit: Newer versions of Ruby seem to not define a default .to_a for some standard objects anymore. You can always use the "explicit cast" syntax Array(x) to achieve the same effect.

Resources