5

Input string:

1654AaBcDddeeFF 

Output string:

1456acddeeABDFF

Code I tried:

test_array = []
'1654AaBcDddeeFF'.each_byte do |char|
  test_array << char
end

test_array.sort.pack('C*')
# => 1456ABDFFacddee

But I would like to see the upper case characters at last.

sawa
  • 165,429
  • 45
  • 277
  • 381
Appunu
  • 83
  • 4
  • 1
    So you have to try to make your program not case sensitive. – xsami Apr 22 '15 at 20:53
  • You can overload <=> and write some custom sorting. – Chase Apr 22 '15 at 20:55
  • @ChaseGilliam not to be a nazi but "overloading" is not a ruby convention or even possible. Jörg W Mittag Wrote an [excellent post](http://stackoverflow.com/a/9380268/1978251) explaining this concept. Also [`Array#sort`](http://ruby-doc.org/core-2.2.0/Array.html#method-i-sort) will allow you to specify any custom sorting you want – engineersmnky Apr 22 '15 at 21:01
  • @engineersmnky ok, you are correct. You can however def <=> ... end and yeah, Array#sort is a better approach. – Chase Apr 22 '15 at 21:27
  • Reminds me of http://stackoverflow.com/q/28413845/477037 – Stefan Apr 23 '15 at 10:05

2 Answers2

13

What about this?

p '1654AaBcDddeeFF'.each_char.sort_by(&:swapcase).join #=> "1456acddeeABDFF"

Edit: As @Cary Swoveland pointed out .chars is just a shortcut for .each_char.to_a and since we do not need to to_a here .each_char is a better method to use

hirolau
  • 13,451
  • 8
  • 35
  • 47
5

Since @hirolau's already taken swapcase, I offered an alternative (even though I prefer his answer). Alas, @Stefan identified a flaw, but suggested a nice fix:

str = '1654AaBcDddeeFF'

order_array = [*'0'..'9',*'a'..'z',*'A'..'Z']
str.each_char.sort_by { |c| order_array.index(c) }.join
  #=> "1456acdeABDF" 

(I am a mere scribe.)

One advantage of this approach is that you could use it for other orderings. For example, if:

str = '16?54!AaBcDdde,eFF'

and you also wanted to group the characters `'!?,' at the beginning, in that order, you could write:

order_array = [*'!?,'.chars,*'0'..'9',*'a'..'z',*'A'..'Z']
str.each_char.sort_by { |c| order_array.index(c) }.join
  #=> "!?,1456acdeABDF" 

We can make this a bit more efficient by converting order_array to a hash. For the last example:

order_hash = Hash[order_array.each_with_index.to_a]

then:

str.each_char.sort_by { |c| order_hash[c] }.join
  # => "!?,1456acddeeABDFF"
undur_gongor
  • 15,657
  • 5
  • 63
  • 75
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
  • Note that this removes duplicate characters. – Stefan Apr 22 '15 at 22:00
  • You could use an `order` array and `sort_by { |c| order.index(c) }` – Stefan Apr 22 '15 at 22:05
  • Looks nice. But is actual sorting happening here ? Like say are they sorted based on ASCII values when it comes to special characters ? To me it looks like mere grouping when it comes to symbols but am not sure, please enlighten me – Appunu Apr 22 '15 at 22:19
  • 1
    @Appunu, you'd have to include the special characters in order_array. For example, if `str = 'βΔ3Rαe'` and we want to modify my example to put the Greek letters at the end (lower then upper case), we would need `order_array = [*'0'..'9',*'a'..'z',*'A'..'Z',*'α'..'ω',*'Α'..'Ω']`, `str.each_char.sort_by { |c| order_array.index(c) }.join #=> "3eRαβΔ"`. Note also `str.each_char.map(&:swapcase).join #=> "βΔ3rαE"`. – Cary Swoveland Apr 22 '15 at 23:09
  • Eventhough I accepted the previous answer. This solution provided by @CarySwoveland is the optimal one under various situations where we need to involve special characters and symbols. – Appunu Apr 23 '15 at 16:48