Why does sort method go low to high, but passing <=> make it go high to low?

So, I’m was playing around with the sort method in irb with a basic
array. I noticed the array gets sorted low to high if .sort is called.
But, if I pass a block of code and use <=>, it goes high to low insead.

Here is an example:

array=[1, 5, 15, 10]
array.sort
>>[1, 5, 10, 15]
array.sort{|x, y| y<=>x}
>>[15, 10, 5, 1]

I am really confused by this. First, I got the sort and passing block
example from ruby docs. So, I am just completely confused by what is
going on in the second thing.

I understand that sort is being passed a block of code. Beyond that I
am confused.

Can someone explain why there are x and y being passed to that block (or
why it is needed between the || symbols)? Also, what does the <=> do
that makes it do the opposite of sort?

I understand that a<=>b returns 1 if a > b, 0 if a==b, and -1 if a<b.
Other than that, I don’t know why that translates to it going highest to
lowest.

Thanks for any help.

On 10/15/2013 06:33 PM, JD JD wrote:

 >>[15, 10, 5, 1]

that makes it do the opposite of sort?

I understand that a<=>b returns 1 if a > b, 0 if a==b, and -1 if a<b.
Other than that, I don’t know why that translates to it going highest to
lowest.

Thanks for any help.

Are you sure you are keeping your x and y straight? Note in the docs it
flips them.

2.0.0p247 :001 > [3, 1, 2].sort { |x, y| x <=> y }
=> [1, 2, 3]
2.0.0p247 :002 > [3, 1, 2].sort { |x, y| y <=> x }
=> [3, 2, 1]

-Justin

Yes, I did notice that. Sorry, I edited it after I noticed that in the
code, but forgot to edit that sentence (I would edit it, but it won’t
let me do that now).

So, it still confuses me. Can you explain to me when it flips them
exactly? I really am just lost by this code. It probably is something
simple, but I’m just lost by it.

I know to write it down and it makes those results come out. But, I’m
sort of lost beyond that.

Read up on the spaceship operator and it should make sense

John

I never understood this either. I just kind of assume that when you flip
the x and y, the placeholder values never take that into account

Sent from my iPhone

JD JD wrote in post #1124635:

Can someone explain why there are x and y being passed to that block (or
why it is needed between the || symbols)?

That’s Ruby syntax. The things between the || symbols are the
parameters of the block. If you were going to use a regular function
you might write this:

def compare_values(x, y)
x <=> y
end

compare_values 1, 2

this sets x=1, y=2 inside the scope of the function

However, as we’re using an anonymous block, we write:

{|x,y| x <=> y }

Or in long-form:

do |x, y|
x <=> y
end

