41

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?

theTRON
  • 9,608
  • 2
  • 32
  • 46
Gracko
  • 447
  • 1
  • 4
  • 5
  • 2
    Possible Duplicate of http://stackoverflow.com/questions/800122/best-way-to-convert-strings-to-symbols-in-hash –  May 12 '13 at 00:00
  • @Abe: That's for keys of a hash. This is for array elements. – icktoofay May 12 '13 at 00:04
  • @Abe That is not a dup, it's for hashes. (edit: icktoofay beat me to it :P) – tckmn May 12 '13 at 00:04
  • 1
    @icktofay I see, but it would be easy to convert them, right? –  May 12 '13 at 00:05
  • 1
    @Abe That doesn't mean it's a duplicate.... Also, not really. Do you know Ruby? – tckmn May 12 '13 at 00:05
  • 1
    @Abe: Maybe, but you'd have a cleaner answer if you just started out trying to do it with an array. – icktoofay May 12 '13 at 00:05
  • @Doorknob Not really I confess :P But I'll keep the comment because it got an up vote –  May 12 '13 at 00:05
  • It should work. Not clear what you mean by not success. – sawa May 12 '13 at 00:18
  • For those saying the link recommended by @Abe isn't applicable, it is. The keys of a hash *ARE* an array. `{'a'=>1}.keys.class => Array`. The problem is you have to know how to identify where that array is being massaged from strings into a symbol. – the Tin Man May 12 '13 at 01:00
  • @theTinMan EXACTLY!!! –  May 12 '13 at 01:05
  • @Abe probably this is more of a duplicate of http://stackoverflow.com/questions/6496931/apply-method-to-array-elements – lulalala May 17 '13 at 02:53
  • @lulalala perhaps, but that's more of a global area rather than a local idea. –  May 18 '13 at 00:02
  • Try #map. `strings.map(&:to_sym)` – Nathan Apr 11 '15 at 02:46

9 Answers9

59

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.

icktoofay
  • 126,289
  • 21
  • 250
  • 231
33

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.

cb24
  • 577
  • 5
  • 7
  • 1
    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. – Lomefin Jul 09 '16 at 00:37
10

I'd do something like

strings.map! &:to_sym
DaniG2k
  • 4,772
  • 36
  • 77
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 }
Community
  • 1
  • 1
knut
  • 27,320
  • 6
  • 84
  • 112
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 }
Michaël Witrant
  • 7,525
  • 40
  • 44
Teddy
  • 18,357
  • 2
  • 30
  • 42
1

@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.

Lomefin
  • 1,173
  • 12
  • 43
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
Frank Koehl
  • 3,104
  • 2
  • 29
  • 37
0

Or can be done as follows:

strings.each do |s|  
symbols.push(s.to_sym)
blackbishop
  • 30,945
  • 11
  • 55
  • 76
Harsha
  • 11
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]
diemer
  • 341
  • 2
  • 7