Array is a mutable data structure that stores a collection of elements of a specific type. An array is an indexable data structure. Arrays being mutable means that if a method is called on an array that modifies the array, the array will be modified. Meaning it doesn't create a new array but modifies the existing one.
To create an array, use the array literal denotation syntax ([]
) and within it, specify the elements of the array separated by a comma.
[1, 2, 3] # => [1, 2, 3]
Crystal will infer the type of the array from the elements.
[1, 2, 3].class # => Array(Int32)
Even if mentioned earlier about arrays being a collection of elements of a specific type, you can create an array with multiple types using union types. This allows the array to contain elements of any of the types specified in the union type. Crystal will infer the type of the array from the elements.
[1, "2", 3.0] # => [1, "2", 3.0]
[1, "2", 3.0].class # => Array(Int32 | Float64 | String)
A multi-dimensional array is an array of arrays.
This means that each element in the array is an array itself.
This can be useful when wanting to store data in a table format.
To define a multi-dimensional array, you can either write an array of arrays literal or use the Array.new
constructor.
[[1, 2], [3, 4]] Â Â Â Â Â Â Â Â Â Â # => [[1, 2], [3, 4]]
numbers = Array(Array(Int32)).new() # => []
numbers << [1, 2] Â Â Â Â Â Â Â Â Â # => [[1, 2]]
To add an element to an array, use the <<
(append) operator.
[1, 2, 3] << 4 # => [1, 2, 3, 4]
The type of the element you want to add must be the same as the type of the array, if it is not an error will be raised.
numbers : Array(Int32 | Float64) = [1, 2, 3]
numbers << 4.0
numbers # => [1, 2, 3, 4.0]
numbers << "5" # => Error: no overload matches 'Array(Int32 | Float64)#<<' with type String
As with String
, can you get the size of an array by using the size
method.
[1, 2, 3].size # => 3
The compiler cannot infer the array type when creating an empty array.
Therefore, you need to specify the type of the array.
This can be done by specifying the array's type, using the of
keyword, or using the array initializer syntax, which is Array(T).new
.
[] of (Int32 | Float64 | String) Â Â # => []
Array(Int32 | Float64 | String).new # => []
As with String
, you can access elements in an array by using the []
(index) operator and giving it the index of the element you want to access.
If the index is out of bounds, an IndexError
will be raised.
[1, 2, 3][0] # => 1
[1, 2, 3][3] # => Index out of bounds (IndexError)
It is also possible to access elements by using a range.
[1, 2, 3][0..1] # => [1, 2]
To create an array from a range, use the to_a
method.
This can be useful when you want to create an array of numbers.
(1..3).to_a # => [1, 2, 3]
To create an array from a string, use the split
method.
This lets you split a string into an array of strings using a delimiter.
"1,2,3".split(",") # => ["1", "2", "3"]
It is also possible to get an array of characters from a string using the chars
method.
"123".chars # => ['1', '2', '3']
To convert an array of Char
or String
to a String
you can use the join
method which takes a delimiter as an argument.
['1', '2', '3'].join(".") # => "1.2.3"
When you want to delete an element from the end of an array, you can use the pop
method, which takes an optional argument specifying how many elements to remove from the end of the array.
The method returns the element that was removed.
If the array is empty an IndexError
will be raised.
numbers = [1, 2, 3]
[1, 2, 3].pop # => 3
numbers    # => [1, 2]
empty_numbers = [] of Int32
empty_numbers.pop # => Index out of bounds (IndexError)
When you want to delete an element of a specific index from an array, you can use the delete_at
method which takes the element's index to remove as an argument.
If the array is empty an IndexError
will be raised.
numbers = [1, 2, 3]
[1, 2, 3].delete_at(1) # => 2
numbers         # => [1, 3]
empty_numbers = [] of Int32
empty_numbers.delete_at(0) # => Index out of bounds (IndexError)
When you want to modify an element of a specific index from an array, you can use the []=
(index assign) operator which takes the index of the element to modify and the new value as arguments.
If the index is out of bounds, an IndexError
will be raised.
numbers = [1, 2, 3]
numbers[1] = 4
numbers # => [1, 4, 3]
numbers[3] = 5 # => Index out of bounds (IndexError)
Arrays being mutable gives some properties unavailable for immutable data types/structures.
One of these properties is that arrays are passed by reference.
This means that when passing an array to a method, the method can modify it.
This has the benefit of not having to create a new array which gives better performance.
For example when working with String
every time you modify a string a new string is created which can be expensive.
But this can come with some behavior which is good to be aware of.
numbers = [1, 2, 3]
other_numbers = numbers
other_numbers << 4
other_numbers # => [1, 2, 3, 4]
numbers    # => [1, 2, 3, 4]
As you can see even though we only modified other_numbers
, numbers
was also modified.
This is because other_numbers
and numbers
point to the same array.
To avoid this behavior you can use the dup
method which creates a duplication of the array.
numbers = [1, 2, 3]
other_numbers = numbers.dup
other_numbers << 4
other_numbers # => [1, 2, 3, 4]
numbers    # => [1, 2, 3]