I can sort by any one element in the array no problem using something
like :
> summary_data.sort! { |a,b| a[ 3 ] <=> b[ 3 ] }

Unfortunately, I can't do this line more than once because it blows
away any previous sorting.

I found a few pages on the internet that describe how to "sort an
array of Ruby objects by multiple class fields", however, I don't know
how to create a "class field". I'm looking at an array, not a class.

Is there a way to do this or do I need to convert my data to something
else so I can do what I need?

Advertisements

On Sun, Aug 30, 2009 at 1:30 AM, Paul <> wrote:
> Hi there, I have an array of arrays that I want to sort by multiple
> elements.
>
> Sample data in the array looks like: [ [ id, date, num, name ], [ id,
> date, num, name ], ... ]
>
> I need to sort by : (1) name, (2) date, and (3) id.
>
> I can sort by any one element in the array no problem using something
> like :
> > summary_data.sort! { |a,b| a[ 3 ] <=> b[ 3 ] }
>
> Unfortunately, I can't do this line more than once because it blows
> away any previous sorting.
>
> I found a few pages on the internet that describe how to "sort an
> array of Ruby objects by multiple class fields", however, I don't know
> how to create a "class field". I'm looking at an array, not a class.
>
> Is there a way to do this or do I need to convert my data to something
> else so I can do what I need?
>
> Please help.
>
> Thanks.
>
>

Advertisements

On Sunday 30 August 2009, Paul wrote:
> |Hi there, I have an array of arrays that I want to sort by multiple
> |elements.
> |
> |Sample data in the array looks like: [ [ id, date, num, name ], [ id,
> |date, num, name ], ... ]
> |
> |I need to sort by : (1) name, (2) date, and (3) id.
> |
> |I can sort by any one element in the array no problem using something
> |
> |like :
> |> summary_data.sort! { |a,b| a[ 3 ] <=> b[ 3 ] }
> |
> |Unfortunately, I can't do this line more than once because it blows
> |away any previous sorting.
> |
> |I found a few pages on the internet that describe how to "sort an
> |array of Ruby objects by multiple class fields", however, I don't know
> |how to create a "class field". I'm looking at an array, not a class.
> |
> |Is there a way to do this or do I need to convert my data to something
> |else so I can do what I need?
> |
> |Please help.
> |
> |Thanks.
> |

This should do what you want. It first compares the names then, if they're
equal, it compares the dates. If the dates are also equal it compares the ids.

Thanks for the great feedback! I couldn't find good documentation for
the 'sort_by' and the examples provided didn't work with my data. It
did nothing actually, which surprised me. Josh and Stefano's approach
worked well enough.

This gives me a 98% solution, which is good enough. There's a small
catch or trick with my data that I can't work around.

Details on the last 2%:

Unfortunately, the data in the array is not a fixed size. Sometimes
it is [ id, date, num, name ] and sometimes it may be [ id, date, num,
name1, name2 ] (or more names.. I don't know how many since the data
collection script figures it out as it goes along).

When I do the sort by 'name' (e.g. first = a_name <=> b_name ), all
the records with _additional_ names appears at the bottom of the list
as if it were a different name (unexpected).

On Mon, 31 Aug 2009, Paul Carvalho wrote:
> Thanks for the great feedback! I couldn't find good documentation for
> the 'sort_by' and the examples provided didn't work with my data. It
> did nothing actually, which surprised me. Josh and Stefano's approach
> worked well enough.

I think I know what the happened now. When I tried the other
approach, I sorted in place using: data.sort!

But when I used "data.sort_by" it might have sorted it but didn't save
it back to the same array.

"sort_by!" doesn't exist so I'll need to save the sorted data to
another variable.

The sort with the sample data above works as I expect, but for some
reason it still isn't sorting correctly with my real data. I'll keep
looking at it. I must be missing something else, although I can't
think what it might be right now.

