views:

55

answers:

2

My sort function is clearly not working, I am trying to sort ALL_VIEWS by @permalink.length:

ALL_VIEWS.sort_by {|view| view.permalink.length}

ALL_VIEWS[0..4].each do |view|
    puts view.inspect
    puts view.permalink.length
end

this produces:

#<View:0x1014da7b8 @permalink="xxxx">
4
#<View:0x1014da790 @permalink="yyyyy">
5
#<View:0x1014da718 @permalink="zzz">
3
#<View:0x1014da6a0 @permalink="aaaaaaa">
7
#<View:0x1014da628 @permalink="b">
1

I am expecting the following result:

#<View:0x1014da628 @permalink="b">
1
#<View:0x1014da718 @permalink="zzz">
3
#<View:0x1014da7b8 @permalink="xxxx">
4
#<View:0x1014da790 @permalink="yyyyy">
5
#<View:0x1014da6a0 @permalink="aaaaaaa">
7
+3  A: 

Use

ALL_VIEWS.replace ALL_VIEWS.sort_by {|view| view.permalink.length }

for the first line. sort_by does not sort in place - it returns the sorted array and does not modify the original one.

Adrian
Ah of course, I hadn't assigned it to anything. Thanks.
Dr. Frankenstein
Even better, use the mutating version `sort_by!`, available in 1.9.2 or by requiring `backports`
Marc-André Lafortune
@Marc: Yeah, while I was answering this question, I looked at the documentation for that method and was surprised when it wasn't there. Now I realize that I was looking at the 1.8 docs.
Adrian
+3  A: 

The Problem

sort_by does not mutate the object, it is a method of Enumerable that returns an Array whose elements are the elements yielded by your object's each method. It then sorts that array by the criteria given in your block. But notice that it does not sort your array docs for Enumerable#sort_by

You could normally do what Adrian suggested, and assign the results back to the original variable, but your original is a constant, which is going to give you issues.

The Solution(s)

So you need to either make ALL_VIEWS something other than a constant (if you are wanting to change it, then it really shouldn't be a constant anyway). Perhaps a class variable?


Or you need to have these values sorted before they are assigned to ALL_VIEWS

ALL_VIEWS = view_paths.map do |path| 
  View.new 'views/pages' , path 
end.sort_by do |view| 
  view.permalink.length
end

Or you can sort in place with something other than the sort_by method

ALL_VIEWS.sort! do |view1,view2| 
  view1.permalink.length <=> view2.permalink.length
end

This will work, but again, if you are doing stuff like this, then should ALL_VIEWS really be a constant? docs for Array#sort and Array#sort!

Joshua Cheek
Awesome. ALL_VIEWS should be a constant so it should be sorted before it is assigned - your first code snippet is just the ticket. Thanks, this is a great answer +1.
Dr. Frankenstein