Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

ruby

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?

like image 787
dgsan Avatar asked Jul 10 '18 17:07

dgsan


4 Answers

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", "", ""]
like image 64
Cary Swoveland Avatar answered Oct 18 '22 05:10

Cary Swoveland


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 ("")

like image 45
engineersmnky Avatar answered Oct 18 '22 03:10

engineersmnky


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
like image 42
asthasr Avatar answered Oct 18 '22 05:10

asthasr


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.

like image 25
Tom Lord Avatar answered Oct 18 '22 03:10

Tom Lord