On Sun, Aug 30, 2009 at 12:25 PM, Paul Carvalho <>wrote:
> On Aug 30, 11:41 am, "David A. Black" wrote:
> >
> > The nothing result seems strange. This:
> [snip]
> >
> > data = [ [2, "2009-08-21", 2, "alfie"],
> > [6, "2009-08-23", 3, "alfie"],
> > [1, "2009-08-21", 4, "barny"],
> > [3, "2009-08-23", 3, "alfie"],
> > [5, "2009-08-24", 1, "cliff"],
> > [4, "2009-08-21", 1, "cliff", "bob"] ]
> >
> > data.sort_by {|id, date, num, name| [name, date, id] }
> >
> > gives me:
> >
> > [2, "2009-08-21", 2, "alfie"]
> > [3, "2009-08-23", 3, "alfie"]
> > [6, "2009-08-23", 3, "alfie"]
> > [1, "2009-08-21", 4, "barny"]
> > [4, "2009-08-21", 1, "cliff", "bob"]
> > [5, "2009-08-24", 1, "cliff"]
> >
>
> Thanks David,
>
> I think I know what the happened now. When I tried the other
> approach, I sorted in place using: data.sort!
>
> But when I used "data.sort_by" it might have sorted it but didn't save
> it back to the same array.
>
> "sort_by!" doesn't exist so I'll need to save the sorted data to
> another variable.
>
> The sort with the sample data above works as I expect, but for some
> reason it still isn't sorting correctly with my real data. I'll keep
> looking at it. I must be missing something else, although I can't
> think what it might be right now.
>
> Sort the data, write it out to file. Should be straightforward.
>
> Cheers.
>
>
I got the same answer as David, those last two should compare "cliff" to
"cliff" and decide they are the same, then move to dates, which are all the
same except the last character. So the one with the date ending in "21"
should be before the one with the date ending in "24". This is what I saw
when I tried it.

Here are some other things you can do, to help you deal with the sometimes
last name issue.

If you are using names, you can use a variable that takes an arbitrary
number of arguments. Something like *name, then if there is one name there,
or two, they both get put into name, which is an array. So if the name was
"barney" then the variable would look like ["barney"] and if there was also
a last name that was "rubble" then the variable would look like
["barney","rubble"].

If you are not naming your variables, but instead using indexes, you can
access then by submitting a range, like this d[2..-1] which says to return a
new array of values starting at index 2 (I think name was index 3 with your
data), and through index -1. So it will do the same as the above.

Got it!
> The sort with the sample data above works as I expect, but for some
> reason it still isn't sorting correctly with my real data. I'll keep
> looking at it. I must be missing something else, although I can't
> think what it might be right now.
>

The 'name' element turned out to be an array. (Doh!) I flattened the
data and the sort_by now works correctly, as expected.

On Aug 30, 2009, at 2:05 PM, Paul Carvalho wrote:
> Got it!
>
>> The sort with the sample data above works as I expect, but for some
>> reason it still isn't sorting correctly with my real data. I'll keep
>> looking at it. I must be missing something else, although I can't
>> think what it might be right now.
>>
>
> The 'name' element turned out to be an array. (Doh!) I flattened the
> data and the sort_by now works correctly, as expected.
>
> Thanks again for all your help.

Be advised that sort_by sorts in ascending order by default. If you
want to sort descending, things get a little complicated. For example,
you can sort descending by putting a minus (-) sign in front of your
Fixnum variables. That works nicely. Now try to do that with a string.
Kaboom.

Try this code to make it (mostly) work.

# Added to the String class so in Enumerable#sort_by we can
# use the #- to reverse the sort order from ascending to descending
# Not perfect; see ruby-talk ML 20090320 where it shows that it doesn't
# always work correctly
class String
def -@
self.gsub(/./) {|s| (255 - s[0]).chr }
end
end

Share This Page

Welcome to The Coding Forums!

Welcome to the Coding Forums, the place to chat about anything related to programming and coding languages.

Please join our friendly community by clicking the button below - it only takes a few seconds and is totally free. You'll be able to ask questions about coding or chat with the community and help others.
Sign up now!