0

I have an array of strings, each element contains two or three numbers, one space and one letter.

array = [ "293 C",
 "401 B","421 B","428 B","439 B","315 C","529 B","560 B","566 B",
 "567 B","616 B","39 C","28 C","30 C","698 B","719 B","722 B",
 "640 B","786 B","645 B","236 B","255 B","442 C","446 C","477 C",
 "368 C","381 C","399 C","406 C","504 C","505 C","515 C","116 C",
 "138 C","147 C","174 C"]

What I need as a result is something like this.

["236 B","255 B","401 B","421 B","428 B","439 B","529 B","560 B",
"566 B","567 B","616 B","640 B","645 B","698 B","719 B","722 B",
"786 B","28 C","30 C", "39 C","116 C","138 C","147 C","174 C", "293 C",
"315 C", "368 C", "381 C","399 C","406 C","442 C","446 C",
"477 C","504 C","505 C","515 C"]

In other words, I need to sort the list of strings using the letter suffix as the major sort key, and the integer value of the digit prefix as the minor key.

I've tried with sort_by method, but it only allows me to sort the array by the letter (if I split each element first)

0

5 Answers 5

5
array.sort_by { |item|
  number, letter = *item.split
  [letter, number.to_i]
}

Arrays compare as their first element; in case an element is equal, next element is compared.

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

2 Comments

splat is redundant.
@mudasobwa: It is. And I am inconsistent in applying it. But the rule that autosplats arrays in multiple assignments is probably the one part of Ruby that I truly dislike.
2

Another alternative:

array.sort_by {|s| [s[/[a-zA-Z]+/], s.to_i]}

                        ^                      grab the letter or letters and the end
                                      ^        convert the digits at the front

Or, as point out in comments, it is better not to use a latin based character class.

You can do:

array.sort_by { |s| [s[s.index(/[\t ]\S/)+1..-1], s.to_i] }

                                 ^       tab, space followed by not a space

Or,

array.sort_by { |s| [s[/[^\d\t ]+/], s.to_i] }

                            ^             skip digits and spaces -- the rest

Or,

array.sort_by { |s| [s[/\p{L}+/], s.to_i] }

                           ^ code point in the category "letter".

9 Comments

...or { |s| [s[/\D+/], s.to_i] }.
s[/\D+/] picks up leading spaces before the letters. If there ever where 'noise' in the input data, such as an inconsistent number of spaces between the two columns, it would change the sort. s[s.index(/\s\S/)+1..-1] probably better.
@dawg well, it’s a naïve approach to think that letters means latin. I could bring more examples on the table if we were tête-à-tête, но вокруг, к сожалению, много наблюдателей. :)
\p{L} should do.
Google translate say Следуйте за деньгами => Follow the money
|
1

Here is one way to do it:

array.sort_by { |e| [e[-1], e.to_i] }

2 Comments

How is this answer different compared to the one by @Amadan?
@mudasobwa Not much, only differs in how the array is formed; and it can be used in a single line without the need of ; (BTW, I wasn't aware of Amadan's answer while posting).
1
array.sort_by { |s| s[-1] << s[/\d+/].rjust(3, '0') }
  #=> ["236 B", "255 B", "401 B",..., "722 B", "786 B",
  #     "28 C",  "30 C",  "39 C",..., "505 C", "515 C"]

Note:

array.map { |s| s[-1] << s[/\d+/].rjust(3, '0') }.sort
  #=> ["B236", "B255", "B401",..., "B722", "B786",
  #    "C028", "C030", "C039",..., "C505", "C515"]

Comments

0
arr.sort_by { |e| e.split.reverse.map { |s| s.to_i(36) } }

Comments

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.