Version Sort (With Alphas, Betas, etc.) in Ruby

Version sort (with alphas, betas, etc.) in ruby

Ruby ships with the Gem class, which knows about versions:

ar = ['10.0.0b12', '10.0.0b3', '10.0.0a2', '9.0.10', '9.0.3']

p ar.sort_by { |v| }
# => ["9.0.3", "9.0.10", "10.0.0a2", "10.0.0b3", "10.0.0b12"]

How to sort an alphanumeric array in ruby

You can pass a block to the sort function to custom sort it. In your case you will have a problem because your numbers aren't zero padded, so this method zero pads the numerical parts, then sorts them, resulting in your desired sort order.

a.sort { |a,b|
ap = a.split('_')
a = ap[0] + "%05d" % ap[1] + "%05d" % ap[2]
bp = b.split('_')
b = bp[0] + "%05d" % bp[1] + "%05d" % bp[2]
b <=> a

How can I sort an array of strings with numbers like

arr.sort_by{|a| a.split('.').map &:to_i }

Which will:

  1. split each of the strings into components.
  2. Change each of them into integers (map &:to_i).
  3. Compare between each other these arrays.

How to do version numbers?


major: Really a marketing decision. Are you ready to call the version 1.0? Does the company consider this a major version for which customers might have to pay more, or is it an update of the current major version which may be free? Less of an R&D decision and more a product decision.

minor: Starts from 0 whenever major is incremented. +1 for every version that goes public.

release: Every time you hit a development milestone and release the product, even internally (e.g. to QA), increment this. This is especially important for communication between teams in the organization. Needless to say, never release the same 'release' twice (even internally). Reset to 0 upon minor++ or major++.

build: Can be a SVN revision, I find that works best.

My current chrome: 83.0.4103.61

Making my own sort method in Ruby

Here are a few observations about your code:

  • since test_obj is a string, '#{tested_obj}' is the same as #{tested_obj}, which is the same as tested_obj.
  • declaring sorted_array = [] has no effect. Being a local variable, it is not within the scope of teh method recursive_sort. That method receives an array that it calls sorted_array, so you would not want it initialized anyway.
  • you don't need to create the new array, still_unsorted; simply transfer elements from unsorted_array to sorted_array.

Below I've fixed and tightened up your code.

  def recursive_sort(unsorted_array, sorted_array = []) 
return sorted_array unless unsorted_array.length > 0
smallest = unsorted_array.min
unsorted_array.each {|e| sorted_array << e if e == smallest}
recursive_sort(unsorted_array, sorted_array)

unsorted_array = ['gamma', 'alpha', 'delta', 'beta', 'gamma', 'alpha', 'zeta']
p recursive_sort unsorted_array
# => ["alpha", "alpha", "beta", "delta", "gamma", "gamma", "zeta"]

Here's what's happening:

  • by giving the second argument of recursive_sort (sorted_value) a default value of [] (an empty array), there is no need for the method sort you had previously.
  • sorted_array is returned if sorting is finished (same as return sorted_array if unsorted_array.length == 0).
  • use Enumerable#min to find the smallest value of the unsorted items (smallest).
  • add each instance of smallest in unsorted_array to sorted_array.
  • delete all instances of smallest in unsorted_array.
  • call the same method again, to remove the next smallest unsorted item, etc.


  unsorted_array.each {|e| sorted_array << e if e == smallest}

could be expressed in many different ways. Here's one:

  sorted_array += [smallest]*(unsorted_array.count {|e| e == smallest})

To see how this works, suppose smallest = 'alpha'. Then

  unsorted_array.count {|e| e == 'alpha'} # => 2

so the above expression is:

  sorted_array += ['alpha']*2

which is

  sorted_array += ['alpha', 'alpha']

which adds two "alpha"'s to sorted_array.

Related Topics

Leave a reply