String#chars
str.chars -> array The .chars method breaks a string into an array of individual characters. Each character becomes a separate string element in the returned array, making it easy to iterate over, count, or transform individual characters.
Syntax
string.chars
Parameters
.chars takes no parameters. The method operates on the string it’s called on.
Examples
Basic usage
The syntax is deliberately simple — .chars takes no arguments and always returns an array. This predictability makes it easy to chain with other array methods without consulting the documentation for edge cases or optional parameters.
"hello".chars
# => ["h", "e", "l", "l", "o"]
"Ruby".chars
# => ["R", "u", "b", "y"]
The return value is always a flat array of one-character strings, even when the source string contains multi-byte characters. Empty strings produce an empty array: "".chars # => []. This makes the result predictable regardless of input length and avoids the surprise of nil or other sentinel values when the string is empty. The consistency of always returning an array — never nil — means you can chain .chars directly into other array methods without a nil check.
Working with Unicode
"café".chars
# => ["c", "a", "f", "é"]
"🎉🎊".chars
# => ["🎉", "🎊"]
Ruby handles multi-byte Unicode characters correctly in .chars — each emoji or accented character becomes one array element, not a sequence of raw bytes. This means .chars is safe for text that includes non-ASCII characters, unlike older byte-oriented approaches. The encoding awareness comes from Ruby’s built-in string encoding system, which tracks whether a string is UTF-8, ASCII, or another encoding.
Common Patterns
Counting characters
word = "antidisestablishmentarianism"
counts = word.chars.tally
# => {"a"=>6, "n"=>4, "t"=>5, "i"=>5, "d"=>2, "s"=>4, "e"=>4, "l"=>3, "h"=>1, "m"=>2, "r"=>1}
Chaining .chars.tally is a concise way to get a character frequency count in one expression. tally was added in Ruby 2.7 and counts how many times each element appears in the array, returning the result as a hash. This two-call chain replaces what used to require a manual loop with a counting hash.
Iterating over characters
"hello".chars.each { |c| puts c }
# h
# e
# l
# l
# o
The each call with .chars iterates character by character, which is the simplest way to process text one character at a time. For streams or very large strings, each_char is more memory-efficient because it does not build the full array first. Use .chars.each when you also need the array for other purposes.
Transforming characters
"hello".chars.map(&:upcase)
# => ["H", "E", "L", "L", "O"]
Mapping over the character array with map(&:upcase) transforms every character in a single pass. The result is an array of uppercase letters, which you can join back into a string with .join if needed. This pattern is common for simple text normalization where each character’s transformation is independent of its neighbors.
Checking for character presence
vowels = ["a", "e", "i", "o", "u"]
word = "education"
has_vowel = word.chars.any? { |c| vowels.include?(c) }
# => true
The any? check combined with .chars tests whether at least one character matches a condition. Because any? short-circuits — it stops as soon as it finds a match — this pattern does not scan the entire string when a vowel appears early. For strings where the match is expected to be found quickly, the overhead of building the character array is the main cost.
Performance Considerations
For very large strings, .chars creates a new array containing every character, which uses memory proportional to the string length. For simple iteration, consider using .each_char instead:
# More memory-efficient for large strings
"very long string".each_char { |c| process(c) }
The trade-off between .chars and .each_char is a classic memory-versus-convenience decision. .chars gives you an array that you can inspect, slice, pass around, and reuse — at the cost of allocating memory for every character. .each_char streams the characters one at a time, which is better for large inputs where you only need to process each character once.
Turning text into a character list
chars is a nice fit when the code needs to treat each character as a separate item. That can make counting, filtering, and per-character operations much easier to express than by working with the original string directly. Once the string becomes an array, the rest of the code can use familiar array tools like map, reject, and tally.
The tradeoff is memory, because the whole character list is built up front. For short strings that is usually fine, and the resulting array is easy to inspect in examples or tests. For longer text, each_char keeps the work streaming instead of collecting every character at once.
The array form is especially convenient when the next step wants array behavior right away, such as counting, mapping, or rejecting certain characters. It gives the code a clean bridge from a string to an enumerable shape. That makes it easy to keep the character-level work separate from the original text value.
That separation is helpful in examples because it makes the transformation easy to see in one line and the follow-up work easy to read in the next. A string becomes an array, and then the array tools take over. That is a simple pattern, but it is often exactly what a small text task needs.