Artyom Zankevich
Artyom Zankevich

Reputation: 459

How to sort a string based on the given characters order?

I am trying to solve this problem: given a string, you are asked to sort it according to characters order in other string:

Example:

> sort_string('foos', 'of')
=> 'oofs'

> sort_string('string', 'gnirts')
=> 'gnirts'

> sort_string('banana', 'abn')
=> 'aaabnn'

I've tried following implementation:

def sort_string(f_string, s_string)
  sanitize(s_string)
  s_string.chars.each do |e|
    f_string.length.times do |n|
      if f_string[n] == e
        s_string[e], s_string[n] = s_string[n], s_string[e]
      end
    end
  end
end

private

def sanitize(string)
  string.chars.uniq.join
end

But it gives me following error:

4_AStringOfSorts.rb:6:in `[]=': no implicit conversion of nil into String (TypeError)
        from 4_AStringOfSorts.rb:6:in `block (2 levels) in sort_string'
        from 4_AStringOfSorts.rb:4:in `times'
        from 4_AStringOfSorts.rb:4:in `block in sort_string'
        from 4_AStringOfSorts.rb:3:in `each'
        from 4_AStringOfSorts.rb:3:in `sort_string'
        from 4_AStringOfSorts.rb:18:in `'

Upvotes: 2

Views: 141

Answers (3)

Cary Swoveland
Cary Swoveland

Reputation: 110685

If @tokland's interpretation of the question is correct, it can also be done thus, without sorting per se:

def sort_string(s, order)
  sa = s.chars
  order.each_char.with_object('') do |c,str|
    while (idx = sa.index(c))
      str << sa.delete_at(idx)
    end
  end + sa.join
end

sort_string('zaefcf', 'fa')
  #=>"ffazec"

Here's another way that uses sort_by, but only on the part of the string that requires sorting:

def sort_string(s, order)
  sa = s.chars
  e = sa - order.chars
  (sa - e).sort_by { |c| order.index(c) }.concat(e).join
end

sort_string('zaefcf', 'fa')
  #=> "ffazec" 

Upvotes: 0

Andrea
Andrea

Reputation: 116

A similar approach:

def sort_string(s, order)
  order.chars.map { |c| c * s.count(c) }.join
end

Upvotes: 2

tokland
tokland

Reputation: 67870

Notes:

  • Your approach is not idiomatic in Ruby (it's overly complicated even by imperative standards)

  • sanitize(s_string): you don't capture the result of this method, so it's doing nothing.

A more idiomatic and functional approach:

def sort_string(s, order)
  s.chars.sort_by { |c| order.index(c) || order.size }.join
end

Upvotes: 4

Related Questions