class Array

Practical examples and pitfalls for Array

Array examples: transform a list without losing the original

Practical notes by Ruby-Doc.org

Keep both lists available for the import screen

Consider an import screen with the clean tags displayed next to the original tags. The application needs both lists. map is used to create a new list, which includes only the transformed data. The string operations included here also create new strings; simply copying the outer list (or array) does not preserve mutable objects referenced by both lists.

Example 1
tags = [" Ruby ", "", "RUBY", " Docs "]
cleaned = tags.map { |tag| tag.strip.downcase }
              .reject(&:empty?).uniq
p cleaned
p tags
Expected output
["ruby", "docs"]
[" Ruby ", "", "RUBY", " Docs "]

Blank tag removal, case conversion and duplicate tag elimination occur at different times. Maintaining the input provides users with information on how two entries came to become one entry. If the UI wants to notify users that they entered a blank field as part of a record, do so prior to deleting those items from the cleaned list.

Create a separate mutable row

Small grids are excellent tools for identifying if you're misusing objects. When creating a grid using Array.new, passing a single empty array as the second argument causes each row to reference the same inner array. In the block form, a new inner array is created for each row instead. Run this example and compare the output for both grids before applying it to a table layout or a collection of records.

Example 2
shared = Array.new(2, [])
shared[0] << "reserved"
p shared

separate = Array.new(2) { [] }
separate[0] << "reserved"
p separate
Expected output
[["reserved"], ["reserved"]]
[["reserved"], []]

When your rows include hashes or strings ask yourself whether you should apply the same "ownership" questions. Duplication of the outer array with dup does not recursively duplicate its elements. Your draft's editable values may require their own duplicates, but some values, like immutable values, may not.

Read return values before chaining methods

A bang (!) after a method name tells you to read the individual method contract. map! returns the array. However, uniq! returns nil when no removals occurred. Thus, a chain of mutations that assumes each mutation returns an array might break if there is nothing left to remove. That already-clean input is easy to miss during a quick manual test.

Try testing three forms of importing for yourself: an empty list, a list that requires modification and a list that doesn't need to be changed. Decide how the caller should respond if an element is missing. In Ruby, an ordinary index operation can return nil if an indexed item is missing; fetch allows callers to determine whether an exception occurs or a default value is returned.

API reference: Array API reference

Related: Hash · Enumerable

An Array object is an ordered, integer-indexed collection of objects, called elements; the object represents an array data structure.

An element may be any object (even another array); elements may be any mixture of objects of different types.

Important data structures that use arrays include:

There are also array-like data structures:

Array Indexes

Array indexing starts at 0, as in C or Java.

A non-negative index is an offset from the first element:

A negative index is an offset, backwards, from the end of the array:

In-Range and Out-of-Range Indexes

A non-negative index is in range if and only if it is smaller than the size of the array. For a 3-element array:

A negative index is in range if and only if its absolute value is not larger than the size of the array. For a 3-element array:

Effective Index

Although the effective index into an array is always an integer, some methods (both within class Array and elsewhere) accept one or more non-integer arguments that are integer-convertible objects.

Creating Arrays

You can create an Array object explicitly with:

A number of Ruby methods, both in the core and in the standard library, provide instance method to_a, which converts an object to an array.

Example Usage

In addition to the methods it mixes in through the Enumerable module, class Array has proprietary methods for accessing, searching and otherwise manipulating arrays.

Some of the more common ones are illustrated below.

Accessing Elements

Elements in an array can be retrieved using the Array#[] method. It can take a single integer argument (a numeric index), a pair of arguments (start and length) or a range. Negative indices start counting from the end, with -1 being the last element.

arr = [1, 2, 3, 4, 5, 6]
arr[2]    #=> 3
arr[100]  #=> nil
arr[-3]   #=> 4
arr[2, 3] #=> [3, 4, 5]
arr[1..4] #=> [2, 3, 4, 5]
arr[1..-3] #=> [2, 3, 4]

Another way to access a particular array element is by using the at method

arr.at(0) #=> 1

The slice method works in an identical manner to Array#[].

To raise an error for indices outside of the array bounds or else to provide a default value when that happens, you can use fetch.

arr = ['a', 'b', 'c', 'd', 'e', 'f']
arr.fetch(100) #=> IndexError: index 100 outside of array bounds: -6...6
arr.fetch(100, "oops") #=> "oops"

The special methods first and last will return the first and last elements of an array, respectively.

arr.first #=> 1
arr.last  #=> 6

To return the first n elements of an array, use take

arr.take(3) #=> [1, 2, 3]

drop does the opposite of take, by returning the elements after n elements have been dropped:

arr.drop(3) #=> [4, 5, 6]

Obtaining Information about an Array

An array keeps track of its own length at all times. To query an array about the number of elements it contains, use length, count or size.

browsers = ['Chrome', 'Firefox', 'Safari', 'Opera', 'IE']
browsers.length #=> 5
browsers.count #=> 5

