# Sort double-dimensional array by column

**URL:** <https://www.ruby-forum.com/t/sort-double-dimensional-array-by-column/117020>\
**Category:** Ruby\
**Created:** [October 12, 2007, 4:15am UTC](https://www.ruby-forum.com/t/sort-double-dimensional-array-by-column/117020 "2007-10-12T04:15:49Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![mr\_tibs](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/439d5e/32.png) [@mr\_tibs](https://www.ruby-forum.com/u/mr_tibs)\
**Post date:** [October 12, 2007, 4:15am UTC](https://www.ruby-forum.com/t/sort-double-dimensional-array-by-column/117020/1 "2007-10-12T04:15:49Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![mr\_tibs](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/439d5e/32.png) [@mr\_tibs](https://www.ruby-forum.com/u/mr_tibs)\
**Post date:** [October 12, 2007, 4:34am UTC](https://www.ruby-forum.com/t/sort-double-dimensional-array-by-column/117020/2 "2007-10-12T04:34:31Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![mr\_tibs](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/439d5e/32.png) [@mr\_tibs](https://www.ruby-forum.com/u/mr_tibs)\
**Post date:** [October 12, 2007, 5:49am UTC](https://www.ruby-forum.com/t/sort-double-dimensional-array-by-column/117020/3 "2007-10-12T05:49:18Z")

</div>

Mr\_Tibs wrote:

> ```
> 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

What about this one?

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

---

<div class="post-metadata">

**Author:** ![mr\_tibs](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/439d5e/32.png) [@mr\_tibs](https://www.ruby-forum.com/u/mr_tibs)\
**Post date:** [October 12, 2007, 11:00pm UTC](https://www.ruby-forum.com/t/sort-double-dimensional-array-by-column/117020/4 "2007-10-12T23:00:19Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![mr\_tibs](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/439d5e/32.png) [@mr\_tibs](https://www.ruby-forum.com/u/mr_tibs)\
**Post date:** [October 12, 2007, 10:55pm UTC](https://www.ruby-forum.com/t/sort-double-dimensional-array-by-column/117020/5 "2007-10-12T22:55:39Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![mr\_tibs](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/439d5e/32.png) [@mr\_tibs](https://www.ruby-forum.com/u/mr_tibs)\
**Post date:** [October 13, 2007, 12:28am UTC](https://www.ruby-forum.com/t/sort-double-dimensional-array-by-column/117020/6 "2007-10-13T00:28:37Z")

</div>

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
