On Oct 13, 12:46=A0am, Rahul Kumar <sentinel1... / gmail.com> wrote:
> I need to use *sort_by* to sort a table, since the user could select
> columns in any order.
>
> =A0 =A0 b=3D[ ["radio", 30, 5], ["radio", 20, 5], ["archie", 20, 5],
> ["newton", 10, 3] ]
> =A0 =A0 b.sort_by{|x| [ x[1], x[0] ]}
>
> Works fine. However, often the user will want a reverse sort on some
> field. I do not off-hand know the datatype of the field, but in most
> cases it will be a String.
>
> I tried:
>
> =A0 =A0 b.sort_by{|x| [ !x[1] ]}
> This works (one criteria), but this does not:
> =A0 =A0 b.sort_by{|x| [ !x[1], x[0] ]}
>
> Another thread here, suggested using a minus for Numbers but what about
> Strings.

Here is a solution that allows you to lazily evaluate keys and specify
that certain keys are to be reversed.

  module Enumerable
    def sort_by(*key_methods)
      # allow for multiple key_methods and only
      # evaluate them when they are truly needed
      # for the sort
      def compare(a,b, key_methods)
        i =3D 0
        while i < key_methods.size do
          for elem in [a, b] do
            fields =3D elem[0]
            key =3D elem[1]
            if fields.size <=3D i
              fields[i] =3D key_methods[i][0].call(key)
            end
          end
          if key_methods[i][1] =3D=3D :desc
            a, b =3D b, a
          end
          result =3D (a[0][i] <=3D> b[0][i])
          return result unless result =3D=3D 0
          i +=3D 1
        end
        return result
      end

      self.collect do |item|
        [ [], item ]
      end.sort do |a, b|
        compare(a, b, key_methods)
      end.collect do |kv|
        kv[1]
      end
    end
  end

  a =3D [
    ["radio", 30, 5],
    ["radio", 40, 5],
    ["radio", 20, 5],
    ["archie", 20, 5],
    ["newton", 10, 3]
  ]

  puts a.sort_by(
    [Proc.new { |a| a[0] }],
    [Proc.new { |a| a[1] }, :desc]
  ).inspect