Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby: Sorting a multidimensional array which may contain nil sometimes fails

I found this example in a failing test in one of my projects. Why does this work:

[[1,2,3], [2,3,4], [1,1,nil]].sort
#=> [[1, 1, nil], [1, 2, 3], [2, 3, 4]]

But this does not:

[[1,2,3], [nil,3,4], [1,1,nil]].sort
#=> ERROR: ArgumentError: comparison of Array with Array failed

Tested Ruby versions: 2.0.0, 1.9.3.

like image 487
joni Avatar asked Jan 24 '14 15:01

joni


3 Answers

It's failing because it's going over the nil. The reason the first test example doesn't fail is because the comparison of 1, 1 happens with 1, 2. It doesn't go as far as the nil to verify because it doesn't need to.

The example below fails because it must go over nil initially. Try it out in irb:

[1, nil, 2].sort
ArgumentError: comparison of Fixnum with nil failed

# in this example 1,2 comes before 2,1 so no need to continue in sort
# and nil is never reached
[ [2,1,3,5], [1,2,nil,6] ].sort
 => [[1, 2, nil, 6], [2, 1, 3, 5]]
like image 114
AdamT Avatar answered Oct 31 '22 15:10

AdamT


This is because of the comparison between the arrays in order to sort them, it's element to element:

In the first case [[1,2,3], [2,3,4], [1,1,nil]].sort, the algorithm compares:

1 (of the first array) < 2 (of the second array)
1 (of the first array) = 1 (of the third array)

Then, as there are two elements equal, we have to compare the second element:

2 (of the first array) > 1 (of the third array)

Then the comparison finishes, no need to compare the 3th element.

Now, in the second case [[1,2,3], [nil,3,4], [1,1,nil]].sort, the comparison of the first element of the arrays contains a nil, and this will raise an error.

For example, this will raise an error:

[[1,2,3], [1,nil,4], [3,1,3]].sort

This wont:

[[2,2,3], [1,nil,4], [3,1,3]].sort
like image 3
Sergio A. Avatar answered Oct 31 '22 15:10

Sergio A.


This is because the #sort method uses QSort sorting methodics, and in come cases the sort procedure reach not the last value of array. In your case [1,1,nil] it is nil. And it reach to nil in case 1,nil,4, because in last case #<=> method returns nil. To avoid the error, you need to redefine Array's #<=> method, or use complex block:

[[1,2,3], [nil,3,4], [1,1,nil]].sort do| x, y |
   x <=> y || 1
end
like image 3
Малъ Скрылевъ Avatar answered Oct 31 '22 14:10

Малъ Скрылевъ