sort double-dimensional array by column

Discussion in 'Ruby' started by Mr_Tibs, Oct 12, 2007.

1. Mr_TibsGuest

Hi,

I have a [m X n] double-dimensional array in Ruby and I'm trying to
sort the entries (rows) by the 6-th column, which is a DateTime
object. Things get a little bit complicated, since I might have a nil
value in that column. If that happens, then I want to sort by the 7-th
column.
This is what I have so far:

cset_arr = cset_arr.sort do |x, y|
if x[5].nil? && y[5].nil?
x[6] <=> y[6]
elsif x[5].nil? && !y[5].nil?
y[5] # ???
elsif !x[5].nil? && y[5].nil?
x[5] # ???
elsif !x[5].nil? && !y[5].nil?
x[5] <=> y[5]
end
end

For the second and third if statements, I don't know how to "select"
that value. So, for example, if both 6-th columns are nil, then
compare by the 7-th column (first if). But, if the first record has a
nil 6-th column and the second record has a non-nil 6-th column, then
how do I set the second record as being the "selected" one of the two?
The current form (e.g. y[5]) does not work.

Thanks,
Tiberiu

Mr_Tibs, Oct 12, 2007

2. John WoodsGuest

If I understand what you're after, I'd say in the 2nd & 3rd conditions,
you'd want to always treat nil as being less than a non-nil value. So
always return -1 and 1 respectively. As an aside, you can get rid of the
!x.nil? checks because the previous conditions assure that state...

cset_arr = cset_arr.sort do |x, y|
if x[5].nil? && y[5].nil?
x[6] <=> y[6]
elsif x[5].nil?
-1
elsif y[5].nil?
1
else
x[5] <=> y[5]
end
end

-----Original Message-----
From: Mr_Tibs
Sent: 10/11/2007 07:15 PM
> Hi,
>
> I have a [m X n] double-dimensional array in Ruby and I'm trying to
> sort the entries (rows) by the 6-th column, which is a DateTime
> object. Things get a little bit complicated, since I might have a nil
> value in that column. If that happens, then I want to sort by the 7-th
> column.
> This is what I have so far:
>
> cset_arr = cset_arr.sort do |x, y|
> if x[5].nil? && y[5].nil?
> x[6] <=> y[6]
> elsif x[5].nil? && !y[5].nil?
> y[5] # ???
> elsif !x[5].nil? && y[5].nil?
> x[5] # ???
> elsif !x[5].nil? && !y[5].nil?
> x[5] <=> y[5]
> end
> end
>
> For the second and third if statements, I don't know how to "select"
> that value. So, for example, if both 6-th columns are nil, then
> compare by the 7-th column (first if). But, if the first record has a
> nil 6-th column and the second record has a non-nil 6-th column, then
> how do I set the second record as being the "selected" one of the two?
> The current form (e.g. y[5]) does not work.
>
> Thanks,
> Tiberiu
>
>
>
>

John Woods, Oct 12, 2007

3. morteeGuest

Mr_Tibs wrote:
> Hi,
>
> I have a [m X n] double-dimensional array in Ruby and I'm trying to
> sort the entries (rows) by the 6-th column, which is a DateTime
> object. Things get a little bit complicated, since I might have a nil
> value in that column. If that happens, then I want to sort by the 7-th
> column.
> This is what I have so far:
>
> cset_arr = cset_arr.sort do |x, y|
> if x[5].nil? && y[5].nil?
> x[6] <=> y[6]
> elsif x[5].nil? && !y[5].nil?
> y[5] # ???
> elsif !x[5].nil? && y[5].nil?
> x[5] # ???
> elsif !x[5].nil? && !y[5].nil?
> x[5] <=> y[5]
> end
> end

cset_arr.sort_by{|row| [ row[5] || DateTime.new, row[6] ] }

Comparing two arrays returns the comparison of the first differing pair
of respective elements. DateTime.new returns some value far in the past,
I guess the lowest possible value. So if both dates are nil, then the
first value in the sort key become the same (this default DateTime
value), thus the 7th column gets compared. Otherwise nil gets replaced
by a value which is most probably lower than any meaningful one.

mortee

mortee, Oct 12, 2007
4. Mr_TibsGuest

Hi James,

Thanks for the reply. It was what I needed (with a small correction:
inverse -1 and 1). I didn't read the Array.sort doc properly and
didn't know what I supposed to return.

Thanks,
Tiberiu

Mr_Tibs, Oct 12, 2007
5. Mr_TibsGuest

Thanks mortee. That sort of worked. Unfortunately, it puts those
entries that have the 6-th column = nil, at the start of the sorted
array (instead of at the end). I don't really understand the technique
used. You are sorting by row, but you are somehow defining and using
an array with 2 elements (that's what the square brackets do, right?).

Tiberiu

Mr_Tibs, Oct 12, 2007
6. morteeGuest

Mr_Tibs wrote:
> Thanks mortee. That sort of worked. Unfortunately, it puts those
> entries that have the 6-th column = nil, at the start of the sorted
> array (instead of at the end). I don't really understand the technique
> used. You are sorting by row, but you are somehow defining and using
> an array with 2 elements (that's what the square brackets do, right?).

Yes. Your code seemed like you want to fall back to the 7th column for
ordering when the 6th column is nil in both rows to compare.

If you do a sort_by, then it'll sort the items (in this case rows) based
on the value the block returns for the item passed to it. And comparing
arrays works this way: if the first elements differ, then their order
determines the final ordering; if they are the same, then the rest of
the arrays get compared, all the way through the last element.

So if you provide the mentioned two-element array as a sort key for your
rows, then the result will be almost what your original code did.
Actually, in the meantime I noticed that when the 6th column is non-nil
and equal in both rows to compare, your code returns equal ordering
independently of the 7th column's values - while my code falls back to
them in this case too.

Reagrding your problem with rows with nil in the 6th column going to the
start: naturally, sort will yield an ascending order, and the default
value substituted in my code is the lowest possible, so they'll get
sorted low (that is, to the start). You're of course free to specify any
default value you like, so that they get sorted to where you want them.

mortee

mortee, Oct 12, 2007