43

I want to convert the elements of the string array below to symbols, and output them

strings = ["HTML", "CSS", "JavaScript", "Python", "Ruby"]

look at what I'm doing:

strings.each { |x| puts x.to_sym }

No success. What am I doing wrong?

13
  • 2
    Possible Duplicate of stackoverflow.com/questions/800122/… Commented May 12, 2013 at 0:00
  • @Abe: That's for keys of a hash. This is for array elements. Commented May 12, 2013 at 0:04
  • 1
    @icktofay I see, but it would be easy to convert them, right? Commented May 12, 2013 at 0:05
  • 1
    @Abe That doesn't mean it's a duplicate.... Also, not really. Do you know Ruby? Commented May 12, 2013 at 0:05
  • 1
    @Abe: Maybe, but you'd have a cleaner answer if you just started out trying to do it with an array. Commented May 12, 2013 at 0:05

9 Answers 9

60

Use map rather than each:

>> strings.map { |x| x.to_sym }
=> [:HTML, :CSS, :JavaScript, :Python, :Ruby]

For Ruby 1.8.7 and later or with ActiveSupport included, you can use this syntax:

>> strings.map &:to_sym
=> [:HTML, :CSS, :JavaScript, :Python, :Ruby]

The reason your each method appears to not work is that calling puts with a symbol outputs the string representation of the symbol (that is, without the :). Additionally, you're just looping through and outputting things; you're not actually constructing a new array.

Sign up to request clarification or add additional context in comments.

Comments

37

Clean one-liner:

%w(HTML CSS JavaScript Python Ruby).map(&:to_sym)

& tells argument should be treated as a block, i.e. build up array and apply to_sym to each element.

1 Comment

Your answer is the cleanest of all, also I Benchmarked it and checked its performance on different kind of arrays. I wasn't sure if it was better to give a new answer based on yours or just edit this one.
10

I'd do something like

strings.map! &:to_sym

Comments

4

icktoofay already gave the correct answer.

On additional remark: With

strings.map { |x| x.to_sym }

you get a new array, the original array is unchanged.

To use it, you can assign it to another variable:

string2 = strings.map { |x| x.to_sym }

If you want to modify string, you can use map!:

strings.map! { |x| x.to_sym }

Comments

2

@icktoofay has the correct answer, but just to help you better understand the each method, here is how you can do the same thing using each:

strings = ["HTML", "CSS", "JavaScript", "Python", "Ruby"]
symbols = [] # an empty array to hold our symbols
strings.each { |s| symbols << s.to_sym }

Comments

2

@cb24's answer is generally the most appropiate, I wanted to compare that solution with another one

strings.collect {|x| x.to_sym }

I did some benchmarks and @cb24's answer works best in most cases, when there are some more elements in the array, but if it happens to be a very tiny array, the collect method works a little faster.

I publish here the code and the results, this is my real first benchmark so if I got something wrong some feedback would be appreciated. I did it on both String -> Symbol and Symbol -> String

n = 1000000 

a = [:a,:b,:c,:d,:e,:f,:g,:h,:i].freeze #A "long" array of symbols

Benchmark.bm do |x|
  x.report { n.times { a.map(&:to_s)} }
  x.report { n.times { a.collect{|x| x.to_s}} }
end  

       user     system      total        real
   2.040000   0.010000   2.050000 (  2.056784)
   2.100000   0.010000   2.110000 (  2.118546)


b = [:a, :b].freeze  #Small array

Benchmark.bm do |x|
  x.report { n.times { b.map(&:to_s)} }
  x.report { n.times { b.collect{|x| x.to_s}} }
end  


       user     system      total        real
   0.610000   0.000000   0.610000 (  0.622231)
   0.530000   0.010000   0.540000 (  0.536087)



w = %w(a b).freeze  #Again, a small array, now of Strings
Benchmark.bm do |x|
  x.report { n.times { w.map(&:to_sym)} }
  x.report { n.times { w.collect{|x| x.to_sym}} }
end 

       user     system      total        real
   0.510000   0.000000   0.510000 (  0.519337)
   0.440000   0.010000   0.450000 (  0.447990)



y = %w(a b c d e f g h i j k l m n o p q).freeze #And a pretty long one
Benchmark.bm do |x|
  x.report { n.times { y.map(&:to_sym)} }
  x.report { n.times { y.collect{|x| x.to_sym}} }
end 

       user     system      total        real
   2.870000   0.030000   2.900000 (  2.928830)
   3.240000   0.030000   3.270000 (  3.371295)

The inflection points I didn't calculate but it is quite interesting, I read somewhere that some improvements where made with short arrays, since most of them are just a couple of elements long.

Comments

1

If you want to go the gem route, finishing_moves has an Array#to_sym_strict method that does exactly what you're looking for:

strings = ["HTML", "CSS", "JavaScript", "Python", "Ruby"]
strings.to_sym_strict
# => [:HTML, :CSS, :JavaScript, :Python, :Ruby]

There's also a #to_sym_loose to handle arrays of mixed type:

strings = ["HTML", "CSS", "JavaScript", "Python", "Ruby", 1, /a regex/, {a: :hash}]
strings.to_sym_loose
# => [:HTML, :CSS, :JavaScript, :Python, :Ruby, 1, /a regex/, {a: :hash}]
# no exceptions thrown

Comments

0

Or can be done as follows:

strings.each do |s|  
symbols.push(s.to_sym)

Comments

0

In the interest of completeness you can also make use of the %i string literal since at least Ruby 2.0.0 to start off with an array of symbols.

%i[HTML CSS JavaScript Python Ruby]
# => [:HTML, :CSS, :JavaScript, :Python, :Ruby]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.