Roman numeral converter I'm writing a program to convert integers to Roman numerals (naively -- it doesn't know how to do the subtraction trick yet). What I have is functional, but it is not "Good Ruby". 

VALUES = [
  ["M", 1000], 
  ["D", 500], 
  ["C", 100], 
  ["L", 50], 
  ["X", 10], 
  ["V", 5], 
  ["I", 1], 
]

def romanize n
  roman = ""

  VALUES.each do |pair|
    letter = pair[0]
    value = pair[1]
    roman += letter*(n / value)
    n = n % value
  end
  return roman
end


Perhaps a hash makes more sense than the array of arrays, but the way I update n, order matters. Passing in pair to the block is dumb, but passing letter, value didn't work in the way I expected.
I don't know what you mean about 'good ruby'. I show you a object-oriented version. 
Instead of 

romanize(4)


you can call

4.roman


My solution is similar to your solution. It is Hash-oriented, so you need ruby 1.9.

I also tried to use a array in array solution with ROMAN_NUMBERS.each do |value, letter| without any problem (I'm not sure if it is only ruby 1.9).

class Fixnum
  ROMAN_NUMBERS = {
    1000 =&gt; "M",  
     900 =&gt; "CM",  
     500 =&gt; "D",  
     400 =&gt; "CD",
     100 =&gt; "C",  
      90 =&gt; "XC",  
      50 =&gt; "L",  
      40 =&gt; "XL",  
      10 =&gt; "X",  
        9 =&gt; "IX",  
        5 =&gt; "V",  
        4 =&gt; "IV",  
        1 =&gt; "I",  
  }

  def roman
    n = self
    roman = ""
    ROMAN_NUMBERS.each do |value, letter|
      roman &lt;&lt; letter*(n / value)
      n = n % value
    end
    return roman
  end
end


With ruby 1.8 you may add use ROMAN_NUMBERS.sort.each.

As an alternative, you can use a recursive solution:

class Fixnum
  ROMAN_NUMBERS = {
    1000 =&gt; "M",  
     900 =&gt; "CM",  
     500 =&gt; "D",  
     400 =&gt; "CD",
     100 =&gt; "C",  
      90 =&gt; "XC",  
      50 =&gt; "L",  
      40 =&gt; "XL",  
      10 =&gt; "X",  
        9 =&gt; "IX",  
        5 =&gt; "V",  
        4 =&gt; "IV",  
        1 =&gt; "I",  
        0 =&gt; "",  
  }
  def roman
    return '' if self == 0
    ROMAN_NUMBERS.each do |value, letter|
      return ( letter * (self / value)) &lt;&lt; (self % value).roman if value &lt;= self
    end
    return (self % value).roman
  end
end


I don't recommend the recursive solution. Each number must start again with M.



A little testcode, to check the results:

require 'test/unit'

class NumberTest &lt; Test::Unit::TestCase
  def test_0; assert_equal('',0.roman); end
  def test_1; assert_equal('I',1.roman); end
  def test_2; assert_equal('II',2.roman); end
  def test_3; assert_equal('III',3.roman); end
  def test_4; assert_equal('IV',4.roman); end
  def test_5; assert_equal('V',5.roman); end
  def test_6; assert_equal('VI',6.roman); end
  def test_7; assert_equal('VII',7.roman); end
  def test_8; assert_equal('VIII',8.roman); end
  def test_9; assert_equal('IX',9.roman); end
  def test_10; assert_equal('X',10.roman); end
  def test_11; assert_equal('XI',11.roman); end
  def test_12; assert_equal('XII',12.roman); end
  def test_13; assert_equal('XIII',13.roman); end
  def test_14; assert_equal('XIV',14.roman); end
  def test_15; assert_equal('XV',15.roman); end
  def test_16; assert_equal('XVI',16.roman); end
  def test_20; assert_equal('XX',20.roman); end
  def test_30; assert_equal('XXX',30.roman); end
  def test_40; assert_equal('XL',40.roman); end
  def test_50; assert_equal('L',50.roman); end
  def test_60; assert_equal('LX',60.roman); end
  def test_70; assert_equal('LXX',70.roman); end
  def test_80; assert_equal('LXXX',80.roman); end
  def test_90; assert_equal('XC',90.roman); end
  def test_99; assert_equal('XCIX',99.roman); end
  def test_100; assert_equal('C',100.roman); end
  def test_200; assert_equal('CC',200.roman); end
  def test_300; assert_equal('CCC',300.roman); end
  def test_400; assert_equal('CD',400.roman); end
  def test_500; assert_equal('D',500.roman); end
  def test_600; assert_equal('DC',600.roman); end
  def test_900; assert_equal('CM',900.roman); end
  def test_1000; assert_equal('M',1000.roman); end
  def test_2000; assert_equal('MM',2000.roman); end
  def test_2003; assert_equal('MMIII',2003.roman); end
end