HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyMinor

Ruby implementation of Soundex algorithm

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
implementationalgorithmsoundexruby

Problem

I'm new to Ruby. I normally sling code in C#. What could I do to make this simple Soundex class more Rubyesque?

class Surname
  attr_accessor :value

  def initialize(input)
    @value = input
  end

  def soundex
    result = ''
    @value.chars.drop(1).each do |s|
      number = soundex_value(s).to_s
      result << number unless result[-1,1] == number
    end
    @value.chars.first << result.ljust(3,'0')
  end

  def soundex_value(s)
    case s
    when /[bfpv]/
      1
    when /[cgjkqsxz]/ 
      2
    when /[dt]/ 
      3
    when /l/ 
      4
    when /[mn]/ 
      5
    when /r/ 
      6
    else ''
    end
  end
end

def print_name(input)
  surname = Surname.new(input)
  puts(surname.value + ' => ' + surname.soundex)
end

['Smith', 'Johnson', 'Williams', 'Jones', 'Brown'].each do |s|
  print_name s
end


The output is:

Smith => S530
Johnson => J525
Williams => W452
Jones => J520
Brown => B650

Solution

Per your request, your code converted to the Ruby way perhaps would be as follows.

A small point: in our output, in the Ruby world, we would avoid the => fat comma which Rubyists (at least in the English-speaking world) call a 'hash-rocket' because:

  • It reads like Ruby's (older) hash accessing syntax.



  • In Ruby language documentation (e.g., see Array), the similar #=>, comprising a hash to introduce a comment, followed by a hash-rocket (presumably, this gave us the name), shows us the result (or value) of an individual line of code—the two meanings conflict.



BTW, we also would avoid ->, dash-greater-than, which in Ruby (syntax) generates a lambda.

class Soundex < String
  IGNORED_BEGINNING_LENGTH = 1
  MINIMUM_LENGTH = 3

  CASES = [ # Keep order.
    /[bfpv]/,
    /[cgjkqsxz]/,
    /[dt]/,
    /l/,
    /[mn]/,
    /r/,
  ]
  CASES_LENGTH = CASES.length

  def initialize(surname)
    a = surname.split ''
    kept    = a.take(IGNORED_BEGINNING_LENGTH).join ''
    indices = a.drop(IGNORED_BEGINNING_LENGTH).map do |e|
      (0...CASES_LENGTH).detect{|i| e =~ (CASES.at i)}
    end.compact
# Adjust to one-based notation; collapse repetition; right-pad with zeros.
    digits = indices.map(&:succ).join('').squeeze.ljust MINIMUM_LENGTH, '0'
    super kept + digits
  end

  def self.show(s) "#{s}: #{new s}" end
end

names = %w[Smith Johnson Williams Jones Brown Atchison]
names.each{|s| puts Soundex.show s}


gives the results

Smith: S530
Johnson: J525
Williams: W452
Jones: J520
Brown: B650
Atchison: A325

Code Snippets

class Soundex < String
  IGNORED_BEGINNING_LENGTH = 1
  MINIMUM_LENGTH = 3

  CASES = [ # Keep order.
    /[bfpv]/,
    /[cgjkqsxz]/,
    /[dt]/,
    /l/,
    /[mn]/,
    /r/,
  ]
  CASES_LENGTH = CASES.length

  def initialize(surname)
    a = surname.split ''
    kept    = a.take(IGNORED_BEGINNING_LENGTH).join ''
    indices = a.drop(IGNORED_BEGINNING_LENGTH).map do |e|
      (0...CASES_LENGTH).detect{|i| e =~ (CASES.at i)}
    end.compact
# Adjust to one-based notation; collapse repetition; right-pad with zeros.
    digits = indices.map(&:succ).join('').squeeze.ljust MINIMUM_LENGTH, '0'
    super kept + digits
  end

  def self.show(s) "#{s}: #{new s}" end
end

names = %w[Smith Johnson Williams Jones Brown Atchison]
names.each{|s| puts Soundex.show s}

Context

StackExchange Code Review Q#5689, answer score: 3

Revisions (0)

No revisions yet.