Gracko
Gracko

Reputation: 467

How to convert a array of strings to an array of symbols?

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?

Upvotes: 43

Views: 45170

Answers (9)

diemer
diemer

Reputation: 399

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]

Upvotes: 0

Frank Koehl
Frank Koehl

Reputation: 3176

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

Upvotes: 1

Lomefin
Lomefin

Reputation: 1252

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

Upvotes: 2

Teddy
Teddy

Reputation: 18572

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

Upvotes: 2

cb24
cb24

Reputation: 637

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.

Upvotes: 37

DaniG2k
DaniG2k

Reputation: 4893

I'd do something like

strings.map! &:to_sym

Upvotes: 10

Harsha
Harsha

Reputation: 11

Or can be done as follows:

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

Upvotes: 0

knut
knut

Reputation: 27845

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 }

Upvotes: 4

icktoofay
icktoofay

Reputation: 128991

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.

Upvotes: 60

Related Questions