Array#sort -block with conditions for <=>'ing

C

carp __

Hello Rubyists,

I want to sort an array of objects of the same class (MyClass). The
array should be sorted by arbitrary attributes of that class, but the
problem is that one attribute can be nil.

The code looks like this:

my_array.sort {
|x,y| x.some_attribute <=> y.some_attribute
}

Naturally, if some_attribute is nil, the comparison failed (since nil is
not comparable). I am looking for something like this:

my_array.sort {
|x,y| x.some_attribute <=> y.some_attribute unless
(x.some_attribute.nil? or y.some_attribute.nil?)
}

which I have tried without having success (Error was: comparison of
MyClass with MyClass failed).

Does anyone know how to do it? Thanks in advance!
 
F

Farrel Lifson

Hello Rubyists,

I want to sort an array of objects of the same class (MyClass). The
array should be sorted by arbitrary attributes of that class, but the
problem is that one attribute can be nil.

The code looks like this:

my_array.sort {
|x,y| x.some_attribute <=> y.some_attribute
}

Naturally, if some_attribute is nil, the comparison failed (since nil is
not comparable). I am looking for something like this:

my_array.sort {
|x,y| x.some_attribute <=> y.some_attribute unless
(x.some_attribute.nil? or y.some_attribute.nil?)
}

which I have tried without having success (Error was: comparison of
MyClass with MyClass failed).

Does anyone know how to do it? Thanks in advance!

Define <=> on your class:

class MyClass
def <=>(other)
if @some_attribute.nil?
return -1
elsif @other.some_attribute.nil?
return 1
else
@some_attribute <=> other.some_attribute
end
end
end

Farrel
 
B

Brian Candler

The code looks like this:

my_array.sort {
|x,y| x.some_attribute <=> y.some_attribute
}

Naturally, if some_attribute is nil, the comparison failed (since nil is
not comparable). I am looking for something like this:

my_array.sort {
|x,y| x.some_attribute <=> y.some_attribute unless
(x.some_attribute.nil? or y.some_attribute.nil?)
}

which I have tried without having success (Error was: comparison of
MyClass with MyClass failed).

You have to return something from the comparison. Try something like:

my_array.sort { |x,y|
x1 = x.some_attribute
y1 = y.some_attribute
if (not x1.nil?) and (not y1.nil?)
x1 <=> y1
elsif x1.nil? and y1.nil?
0
elsif x1.nil?
-1
else
1
end
}
 
P

Phrogz

Define <=> on your class:

class MyClass
def <=>(other)
if @some_attribute.nil?
return -1
elsif @other.some_attribute.nil?
return 1
else
@some_attribute <=> other.some_attribute
end
end
end

I find your style of mixing imperative and functional return
parameters odd. I would have expected either:

if foo
-1
elsif bar
1
else
x <=> y
end

OR

if foo
return -1
elsif bar
return 1
else
return x <=> y
end

but not the combination you have above. FWIW, I favor the former
style, as it's just a hair faster.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top