dgsan
dgsan

Reputation: 285

What's a good way to sort strings in ruby so that empty strings are at the end?

In Ruby the default sort puts empty strings first.

['', 'g', 'z', 'a', 'r', 'u', '', 'n'].sort

Gives:

["", "", "a", "g", "n", "r", "u", "z"]

However, it's quite common to want empty strings at the end instead.

Doing something like:

['', 'g', 'z', 'a', 'r', 'u', '', 'n'].sort { |a, b| a[0] && b[0] ?  a <=> b : a[0] ? -1 : b[0] ? 1 : 0 }

works and gives:

["a", "g", "n", "r", "u", "z", "", ""]

However, this isn't very readable and is not very flexible.

Is there a reasonable and clean way to get sort in Ruby to put empty strings last? Would it be better to just map to an array with no empty strings, sort, and then pad on the empty strings at the end? Are there other approaches?

Upvotes: 9

Views: 1510

Answers (4)

Tom Lord
Tom Lord

Reputation: 28305

Here's another variation, defining a custom sort comparison:

arr = ["g", "u", "", "a", "", "r", "n", "z"]

arr.sort { |s1, s2| (s1.empty? || s2.empty?) ? (s2 <=> s1) : (s1 <=> s2) }
  #=> ["a", "g", "n", "r", "u", "z", "", ""]

Using s2 <=> s1 is essentially a "reverse sort" - so in cases where an empty string is being compared against, this orders it at the end of the result rather than the beginning.

Upvotes: 1

Cary Swoveland
Cary Swoveland

Reputation: 110685

arr = ["g", "u", "", "a", "", "r", "n", "z"]

arr.sort_by { |s| [s.empty? ? 1 : 0, s] }
  #=> ["a", "g", "n", "r", "u", "z", "", ""]

or

arr.sort_by { |s| s.empty? ? 255.chr : s }
  # => ["a", "g", "n", "r", "u", "z", "", ""]

or

empty, non_empty = arr.partition(&:empty?)
  #=> [["", ""], ["g", "u", "a", "r", "n", "z"]]
non_empty.sort.concat empty
  #=> ["a", "g", "n", "r", "u", "z", "", ""]

Upvotes: 9

engineersmnky
engineersmnky

Reputation: 29373

The simplest solution I can think of is

a = ['', 'g', 'z', 'a', 'r', 'u', '', 'n']
a.sort.rotate(a.count(''))
#=> ["a", "g", "n", "r", "u", "z", "", ""]

Array#rotate: Returns a new array by rotating self so that the element at count is the first element of the new array.

So we just rotate by the count of empty strings ("")

Upvotes: 4

asthasr
asthasr

Reputation: 9407

Just count the number of blanks and then use #rotate to move them to the end:

sorted = ['', 'g', 'z', 'a', 'r', 'u', '', 'n'].sort
blank_count = sorted.count &:empty?
sorted.rotate! blank_count

Upvotes: 2

Related Questions