Hi Morgan:

Thanks again.

I understand the concept now of adding a secondary sort key but I was 
unfamiliar with the .with_index enumerator method.

My app is usually sorting a small array of XML elements that will already be 
sorted but I need to know if the sequence has changed to update the file. Could 
.sort_by return a true or false indicating if the sequence was changed? 
Otherwise I need a first pass to check the sequence.

Thanks,
Bob Rice

On Jan 31, 2011, at 4:57 AM, Morgan Schweers wrote:

> Greetings,
> The long example didn't work quite right.  You can see it here: 
> https://gist.github.com/803842
> 
> --  Morgan
> 
> On Mon, Jan 31, 2011 at 1:55 AM, Morgan Schweers <cyber...@gmail.com> wrote:
> Greetings,
> Ouch.  That's...probably painfully expensive for large @children arrays.  
> Trying to understand, it looks like you're sorting on child.sequence, and 
> keeping each child with the same sequence in the same order as they are 
> initially in the @children array?
> 
> You could try something like this:
> 
> 
> 
>   def sort_children2
> 
>     @children = @children.sort_by.with_index { |child, index| 
> [child.sequence, index]}
> 
>   end
> 
> 
> 
> 
> One key to understanding this is that arrays elements ([child.sequence, 
> index] here) compare on each element when #<=> is used, so you're essentially 
> adding the elements index in the array as a secondary sort key.  The call of 
> #sort_by and then #with_index is chaining enumerators.  (Hopefully I've 
> gotten that basically right; it's not the simplest part of Ruby, but it's 
> fascinating...)  This all allows us to use an unstable (but fast!) sort, 
> while essentially adding additional sort keys that keep it stable.
> 
> A shorter version reads:
> 
>   def sort_children2
>     @children = @children.sort_by.with_index { |*args| args}
>   end
> 
> 
> Clever, perhaps, but a little obscure.  This works because |*args| stuffs all 
> the arguments into an array, which coincidentally is exactly where we want 
> them.
> 
> Here's a fun little piece of code to demonstrate what I'm doing:
> 
> # This example is flawed, but hopefully useful for demonstration purposes.
> 
> def test_stable_sorting
> 
>   ary = (1..100).to_a.shuffle + (1..100).to_a.shuffle
> 
> 
> 
>   # This associates an ordering with the randomized numbers
> 
>   idx = 0
> 
>   paired = ary.collect {|value| [value, idx += 1]}
> 
>   puts "Now the numbers are paired; the first is the random number 1-100,"
> 
>   puts "the second is its sequence within the 200 entries."
> 
>   puts paired.inspect
> 
>   puts
> 
>   puts "#sort is unstable; you'll see many entries with equal first values"
> 
>   puts "where the first of them has a higher second (sequence) number, 
> meaning"
> 
>   puts "it's out of order now."
> 
>   puts paired.sort {|x,y| x.first <=> y.first }.inspect
> 
>   puts
> 
>   puts "Now we sort exclusively on the value, while preserving ordering;"
> 
>   puts "All entries with identical first values should have second values"
> 
>   puts "that are also in numerical order."
> 
>   puts paired.sort_by.with_index {|x, i| [x.first, i]}.inspect
> 
> end
> 
> 
> --  Morgan
> 
> On Sun, Jan 30, 2011 at 5:22 PM, Robert Rice <rice.au...@pobox.com> wrote:
> Hi Morgan:
> 
> Thanks for the info although I have to admit that I don't understand how your 
> solutions work.
> 
> I also needed my sort to return a modified flag to update the file if changed 
> so I wrote my own bubble sort.
> I haven't test this yet:
> 
>       def sort_children # Don't trust Ruby sort to maintain sequence, also 
> need set_modified
>               return if @children.empty?
>               
>               arr = @children.map{ | child | [ child.sequence, child ]
>               modified = false
>               
>               while true # bubble sort
>                       change = false
>                       new_arr = []
>                       
>                       arr.each_with_index do | new_child, index |
>                               if index.zero?
>                                       prior_child = new_child
>                                       new_arr[ 0 ] = new_child
>                                       
>                                       elsif new_child.first < 
> prior_child.first # OOO
>                                       change = true
>                                       new_arr.insert( index - 1, new_child )
>                                       
>                                       else
>                                       new_arr[ index ] = new_child
>                                       prior_child = new_child
>                               end
>                       end
>                       break unless change
>                       
>                       modified = true
>                       arr = new_arr
>               end
>               return unless modified
>               
>               @children = arr.map{ | child | child.last }
>               set_modified()
>       end
> 
> Thanks,
> Bob Rice
> 
> On Jan 30, 2011, at 7:19 PM, Morgan Schweers wrote:
> 
>> Greetings,
>> Ruby's sort algorithm is quicksort, last I checked, and quicksort is not 
>> stable (which is the property you're looking for in a sort).  There are a 
>> bunch of ways around this, including writing your own, but one cute, quick, 
>> but possibly performance-impairing, approach I've seen (Matz's suggestion) 
>> is:
>>   n = 0 
>>   ary.sort_by {|x| [x, n += 1]}
>> 
>> Apparently it's also possible in 1.9.x (and thus MacRuby) to do:
>> 
>>   ary.sort_by.with_index {|x, i| [x, i]}
>> 
>> It's not much faster, though.  In the end, I'd probably suggest writing your 
>> own, if the performance of this is too poor.  (One person claimed this was 
>> on the order of 50 times slower; I haven't benchmarked it myself.)  
>> Mergesort is stable, for example.
>> 
>> This is a common problem; most systems don't need a stable sort, so they use 
>> Quicksort as a 'best general purpose' algorithm.
>> 
>> --  Morgan
>> 
>> On Sun, Jan 30, 2011 at 12:03 PM, Robert Rice <rice.au...@pobox.com> wrote:
>> Hi:
>> 
>> Does the Ruby Array sort algorithm maintain the relative position for 
>> children returning the same value for the comparison? I had an instance 
>> where two children having the compare value were interchanged.
>> 
>> Thanks,
>> Bob Rice
>> 
>> _______________________________________________
>> MacRuby-devel mailing list
>> MacRuby-devel@lists.macosforge.org
>> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>> 
>> _______________________________________________
>> MacRuby-devel mailing list
>> MacRuby-devel@lists.macosforge.org
>> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
> 
> 
> _______________________________________________
> MacRuby-devel mailing list
> MacRuby-devel@lists.macosforge.org
> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
> 
> 
> 
> _______________________________________________
> MacRuby-devel mailing list
> MacRuby-devel@lists.macosforge.org
> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel

_______________________________________________
MacRuby-devel mailing list
MacRuby-devel@lists.macosforge.org
http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel

Reply via email to