Chained [0][0][0][1] ends inside a String
Question
What is the return value of the following Ruby code?
words = [["abc", ["def"]], "ghi"]
words[0][0][0][1] # => ???
The correct answer is
-
["def"] -
"b" -
"ghi" -
nilCorrect
Explanation
TL;DR
Each [...] applies to the result of the previous one, and the chain changes type midway. words[0] is an Array, but words[0][0] is the String "abc". From there String#[] takes over: "abc"[0] is "a", and "a"[1] asks for the second character of a one-character string, which is nil.
Step by step
words = [["abc", ["def"]], "ghi"]
words[0] # => ["abc", ["def"]]
words[0][0] # => "abc"
words[0][0][0] # => "a"
words[0][0][0][1] # => nil
The trap is the silent switch of receiver class. The first two [] are Array#[]; the last two are String#[], which indexes characters and returns one-character strings:
"abc"[0] # => "a"
"abc"[1] # => "b"
"abc"[3] # => nil
The distractor answers correspond to different chains:
words = [["abc", ["def"]], "ghi"]
words[1] # => "ghi"
words[0][1] # => ["def"]
And "b" would require indexing "abc"[1], not "abc"[0][1].
Edge cases
Out-of-range indexing returns nil for both arrays and strings, with one classic exception: a range that starts exactly at the end returns an empty slice:
"abc"[3] # => nil
"abc"[3..] # => ""
"abc"[4..] # => nil
[1, 2, 3][3] # => nil
[1, 2, 3][3..] # => []
Array#dig walks nested collections, but it does not silently cross into strings; it raises instead, which turns a wrong assumption about the nesting into a loud error:
words = [["abc", ["def"]], "ghi"]
words.dig(0, 0) # => "abc"
words.dig(0, 1, 0) # => "def"
begin
words.dig(0, 0, 0, 1)
rescue TypeError => e
e.message # => "String does not have #dig method"
end
Version notes
In Ruby 1.8, "abc"[0] returned the character code 97. Since Ruby 1.9, String#[] with an Integer index returns a one-character string, which is what makes this chain produce "a" and then nil.
Share this quiz
Comments
No comments yet. Be the first.