11

I am using Rails 3.2.2 and I would like to quote all array elements with ' and return a string containing all those quoted and comma-separated elements. At this time I am using

['a', 'b', 'c'].collect {|x| "'#{x}'"}.join(", ")
# => "'a', 'b', 'c'"

but I think I can improve the above code (maybe by using a unknown to me Ruby method, if it exists). Is it possible?

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
Backo
  • 18,291
  • 27
  • 103
  • 170
  • 3
    What you have seems good to me. – Andrew Marshall May 18 '12 at 06:01
  • There's nothing to optimize, bu you can use aliases: `map` instead of `collect` and `*` instead of `join` if you're starving to make your code smaller: `%w[a b c].map{|x|"'#{x}'"}*', '`. – jdoe May 18 '12 at 06:23
  • 2
    What happens if one of the strings in the array contains a `'` by itself, e.g. `["a", "b'cd"]`? – Holger Just May 18 '12 at 06:29
  • 2
    What you have is good. Also, this is pure ruby. There's nothing "rails" about it. – Dty May 18 '12 at 06:41
  • @AndrewMarshall Maybe you should convert that to an answer to make this question answered? – N.N. Dec 22 '12 at 16:24

2 Answers2

13

I use

"'#{%w{a b c}.join("', '")}'"

Here is expanded version:

' # Starting quote
%w{a b c}.join("', '") # Join array with ', ' delimiter that would give a', 'b', 'c
' # Closing quote
Sigurd
  • 7,865
  • 3
  • 24
  • 34
  • It appears [your version performs better](http://stackoverflow.com/a/14032344/789593). – N.N. Dec 26 '12 at 08:47
  • Here's why: First of all it walk array once instead of twice(map and join). Secondly it creates less ruby objects - one wrapper string for actual result and one string as a join result. Initial solution creates new array for map result and new string for each element in array. It might show worsen performance on larger array since GC would take longer. – Sigurd Dec 26 '12 at 10:15
  • You might want to include that in your answer to explain why your code is preferable. Also, do you mean garbage collection with 'GC'? – N.N. Dec 26 '12 at 17:47
9

You can replace collect with its alias map and .join with the equivalent *. Finally, you can use the shortcut for writing an array of strings, %w(...), and you can use single quotes for the argument of .join/* as it does not use string interpolation (though it may be questionable if it preferable when it comes to performance).

%w(a b c).map {|x| "'#{x}'"} * ', '

It appears there is no performance difference between this version and the original but that that Sigurd's version is performing better:

Original  3.620000   0.000000   3.620000 (  3.632081)
This  3.640000   0.000000   3.640000 (  3.651763)
Sigurd's  2.300000   0.000000   2.300000 (  2.303195)

Code for benchmark:

require 'benchmark'

n = 1000000

Benchmark.bm do |x|
  x.report("Original") { n.times do
      ['a', 'b', 'c'].collect {|x| "'#{x}'"}.join(", ")
    end}
  x.report("This") { n.times do
      %w(a b c).map {|x| "'#{x}'"} * ', '
    end}
  x.report("Sigurd's") { n.times do
      "'#{%w{a b c}.join("', '")}'"
    end}
end
Community
  • 1
  • 1
N.N.
  • 8,336
  • 12
  • 54
  • 94