The invocation is similar; when you give a block to `sort’, it goes
through the array, and for each pair of entries in the array it calls
the block with the two entries as parameters, and uses the result to
determine which entry goes before the other in the sorted array.

Also, what does the <=> do that makes it do the opposite of sort?

It’s not the <=> that makes it do the opposite. Have a look at these:

def compare1(x, y)
x <=> y
end
def compare2(x, y)
y <=> x # see the difference?
end
def compare3(y, x)
x <=> y # ditto?
end

compare1(1, 2) #=> -1
compare2(1, 2) #=> 1
compare3(1, 2) #=> 1

What’s changing is the mapping from parameters-of-compareX to
parameters-of-spaceship-operator.

Here’s a simpler version:

def foo(x, y)
x < y
end
def bar(x, y)
y < x # see why this is different?
end
def baz(y, x)
x < y # ditto?
end

foo(1, 2) #=> true
bar(1, 2) #=> false
baz(1, 2) #=> false

On Wed, Oct 16, 2013 at 3:33 AM, JD JD [email protected] wrote:

>>[15, 10, 5, 1]

I am really confused by this. First, I got the sort and passing block
example from ruby docs. So, I am just completely confused by what is
going on in the second thing.

Something is wrong in what you posted. It works as expected and
intended:

irb(main):001:0> array=[1, 5, 15, 10]
=> [1, 5, 15, 10]
irb(main):002:0> array.sort
=> [1, 5, 10, 15]
irb(main):003:0> array.sort{|x, y| x<=>y}
=> [1, 5, 10, 15]

Maybe you accidentally exchanged x and y:

irb(main):008:0> array.sort{|x, y| y<=>x}
=> [15, 10, 5, 1]
irb(main):009:0> array.sort{|y, x| x<=>y}
=> [15, 10, 5, 1]

Note also that #sort! modifies in place but #sort returns a copy.

Can someone explain why there are x and y being passed to that block (or
why it is needed between the || symbols)? Also, what does the <=> do
that makes it do the opposite of sort?

It doesn’t, see above. This only happens if you exchange x and y (see
Matthew’s explanation).

I understand that a<=>b returns 1 if a > b, 0 if a==b, and -1 if a<b.
Other than that, I don’t know why that translates to it going highest to
lowest.

Kind regards

robert

On 10/16/2013 06:02 PM, JD JD wrote:

So, x<=>y returns -1, is sent to sort keeping them in place. Then what
happens next? Does sort compare position 1 and 2 (numbers 15 and 10)?
Or move on completely and compare position 2 and 3 at this point
(numbers 10 and 12)?

Also, what happens in sort when it gets to the end of the list? It may
not be 100% sorted just by going down the list once, no?

These are good questions. Ruby uses quicksort:

The block you provide is the comparison function.

-Justin

I think I get it semi. So, is it just the way sort works? If 1 is
returned, x and y are swapped one way in sort, if -1 they are left
alone, and 0 nothing happens?

One of question. What happens if you have these elements in the array:

[1, 15, 10, 12]

lets call position 0 x and y position 1.

So, x<=>y returns -1, is sent to sort keeping them in place. Then what
happens next? Does sort compare position 1 and 2 (numbers 15 and 10)?
Or move on completely and compare position 2 and 3 at this point
(numbers 10 and 12)?

Also, what happens in sort when it gets to the end of the list? It may
not be 100% sorted just by going down the list once, no?

On Thu, Oct 17, 2013 at 10:02 AM, JD JD [email protected] wrote:

So, x<=>y returns -1, is sent to sort keeping them in place. Then what
happens next? Does sort compare position 1 and 2 (numbers 15 and 10)?
Or move on completely and compare position 2 and 3 at this point
(numbers 10 and 12)?

Also, what happens in sort when it gets to the end of the list? It may
not be 100% sorted just by going down the list once, no?


Posted via http://www.ruby-forum.com/.

Just a thought.

arr = [1,2,3,4,5,10,9,8,7,6,11]
p arr
puts

arr.sort! do |x,y|
p [x,y,x <=> y]
x <=> y
end

p arr

############ output ############

[1, 2, 3, 4, 5, 10, 9, 8, 7, 6, 11]

[1, 10, -1]
[10, 11, -1]
[2, 10, -1]
[3, 10, -1]
[4, 10, -1]
[5, 10, -1]
[9, 10, -1]
[8, 10, -1]
[7, 10, -1]
[6, 10, -1]
[1, 5, -1]
[5, 7, -1]
[2, 5, -1]
[3, 5, -1]
[4, 5, -1]
[6, 5, 1]
[8, 5, 1]
[9, 5, 1]
[6, 8, -1]
[8, 7, 1]
[6, 7, -1]
[9, 7, 1]
[9, 8, 1]
[1, 3, -1]
[3, 4, -1]
[2, 3, -1]
[1, 2, -1]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

Harry

On Thu, Oct 17, 2013 at 8:57 AM, Hans M. [email protected]
wrote:

############ output ############
7
6
11
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

in this one, the compare func is only called once per object

No, that’s wrong. The block you pass here is a replacement function.
This is invoked once per element and the sort will be done on the
results. Typical use case is this:

irb(main):001:0> D = Struct.new :a, :b
=> D
irb(main):002:0> data = Array.new(10) {|i| D.new i, rand(1000)}
=> [#, #, #, #, #, #, #, #, #, #]
irb(main):003:0> puts data.sort_by! {|x| x.a}
#
#
#
#
#
#
#
#
#
#
=> nil
irb(main):004:0> puts data.sort_by! {|x| x.b}
#
#
#
#
#
#
#
#
#
#
=> nil

You can imagine #sort_by to work like this:

  1. associate keys retrieved via the replacement function with original
    values:
    irb(main):007:0> h = data.each_with_object({}) {|x, ha| ha[x.b] = x}
    => {34=>#, 48=>#, 89=>#, 101=>#, 497=>#, 629=>#, 752=>#,
    806=>#, 831=>#,
    997=>#}

  2. sort according to replacement values and map back to original values
    irb(main):008:0> puts h.keys.sort.map {|k| h[k]}
    #
    #
    #
    #
    #
    #
    #
    #
    #
    #
    => nil

Kind regards

robert

other interesting point is:

arr = [1,2,3,4,5,10,9,8,7,6,11]
p arr
puts

arr.sort_by! {|x| p x}

p arr

############ output ############
[1, 2, 3, 4, 5, 10, 9, 8, 7, 6, 11]

1
2
3
4
5
10
9
8
7
6
11
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

in this one, the compare func is only called once per object