Ognjen Regoje bio photo

MY NAME IS
Ognjen Regoje
BUT YOU CAN CALL ME OGGY


I make things that run on the web (mostly).
More /ABOUT me.

me@ognjen.io Twitter LinkedIn Github

Ruby String::negative

I had an interesting scenario in a project recently: I had to sort an array of objects in alphabetic order of one field but reverse alphabetic order of another.

Sorting an array in ascending order of one numerical field and descending order of another is simple enough:

array.sort_by{|x| [x.numeric_1, x.numeric_2 * -1]}

But there is no way to multiply a string by -1.

Until now!

class String
  def negative
    self.chars.map{|x|
      x.ord > 96 ?
        (90 - (x.ord - 97)).chr :
        (122 - (x.ord - 65)).chr }.join
  end
end

Basically, this creates a number line with 0 being between Z and a.

Because Z is 1 away from the “0”, it corresponds to -1. Similarly, a is one away from “0” in the other direction, therefore it corresponds to 1. This makes a the negative of Z. Similarly, z becomes the negative of A.

Example

Person = Struct.new(:f_name, :l_name)

entries = [
  Person.new("Kana", "Delle"),
  Person.new("Ana", "Belle"),
  Person.new("Zana", "Celle"),
  Person.new("Ana", "Zelle"),
]

puts entries.sort_by{|x| [x.f_name, x.l_name.negative]}

#<struct Person f_name="Ana", l_name="Zelle">
#<struct Person f_name="Ana", l_name="Belle">
#<struct Person f_name="Kana", l_name="Delle">
#<struct Person f_name="Zana", l_name="Celle">

Caveats:

  • I didn’t check the performance impact
  • I specifically only cared about [A..z]

#ruby #technical