To check whether an array contains any elements at all

browsers.empty? #=> false

To check whether a particular item is included in the array

browsers.include?('Konqueror') #=> false

Adding Items to an Array

Items can be added to the end of an array by using either push or <<

arr = [1, 2, 3, 4]
arr.push(5) #=> [1, 2, 3, 4, 5]
arr << 6    #=> [1, 2, 3, 4, 5, 6]

unshift will add a new item to the beginning of an array.

arr.unshift(0) #=> [0, 1, 2, 3, 4, 5, 6]

With insert you can add a new element to an array at any position.

arr.insert(3, 'apple')  #=> [0, 1, 2, 'apple', 3, 4, 5, 6]

Using the insert method, you can also insert multiple values at once:

arr.insert(3, 'orange', 'pear', 'grapefruit')
#=> [0, 1, 2, "orange", "pear", "grapefruit", "apple", 3, 4, 5, 6]

Removing Items from an Array

The method pop removes the last element in an array and returns it:

arr =  [1, 2, 3, 4, 5, 6]
arr.pop #=> 6
arr #=> [1, 2, 3, 4, 5]

To retrieve and at the same time remove the first item, use shift:

arr.shift #=> 1
arr #=> [2, 3, 4, 5]

To delete an element at a particular index:

arr.delete_at(2) #=> 4
arr #=> [2, 3, 5]

To delete a particular element anywhere in an array, use delete:

arr = [1, 2, 2, 3]
arr.delete(2) #=> 2
arr #=> [1,3]

A useful method if you need to remove nil values from an array is compact:

arr = ['foo', 0, nil, 'bar', 7, 'baz', nil]
arr.compact  #=> ['foo', 0, 'bar', 7, 'baz']
arr          #=> ['foo', 0, nil, 'bar', 7, 'baz', nil]
arr.compact! #=> ['foo', 0, 'bar', 7, 'baz']
arr          #=> ['foo', 0, 'bar', 7, 'baz']

Another common need is to remove duplicate elements from an array.

It has the non-destructive uniq, and destructive method uniq!

arr = [2, 5, 6, 556, 6, 6, 8, 9, 0, 123, 556]
arr.uniq #=> [2, 5, 6, 556, 8, 9, 0, 123]

Iterating over an Array

Like all classes that include the Enumerable module, class Array has an each method, which defines what elements should be iterated over and how. In case of Array#each, all elements in self are yielded to the supplied block in sequence.

Note that this operation leaves the array unchanged.

arr = [1, 2, 3, 4, 5]
arr.each {|a| print a -= 10, " "}
# prints: -9 -8 -7 -6 -5
#=> [1, 2, 3, 4, 5]

Another sometimes useful iterator is reverse_each which will iterate over the elements in the array in reverse order.

words = %w[first second third fourth fifth sixth]
str = ""
words.reverse_each {|word| str += "#{word} "}
p str #=> "sixth fifth fourth third second first "

The map method can be used to create a new array based on the original array, but with the values modified by the supplied block:

arr.map {|a| 2*a}     #=> [2, 4, 6, 8, 10]
arr                   #=> [1, 2, 3, 4, 5]
arr.map! {|a| a**2}   #=> [1, 4, 9, 16, 25]
arr                   #=> [1, 4, 9, 16, 25]

Selecting Items from an Array

Elements can be selected from an array according to criteria defined in a block. The selection can happen in a destructive or a non-destructive manner. While the destructive operations will modify the array they were called on, the non-destructive methods usually return a new array with the selected elements, but leave the original array unchanged.

Non-destructive Selection

arr = [1, 2, 3, 4, 5, 6]
arr.select {|a| a > 3}       #=> [4, 5, 6]
arr.reject {|a| a < 3}       #=> [3, 4, 5, 6]
arr.drop_while {|a| a < 4}   #=> [4, 5, 6]
arr                          #=> [1, 2, 3, 4, 5, 6]

Destructive Selection

select! and reject! are the corresponding destructive methods to select and reject

Similar to select vs. reject, delete_if and keep_if have the exact opposite result when supplied with the same block:

arr.delete_if {|a| a < 4}   #=> [4, 5, 6]
arr                         #=> [4, 5, 6]

arr = [1, 2, 3, 4, 5, 6]
arr.keep_if {|a| a < 4}   #=> [1, 2, 3]
arr                       #=> [1, 2, 3]

What’s Here

First, what’s elsewhere. Class Array:

Here, class Array provides methods that are useful for:

Methods for Creating an Array

See also Creating Arrays.

Methods for Querying

Methods for Comparing

Methods for Fetching

These methods do not modify self.

Methods for Assigning

These methods add, replace, or reorder elements in self.

Methods for Deleting

Each of these methods removes elements from self:

Methods for Combining

Methods for Iterating

Methods for Converting

Other Methods