Create Array of Strings
Arrays of strings can be created using ruby's percent string syntax:
array = %w(one two three four)
This is functionally equivalent to defining the array as:
array = ['one', 'two', 'three', 'four']
Instead of %w()
you may use other matching pairs of delimiters: %w{...}
, %w[...]
or %w<...>
.
It is also possible to use arbitrary non-alphanumeric delimiters, such as: %w!...!
, %w#...#
or %w@...@
.
%W
can be used instead of %w
to incorporate string interpolation. Consider the following:
var = 'hello'
%w(#{var}) # => ["\#{var}"]
%W(#{var}) # => ["hello"]
Create Array with Array::new
An empty Array ([]
) can be created with Array's class method, Array::new
:
Array.new
To set the length of the array, pass a numerical argument:
Array.new(3)
This returns an array with three nil-values [nil, nil, nil]
.
To set the value of the elements in the array, add a second argument with the desired value:
Array.new(3, :x)
This returns [:x, :x, :x]
.
Instead of a second argument, it's also possible to pass a block, which will be evaluated for each index of the array to generate the array elements.
Array.new(4) { foo(bar) }
This returns an array with 4 elements, each equal to foo(bar)
.
The main reason to use a block is to pass the index of the element as an argument. Here are a few examples:
Array.new(3) { |i| i + 1 } #=> [1, 2, 3]
Array.new(3) { |i| i * 2 } #=> [0, 2, 4]
Array.new(3) { |i| Array.new(i + 1, i + 1) } #=> [[1], [2, 2], [3, 3, 3]]
Creating an Array with []
Manipulating Array Elements
Adding elements:
[1, 2, 3] << 4
# => [1, 2, 3, 4]
[1, 2, 3].push(4)
# => [1, 2, 3, 4]
[1, 2, 3].unshift(4)
# => [4, 1, 2, 3]
[1, 2, 3] << [4, 5]
# => [1, 2, 3, [4, 5]]
Removing elements:
array = [1, 2, 3, 4]
array.pop
# => 4
array
# => [1, 2, 3]
array = [1, 2, 3, 4]
array.shift
# => 1
array
# => [2, 3, 4]
array = [1, 2, 3, 4]
array.delete(1)
# => 1
array
# => [2, 3, 4]
Combining arrays:
[1, 2, 3] + [4, 5, 6]
# => [1, 2, 3, 4, 5, 6]
[1, 2, 3].concat([4, 5, 6])
# => [1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6] - [2, 3]
# => [1, 4, 5, 6]
[1, 2, 3] | [2, 3, 4]
# => [1, 2, 3, 4]
[1, 2, 3] & [3, 4]
# => [3]
You can also multiply arrays, e.g.
[1, 2, 3] * 2
# => [1, 2, 3, 1, 2, 3]
#map
#map
, provided by Enumerable, creates an array by invoking a block on each element and collecting the results:
[1, 2, 3].map { |i| i * 3 }
# => [3, 6, 9]
['1', '2', '3', '4', '5'].map { |i| i.to_i }
# => [1, 2, 3, 4, 5]
The original array is not modified; a new array is returned containing the transformed values in the same order as the source values.
In map
method you can call method or use proc to all elements in array.
# call to_i method on all elements
%w(1 2 3 4 5 6 7 8 9 10).map(&:to_i)
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# using proc (lambda) on all elements
%w(1 2 3 4 5 6 7 8 9 10).map(&->(i){ i.to_i * 2})
# => [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
map
is synonymous with collect
.
Accessing elements
You can access the elements of an array by their indices.
Array index numbering starts at 0
.
%w(a b c)[0] # => 'a'
%w(a b c)[1] # => 'b'
You can crop an array using range
%w(a b c d)[1..2] # => ['b', 'c']
This returns a new array, but doesn't affect the original. Ruby also supports the use of negative indices.
%w(a b c)[-1] # => 'c'
%w(a b c)[-2] # => 'b'
Other useful methods
Use first
to access the first element in an array:
[1, 2, 3, 4].first # => 1
Or first(n)
to access the first n
elements returned in an array:
[1, 2, 3, 4].first(2) # => [1, 2]
Similarly for last
and last(n)
:
[1, 2, 3, 4].last # => 4
[1, 2, 3, 4].last(2) # => [3, 4]
Arrays and the splat (*) operator
The *
operator can be used to unpack variables and arrays so that they can be passed as individual arguments to a method.
This can be used to wrap a single object in an Array if it is not already:
def wrap_in_array(value)
[*value]
end
wrap_in_array(1)
#> [1]
wrap_in_array([1, 2, 3])
#> [1, 2, 3]
wrap_in_array(nil)
#> []
In the above example, the wrap_in_array
method accepts one argument, value
.
If value
is an Array
, its elements are unpacked and a new array is created containing those element.
If value
is a single object, a new array is created containing that single object.
If value
is nil
, an empty array is returned.
This can be useful when working with sets as it allows nil
, single values and arrays to be handled in a consistent manner:
def list(values)
[*values] do |value|
# do something with value
puts value
end
end
list(100)
#> 100
list([100, 200])
#> 100
#> 200
list(nil)
# nothing is outputted
Arrays union, intersection and difference
x = [5, 5, 1, 3]
y = [5, 2, 4, 3]
Union (|
) contains elements from both arrays, with duplicates removed:
x | y
=> [5, 1, 3, 2, 4]
Intersection (&
) contains elements which are present both in first and second array:
x & y
=> [5, 3]
Difference (-
) contains elements which are present in first array and not present in second array:
x - y
=> [1]
Filtering arrays
Often we want to operate only on elements of an array that fulfill a specific condition:
Select
Will return elements that matche a specific condition
array = [1, 2, 3, 4, 5, 6]
array.select { |number| number > 3} # => [4, 5, 6]
Reject
Will return elements that do not match a specific condition
array = [1, 2, 3, 4, 5, 6]
array.reject { |number| number > 3} # => [1, 2, 3]
Both #select
and #reject
return an array, so they can be chained:
array = [1, 2, 3, 4, 5, 6]
array.select { |number| number > 3 }.reject { |number| number < 5}
# => [5, 6]
Get all combinations / permutations of an array
The permutation
method, when called with a block yields a two dimensional array consisting of all ordered sequences of a collection of numbers.
If this method is called without a block, it will return an enumerator
. To convert to an array, call the to_a
method.
Example | Result |
---|---|
[1,2,3].permutation | #<Enumerator: [1,2,3]:permutation |
[1,2,3].permutation.to_a | [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] |
[1,2,3].permutation(2).to_a | [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]] |
[1,2,3].permutation(4).to_a | [] -> No permutations of length 4 |
The combination
method on the other hand, when called with a block yields a two-dimensional array consisting of all sequences of a collection of numbers. Unlike permutation, order is disregarded in combinations. For example, [1,2,3]
is the same as [3,2,1]
Example | Result |
---|---|
[1,2,3].combination(1) | #<Enumerator: [1,2,3]:combination |
[1,2,3].combination(1).to_a | [[1],[2],[3]] |
[1,2,3].combination(3).to_a | [[1,2,3]] |
[1,2,3].combination(4).to_a | [] -> No combinations of length 4 |
Calling the combination method by itself will result in an enumerator. To get an array, call the to_a
method.
The repeated_combination
and repeated_permutation
methods are similar, except the same element can be repeated multiple times.
For example the sequences [1,1]
, [1,3,3,1]
, [3,3,3]
would not be valid in regular combinations and permutations.
Example | # Combos |
---|---|
[1,2,3].combination(3).to_a.length | 1 |
[1,2,3].repeated_combination(3).to_a.length | 6 |
[1,2,3,4,5].combination(5).to_a.length | 1 |
[1,2,3].repeated_combination(5).to_a.length | 126 |
Remove all nil elements from an array with #compact
If an array happens to have one or more nil
elements and these need to be removed, the Array#compact
or Array#compact!
methods can be used, as below.
array = [ 1, nil, 'hello', nil, '5', 33]
array.compact # => [ 1, 'hello', '5', 33]
#notice that the method returns a new copy of the array with nil removed,
#without affecting the original
array = [ 1, nil, 'hello', nil, '5', 33]
#If you need the original array modified, you can either reassign it
array = array.compact # => [ 1, 'hello', '5', 33]
array = [ 1, 'hello', '5', 33]
#Or you can use the much more elegant 'bang' version of the method
array = [ 1, nil, 'hello', nil, '5', 33]
array.compact # => [ 1, 'hello', '5', 33]
array = [ 1, 'hello', '5', 33]
Finally, notice that if #compact
or #compact!
are called on an array with no nil
elements, these will return nil.
array = [ 'foo', 4, 'life']
array.compact # => nil
array.compact! # => nil
Cast to Array from any object
To get Array from any object, use Kernel#Array
.
The following is an example:
Array('something') #=> ["something"]
Array([2, 1, 5]) #=> [2, 1, 5]
Array(1) #=> [1]
Array(2..4) #=> [2, 3, 4]
Array([]) #=> []
Array(nil) #=> []
For example, you could replace join_as_string
method from the following code
def join_as_string(arg)
if arg.instance_of?(Array)
arg.join(',')
elsif arg.instance_of?(Range)
arg.to_a.join(',')
else
arg.to_s
end
end
join_as_string('something') #=> "something"
join_as_string([2, 1, 5]) #=> "2,1,5"
join_as_string(1) #=> "1"
join_as_string(2..4) #=> "2,3,4"
join_as_string([]) #=> ""
join_as_string(nil) #=> ""
to the following code.
def join_as_string(arg)
Array(arg).join(',')
end
Create an Array of consecutive numbers or letters
This can be easily accomplished by calling Enumerable#to_a
on a Range
object:
(1..10).to_a #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
or
a_range = 1...5
a_range.to_a #=> [1, 2, 3, 4]
or
('a'..'f').to_a #=> ["a", "b", "c", "d", "e", "f"]
Create Array of numbers
The normal way to create an array of numbers:
numbers = [1, 2, 3, 4, 5]
Range objects can be used extensively to create an array of numbers:
numbers = Array(1..10) # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
numbers = (1..10).to_a # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
#step
and #map
methods allow us to impose conditions on the range of numbers:
odd_numbers = (1..10).step(2).to_a # => [1, 3, 5, 7, 9]
even_numbers = 2.step(10, 2).to_a # => [2, 4, 6, 8, 10]
squared_numbers = (1..10).map { |number| number * number } # => [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
All the above methods load the numbers eagerly. If you have to load them lazily:
number_generator = (1..100).lazy # => #<Enumerator::Lazy: 1..100>
number_generator.first(10) # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Destructuring
Any array can be quickly destructured by assigning its elements into multiple variables. A simple example:
arr = [1, 2, 3]
# ---
a = arr[0]
b = arr[1]
c = arr[2]
# --- or, the same
a, b, c = arr
Preceding a variable with the splat operator (*
) puts into it an array of all the elements that haven't been captured by other variables. If none are left, empty array is assigned. Only one splat can be used in a single assignment:
a, *b = arr # a = 1; b = [2, 3]
a, *b, c = arr # a = 1; b = [2]; c = 3
a, b, c, *d = arr # a = 1; b = 2; c = 3; d = []
a, *b, *c = arr # SyntaxError: unexpected *
Destructuring is safe and never raises errors. nil
s are assigned where there's not enough elements, matching the behavior of []
operator when accessing an index out of bounds:
arr[9000] # => nil
a, b, c, d = arr # a = 1; b = 2; c = 3; d = nil
Destructuring implicitly tries to call to_ary
implicitly on the object being assigned. By implementing this method in your type you get the ability to destructure it:
class Foo
def to_ary
[1, 2]
end
end
a, b = Foo.new # a = 1; b = 2
If the object being destructured doesn't respond_to?
to_ary
, it's treated as a single-element array:
1.respond_to?(:to_ary) # => false
a, b = 1 # a = 1; b = nil
Get unique array elements
In case you need to read an array elements avoiding repetitions you case use the #uniq
method:
a = [1, 1, 2, 3, 4, 4, 5]
a.uniq
#=> [1, 2, 3, 4, 5]
Instead, if you want to remove all duplicated elements from an array, you may use #uniq!
method:
a = [1, 1, 2, 3, 4, 4, 5]
a.uniq!
#=> [1, 2, 3, 4, 5]
While the output is the same, #uniq!
also stores the new array:
a = [1, 1, 2, 3, 4, 4, 5]
a.uniq
#=> [1, 2, 3, 4, 5]
a
#=> [1, 1, 2, 3, 4, 4, 5]
a = [1, 1, 2, 3, 4, 4, 5]
a.uniq!
#=> [1, 2, 3, 4, 5]
a
#=> [1, 2, 3, 4, 5]
Inject, reduce
Inject and reduce are different names for the same thing. In other languages these functions are often called folds (like foldl or foldr). These methods are available on every Enumerable object.
Inject takes a two argument function and applies that to all of the pairs of elements in the Array.
For the array [1, 2, 3]
we can add all of these together with the starting value of zero by specifying a starting value and block like so:
[1,2,3].reduce(0) {|a,b| a + b} # => 6
Here we pass the function a starting value and a block that says to add all of the values together. The block is first run with 0
as a
and 1
as b
it then takes the result of that as the next a
so we are then adding 1
to the second value 2
. Then we take the result of that (3
) and add that on to the final element in the list (also 3
) giving us our result (6
).
If we omit the first argument, it will set a
to being the first element in the list, so the example above is the same as:
[1,2,3].reduce {|a,b| a + b} # => 6
In addition, instead of passing a block with a function, we can pass a named function as a symbol, either with a starting value, or without. With this, the above example could be written as:
[1,2,3].reduce(0, :+) # => 6
or omitting the starting value:
[1,2,3].reduce(:+) # => 6
Two-dimensional array
Ruby does not have built-in support for two-dimensional arrays, but you can create an array that contains arrays inside it:
size1 = 3
size2 = 4
array = size1.times.map { size2.times.map { 0 } }
The array generated above looks like this when printed with p
:
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
You can read or write to elements like this:
x = array[0][1]
array[2][3] = 2