# Sorting points by their x value is coming up wrong

Hi. Am I making a stupid mistake here? I believe this is a correct usage of sort_by but the result doesn’t seem to be correct.

``````a = [Geom::Point3d.new(12.333, 0.530369, 25.3869), Geom::Point3d.new(12.3615, 0.530369, 21.1437),
Geom::Point3d.new(12.3903, 0.530369, 16.8721), Geom::Point3d.new(12.3242, 0.530369, 26.7057), Geom::Point3d.new(12.3332, 0.530369, 25.3588)]

result = a.sort_by{|pt3d| pt3d.x}

=> [Point3d(12.3242, 0.530369, 26.7057), Point3d(12.3332, 0.530369, 25.3588),
Point3d(12.333, 0.530369, 25.3869), Point3d(12.3615, 0.530369, 21.1437), Point3d(12.3903, 0.530369, 16.8721)]

The third point in result should come before the second point, no?
``````

That’s quite strange. I’ve copied and pasted your code into the Ruby console and I get the correct ordering. Maybe you have some extra lines of code manipulating that list?

If I copy from my post I get the same result as in my post.
but…

pts = a.each.collect{|pt3d| pt3d.to_a}
result = pts.sort_by{|pt| pt[0]}

… does work fine.

in 2024

aka

``````result = a.sort_by(&:x)
``````

is the same as:

``````result = a.sort { |pt1,pt2| pt1.x == pt2.x }
``````

Which means that the method `#==` is called upon the `Geom::Point3d` objects.

The SketchUp API defines it’s own comparison method for `Geom::Point3d` that compares to within SketchUp’s internal tolerance which is 1/1000th of an inch. So, …

Correct, the answer is NO.

The comparison finds the two values to be identical within the tolerance, so the order is not predicable.

(1) The API doc is incorrect for the `Geom::Point3d#to_a` method. It does not return an array of `Length` it returns an array of `Float`.

(2) Again, you can use …

``````result = pts.sort_by(&:x)
``````

… because the SketchUp API adds `#x`, `#y` and `#z` methods to the `Array` class.

(3) The following is better than `a.each.collect`

``````pts = a.map(&:to_a)
``````

(4) But if you map to arrays of `Length`, you should get the same weird results (from the opening post) because `Length#==` does use SketchUp’s internal tolerance.

``````pts = a.map { |pt| pt.to_a.map(&:to_l) }
result = pts.sort_by(&:x)
``````

(A) Please get in the habit of posting code that we don’t have to scroll horizontally. Ex:

``````a = [
Geom::Point3d.new(12.333, 0.530369, 25.3869),
Geom::Point3d.new(12.3615, 0.530369, 21.1437),
Geom::Point3d.new(12.3903, 0.530369, 16.8721),
Geom::Point3d.new(12.3242, 0.530369, 26.7057),
Geom::Point3d.new(12.3332, 0.530369, 25.3588)
]
``````

(B) You can save yourself some typing …

``````a = [
[12.333, 0.530369, 25.3869],
[12.3615, 0.530369, 21.1437],
[12.3903, 0.530369, 16.8721],
[12.3242, 0.530369, 26.7057],
[12.3332, 0.530369, 25.3588]
].map( &Geom::Point3d.method(:new) )
``````
``````#=> [ Point3d(12.333, 0.530369, 25.3869), Point3d(12.3615, 0.530369, 21.1437),
Point3d(12.3903, 0.530369, 16.8721), Point3d(12.3242, 0.530369, 26.7057),
Point3d(12.3332, 0.530369, 25.3588) ]
``````
1 Like

Wow!. Best teacher ever!

1 Like

Ignoring issues with tolerances, to sort only by x, use

``````result = a.sort_by{ |pt3d| pt3d.x.to_f }
``````

Adding `.to_f` forces it to be a float, whcih isn’t affected by SketchUp’s tolerance code.

1 Like

Greg’s solution is the best which does not convert the array items to another type. They remain `Geom::Point3d` objects.