tags:

views:

123

answers:

6

let's say when I'm comparing values in ruby, i have a value in mind that no matter what I want, using sort on that value and anything else returns a -1 (so this value is default sorted as smaller than everything).

for example, let's say i want '100' to sort smaller 100% of the time against 99. so that if i'm sorting values in an array, and a comparison comes up between 100 and 99, 100 is sorted smaller (ie, -1 is returned). but, i want all the other cases to be normal (98 is smaller than 99, 50 is bigger than 30, etc)

edit: okay this is what i want

if i have an x and a y, i do not want to use

x <=> y

i want to use (in pseudocode and hand-wavy-ness)

x > y

which means, this x is always greater than this y

+1  A: 

Why don't you instead use a dictionary to keep values associated with their relative value? In this case, the string abc can be mapped to -1, and then just make sure no other values map to values equal to or less than -1.

Edit: If you're only concerned with one particular value breaking the norm, then this solution is not for you.

AlbertoPL
A: 

You could override the sort method for the object like so:

class Fixnum
  alias old_sort <=>

  def <=>(object)
    if (self == 100 or object == 100)
      return -1
    else
      return self.old_sort object
    end
  end
end

puts (101 <=> 100)

Edit 1: Above is a fully working example.

Edit 2: As stated by johannes in the comments, you really shouldn't implement this exact code. This is merely a simple example of how to override your sorting method to do domain-specific logic. Also, updated the code to reflect johannes' other comment about comparing with both object and self.

Topher Fangio
I don't think you can name methods a mixture of letters and symbols, i.e. `old_<=>` and `old<=>` are invalid method names.
rampion
@rampion Thanks, fixed now :-)
Topher Fangio
Monkeypatching some existing behaviour can have devastating impact. You can't possibly think of all functionality relying on the corrert behaiviour of `Fixnum#<=>`
johannes
While sorting in ruby, you never know if `100` is `self` or `object`. You have to consider both possibilities.
johannes
@johannes I don't actually expect him to implement this, I realize that overriding functionality of Fixnum is horrible, I was just trying to give him a working example that he could modify to suit his own needs.
Topher Fangio
A: 

One way to do it would be to implement a derivative class for your custom comparisons (http://www.ruby-doc.org/core/classes/Comparable.html)

Here's some sample code (and tests) for you:

class StrA < String
 include Comparable
 attr :str
 def <=>(anOther)
  if (str == "abc" && anOther.str == "abc")
   0
  elsif (str == "abc")
   -1
  elsif (anOther.str == "abc")
   1
  else
   str <=> anOther.str
  end
 end

 def initialize(str)
  @str = str
 end

 def inspect
  @str
 end
end

And the tests:

a = StrA.new("Z")
b = StrA.new("B")
c = StrA.new("abc")
d = StrA.new("")

a > b # 1
a > c # 1
c > a # -1
d > c # 1
c > d # -1
c < d # 1
c > d # -1

[a, b, c, d].sort! # [ "Z", "B", "", "abc"]
aronchick
+1  A: 

Easier to handle the specialness outside of the sort!

module Enumerable
  def sort_excluding(*vals)
    special,rest = partition {|x| vals.include?(x)}
    rest.sort + special
  end
end
glenn mcdonald
only works for sort/arrays. if he wants to compare object to object, this won't work.
aronchick
You need to do this in `module Enumerable` instead of `class Array`, `Array` includes `Enumerable`, so it works in `Array` and in all other classes which include `Enumerable`.
johannes
Good point, changed. Although I'm not sure it actually matters much in general practice. What else do you sort that isn't an Array or subclassed from one? Set and Hash convert to arrays for sort. (Even in 1.9 where Hashes preserve order!)
glenn mcdonald
A: 

Array#sort or Enumerable#sort(I don't know what you are trying to sort) can take an obtional block. If the block is given, the block is used for comparison instead of <=>

For example this code will sort reversed:

foo.sort { |a,b| b <=> a }

In your case you need to call #sort something like the following:

foo.sort do |a,b|
  if [a,b].sort == [99,100]
    b-a # returns 1 or -1 so that 99 > 100
  else
    a <=> b
  end
end

I am not entirely sure about the way you are trying to sort, but this should enable you to use sort in the manner you need. More inforamtion about Array#sort, and any other method can be found on your linux(and possible other OS's) via ri like this: ri Array#sort.

johannes
A: 

I think what you want is:

[30, 50, 4, 0, 100, -22, 99].sort_by {|a| [a == 100 ? -1 : 0, a ]}.reverse

which gives:

99
50
30
 4
 0
-22
100

Hope I understood the question!

edavey
He only want's 100 to be smaller than everything else. The rest is supposed to be ordinary. You could do this with `a == 100 ? 1.0/0 : a`. But only if your elements to sort never contain negative infinity.
johannes