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.

5
  • 1
    So you have to try to make your program not case sensitive. Commented Apr 22, 2015 at 20:53
  • You can overload <=> and write some custom sorting. Commented Apr 22, 2015 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 explaining this concept. Also Array#sort will allow you to specify any custom sorting you want Commented Apr 22, 2015 at 21:01
  • @engineersmnky ok, you are correct. You can however def <=> ... end and yeah, Array#sort is a better approach. Commented Apr 22, 2015 at 21:27
  • Reminds me of stackoverflow.com/q/28413845/477037 Commented Apr 23, 2015 at 10:05

2 Answers 2

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

Sign up to request clarification or add additional context in comments.

3 Comments

Thats a very sneaky answer. Loved the approach though. Thanks a lot
Nice one. Maybe each_char rather than chars to avoid the creation of a temporary array.
Had to think about this one for a while. Very clever.
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"

5 Comments

Note that this removes duplicate characters.
You could use an order array and sort_by { |c| order.index(c) }
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, 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".
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.