Oops, I forgot to verify that I was actually caching results. Here is
a fixed version.
module Enumerable
def sort_by_multi(*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 = 0
while i < key_methods.size do
for elem in [a, b] do
key = elem[1]
if elem[0].size <= i
elem[0] << key_methods[i][0].call(key)
puts " #{elem.inspect}"
end
end
if key_methods[i][1] == :desc
a, b = b, a
end
result = (a[0][i] <=> b[0][i])
return result unless result == 0
i += 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 = [
["radio", 30, 5],
["radio", 40, 5],
["radio", 20, 5],
["archie", 20, 5],
["newton", 10, 3]
]
puts a.sort_by_multi(
[Proc.new { |a| a[0] }],
[Proc.new { |a| a[1] }, :desc]
).inspect
You can see that it only evaluates the second key for tiebreaker
purposes. (Of course, in this example, the key calculation is
trivial, but in the real world you might have a key that is expensive
to evaluate.)
[["radio"], ["radio", 30, 5]]
[["radio"], ["radio", 20, 5]]
[["radio", 30], ["radio", 30, 5]]
[["radio", 20], ["radio", 20, 5]]
[["newton"], ["newton", 10, 3]]
[["radio"], ["radio", 40, 5]]
[["radio", 40], ["radio", 40, 5]]
[["archie"], ["archie", 20, 5]]
[["archie", 20, 5], ["newton", 10, 3], ["radio", 40, 5], ["radio", 30,
5], ["radio", 20, 5]]
···
On Oct 14, 8:00 am, Steve Howell <showel...@yahoo.com> wrote:
On Oct 13, 12:46 am, 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.
> b=[ ["radio", 30, 5], ["radio", 20, 5], ["archie", 20, 5],
> ["newton", 10, 3] ]
> 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:
> b.sort_by{|x| [ !x[1] ]}
> This works (one criteria), but this does not:
> 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 = 0
while i < key_methods.size do
for elem in [a, b] do
fields = elem[0]
key = elem[1]
if fields.size <= i
fields[i] = key_methods[i][0].call(key)
end
end
if key_methods[i][1] == :desc
a, b = b, a
end
result = (a[0][i] <=> b[0][i])
return result unless result == 0
i += 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 = [
["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