Ruby Koans: Why convert list of symbols to strings

Ruby

Ruby Problem Overview


I'm referring to this test in about_symbols.rb in Ruby Koans https://github.com/edgecase/ruby_koans/blob/master/src/about_symbols.rb#L26

def test_method_names_become_symbols
  symbols_as_strings = Symbol.all_symbols.map { |x| x.to_s }
  assert_equal true, symbols_as_strings.include?("test_method_names_become_symbols")
end


  # THINK ABOUT IT:
  #
  # Why do we convert the list of symbols to strings and then compare
  # against the string value rather than against symbols?

Why exactly do we have to convert that list into strings first?

Ruby Solutions


Solution 1 - Ruby

This has to do with how symbols work. For each symbol, only one of it actually exists. Behind the scenes, a symbol is just a number referred to by a name (starting with a colon). Thus, when comparing the equality of two symbols, you're comparing object identity and not the content of the identifier that refers to this symbol.

If you were to do the simple test :test == "test", it will be false. So, if you were to gather all of the symbols defined thus far into an array, you would need to convert them to strings first before comparing them. You can't do this the opposite way (convert the string you want to compare into a symbol first) because doing that would create the single instance of that symbol and "pollute" your list with the symbol you're testing for existence.

Hope that helps. This is a bit of an odd one, because you have to test for the presence of a symbol without accidentally creating that symbol during the test. You usually don't see code like that.

Solution 2 - Ruby

Because if you do:

assert_equal true, all_symbols.include?(:test_method_names_become_symbols)

It may (depending on your ruby implementation) automatically be true, because :test_method_names_become_symbols creates the symbol. See this bug report.

Solution 3 - Ruby

Both answers above are correct, but in light of Karthik's question above I thought I would post a test that illustrates how one might accurately pass a symbol to the include method

def test_you_create_a_new_symbol_in_the_test
  array_of_symbols = []
  array_of_symbols << Symbol.all_symbols
  all_symbols = Symbol.all_symbols.map {|x| x}
  assert_equal false, array_of_symbols.include?(:this_should_not_be_in_the_symbols_collection)  #this works because we stored all symbols in an array before creating the symbol :this_should_not_be_in_the_symbols_collection in the test
  assert_equal true, all_symbols.include?(:this_also_should_not_be_in_the_symbols_collection) #This is the case noted in previous answers...here we've created a new symbol (:this_also_should_not_be_in_the_symbols_collection) in the test and then mapped all the symbols for comparison. Since we created the symbol before querying all_symbols, this test passes.
end

An additional note about the Koans: make use of puts statements as well as custom tests if you don't understand anything. For instance, if you see:

string = "the:rain:in:spain"
words = string.split(/:/)

and have no idea what words might be, add the line

puts words

and run rake at the command line. Likewise, tests like the one I added above can be helpful in terms of understanding some of the nuances of Ruby.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioncodercodeView Question on Stackoverflow
Solution 1 - RubyAboutRubyView Answer on Stackoverflow
Solution 2 - RubyAndrew GrimmView Answer on Stackoverflow
Solution 3 - RubyaceofbassgregView Answer on Stackoverflow