Hash#to_a
hash.to_a Hash#to_a converts a dictionary to a nested array where each element is a two-element array containing a key-value pair. This method is useful when you need to work with key-value data in contexts that expect arrays, or when performing array-based transformations.
The conversion is often most useful as a bridge. You can sort, filter, partition, or map the pairs as arrays, then turn them back into a map when you are done. That flow is handy when the operation you need is easier to express with array methods than with hash iteration. It also gives you a simple way to inspect the exact order of entries when the insertion order matters for a report or a quick script. The array form keeps the key and value together, which makes it easy to pass the data into another method without reshaping it first.
Syntax
hash.to_a
The method takes no arguments and always returns a new array. Each element of that array is itself a two-element array where the first entry is the key and the second is the value, mirroring the insertion order of the original collection. This predictable output shape makes to_a a reliable bridge when you want to apply array logic to key-value data.
Examples
Basic conversion
user = { name: "Alice", age: 30, city: "London" }
user.to_a
# => [[:name, "Alice"], [:age, 30], [:city, "London"]]
The resulting nested array gives you direct access to each key-value pair as a two-element sub-array. You can pass this array form to methods like flatten, transpose, or any enumerable that expects a flat or nested list. The conversion is shallow: nested hashes inside values are left untouched by to_a, so only the top-level entries become sub-arrays.
Working with the result
scores = { alice: 95, bob: 87, charlie: 92 }
# Transpose back to hash
scores.to_a.to_h
# => { alice: 95, bob: 87, charlie: 92 }
A common pattern is to convert a collection to an array for processing and then convert it back. The round-trip through to_a and to_h preserves the entry order of the original structure, since Ruby hashes maintain insertion order. This means you can sort, filter, or rearrange the pairs without worrying about the order being shuffled when you rebuild the hash.
Sorting by keys or values
data = { z: 1, a: 3, m: 2 }
# Sort by key
data.to_a.sort
# => [[:a, 3], [:m, 2], [:z, 1]]
# Sort by value
data.to_a.sort_by { |key, value| value }
# => [[:z, 1], [:m, 2], [:a, 3]]
Sorting the array form works because Ruby compares arrays element by element. When you call sort on an array of pairs, it first compares keys, and if keys are equal it falls through to comparing values. The sort_by variant gives you explicit control: you can sort on the value, the key length, or any derived property of the pair.
Filtering and transforming
config = { host: "localhost", port: 3000, ssl: true, debug: false }
# Keep only string values
config.to_a.select { |key, val| val.is_a?(String) }
# => [[:host, "localhost"]]
# Transform to strings
config.to_a.map { |key, val| "#{key}=#{val}" }
# => ["host=localhost", "port=3000", "ssl=true", "debug=false"]
Once the hash is in array form, you can use any array method to reshape the data. select and reject let you keep or drop pairs based on a condition, while map transforms each pair into a new value. The block parameters destructure each sub-array into separate key and value variables, which keeps the code readable even when the transformation logic grows.
Building hashes from arrays
# Create hash from key-value pair arrays
pairs = [[:name, "Bob"], [:age, 25], [:city, "NYC"]]
pairs.to_h
# => { name: "Bob", age: 25, city: "NYC" }
# This is essentially what to_a does in reverse
{ a: 1, b: 2 }.to_a.to_h
# => { a: 1, b: 2 }
When you build a hash from an array of pairs, each inner array must have exactly two elements. A sub-array with one or three elements will raise an ArgumentError when you call to_h. The same constraint applies to the array returned by to_a, which is why the round-trip is reliable: to_a always produces well-formed pairs.
Common Patterns
Iteration with index
hash = { first: "a", second: "b", third: "c" }
hash.to_a.each_with_index do |(key, value), index|
puts "#{index}: #{key} => #{value}"
end
# 0: first => a
# 1: second => b
# 2: third => c
Iterating with an index over a hash is one of the strongest use cases for to_a. A hash enumerator yields key-value pairs, but the index is not part of that enumeration. Converting to an array first lets each_with_index track the position, which is handy for numbered output, progress reporting, or any situation where the entry number matters alongside the key and value.
Grouping and partitioning
data = { a: 1, b: 2, c: 3, d: 4, e: 5 }
even, odd = data.to_a.partition { |k, v| v.even? }
# even: [[:b, 2], [:d, 4]]
# odd: [[:a, 1], [:c, 3], [:e, 5]]
# Convert back to hashes
even.to_h
# => { b: 2, d: 4 }
partition splits the array of pairs into two groups based on the block condition, and each group can be converted back to a hash independently. This is often cleaner than iterating twice over the original hash. The same pattern works with group_by, slice_when, and other enumerable methods that operate on the array form.
Converting to nested arrays for JSON
hash = { users: [{ name: "Alice" }, { name: "Bob" }] }
# Sometimes needed for specific JSON serialization
hash.to_a
# => [[:users, [{ name: "Alice" }, { name: "Bob" }]]]
Some JSON serializers need data in a specific shape, and the nested array format can match that expectation without extra transformation. In most Ruby applications, however, hashes serialize to JSON objects directly, so this use case is rarer than the sorting or filtering patterns shown above.
Edge Cases
Empty hash
{}.to_a
# => []
An empty hash converts to an empty array, which is consistent with the idea that there are no key-value pairs to represent. The return value is always a new array object, so you can safely chain methods on the result without worrying about nil.
Single key-value pair
{ key: "value" }.to_a
# => [[:key, "value"]]
Even a single-entry hash still produces a nested array with one sub-array. The structure is always consistent: an outer array containing inner two-element arrays, regardless of how many entries the hash holds. That consistency makes the return type predictable and safe to pass to methods that expect this shape.
Mixed key types
{ "string" => 1, :symbol => 2, 3 => 3 }.to_a
# => [["string", 1], [:symbol, 2], [3, 3]]
Ruby hashes can mix key types freely, and to_a preserves each key exactly as it appears in the hash. This is useful for debugging or introspection: the array form makes it easy to inspect what types of keys a hash contains and whether any unexpected key objects have crept in.
Hash with array values
{ items: [1, 2, 3], tags: %w[a b] }.to_a
# => [[:items, [1, 2, 3]], [:tags, ["a", "b"]]]
When a hash value is itself an array, to_a wraps it inside the pair rather than flattening it. The nested structure stays intact, which means you can still tell the difference between a hash that maps a key to an array and a hash that maps several keys to individual values.
Performance Notes
Hash#to_a creates a new array and does not modify the original hash. For very large hashes, be aware that this conversion allocates additional memory proportional to the hash size.
If you only need to iterate over key-value pairs without converting, consider using Hash#each or Hash#each_pair instead, which are more memory-efficient.
In practice, to_a is best when the array form is temporary and clearly local to one operation. If the conversion is going to sit around for a long time, a key-value map is usually the better shape because it keeps lookup intent obvious and avoids the extra container.
That gives you a good rule of thumb: use to_a when you need an array tool, then convert the pairs back once the array-based work is done.
See Also
- Hash#keys — Get all keys as an array
- Hash#values — Get all values as an array
- Hash#each_pair — Iterate over key-value pairs