9
\$\begingroup\$

How should I approach refactoring this code?

def separate_comma(number)
  a = number.to_s.split('')
  b = a.size/3.0
  if a.size < 4
    p number.to_s 
  elsif a.size%3 == 0
    n = -4
    (b.to_i-1).times do |i|
      a.insert(n, ',')
      n -= 4
    end
    p a.join("")
  else
      n = -4
    b.to_i.times do |i|
      a.insert(n, ',')
      n -= 4
    end
  p a.join("")
  end
end


separate_comma(97425)    # => "97,425"
separate_comma(89552600) # => "895,926,600"
separate_comma(0)       # => "0"
separate_comma(100)     # => "100"
\$\endgroup\$
1
  • 2
    \$\begingroup\$ Just a hint: Remember i18n. Among other things the symbol for grouping digits isn't always a comma, so avoid hard-coding it. \$\endgroup\$
    – rjnilsson
    Commented Jul 4, 2013 at 8:08

3 Answers 3

14
\$\begingroup\$

I am not sure how hard may be for a newbie to take on functional programming, but if you are interested in new ways of programming, check it out. A more specific article on Ruby: FP with Ruby. If you think in terms of expressions (what things are) instead of statements (update, insert, delete, ...), code is more declarative (and usually shorter). I'll use no regexps to show an alternative approach to the existing answer:

def separate_comma(number)
  reverse_digits = number.to_s.chars.reverse
  reverse_digits.each_slice(3).map(&:join).join(",").reverse
end

If you want to support decimals:

def separate_comma(number)
  whole, decimal = number.to_s.split(".")
  whole_with_commas = whole.chars.to_a.reverse.each_slice(3).map(&:join).join(",").reverse
  [whole_with_commas, decimal].compact.join(".")
end
\$\endgroup\$
1
  • 1
    \$\begingroup\$ String::chars returns an Array so you don't need the to_a following chars \$\endgroup\$
    – cdmo
    Commented Oct 18, 2020 at 2:39
7
\$\begingroup\$

This does the same thing more succinctly

number.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse

source: https://stackoverflow.com/a/11466770/1429887

It converts the number to a string, reverses it, places a comma every three digits (if it is followed by a digit), then reverses it back to its original order.

You might want to make it a function such as

def format_number(number)
  number.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
end

so that you can call the process multiple times

However, this method does not work with numbers that contain 3+ decimals. To solve that:

def format_number(number)
  number = number.to_s.split('.')
  number[0].reverse!.gsub!(/(\d{3})(?=\d)/, '\\1,').reverse!
  number.join('.')
end

This method splits the integer into the whole number part and the decimal part. It then adds the comma to the whole number and then tacks on the second decimal part.

[edit]: the second line of that function should have been number[0] to get the first element (the whole number one). Sorry if you viewed it before I realized my mistake.

\$\endgroup\$
0
2
\$\begingroup\$

If you're using Ruby on Rails, there is a number helper for doing just that.

number_with_delimiter(12345,delimiter: ",")   # => 12,345
\$\endgroup\$
2
  • 6
    \$\begingroup\$ That function is part of ActionView. The question does not mention that Ruby on Rails is being used. \$\endgroup\$ Commented Sep 14, 2018 at 0:22
  • \$\begingroup\$ Good point.. that was an assumption. \$\endgroup\$
    – mikey9
    Commented Sep 14, 2018 at 21:31

Not the answer you're looking for? Browse other questions tagged or ask your own question.