Hi again,
I found an idea that looks promising to address the issues mentioned above. The main problem, as you've probably noticed, is that we cannot simply rely on (x,y) coordinates 'as they are', even after having rounded the values, to extract the implied rows and columns.
When we study the disposition below:

our eyes instantly detect that there should be 3 columns and 5 rows, but this underlying order isn't instantly reached from just sorting the set of coordinates. We need to improve the algorithm.
What I suggest is to speculate on the gaps that occur on the ordered sequence of x-coordinates and y-coordinates, respectively. To reveal these gaps, let's sort data along the x-axis first:

The figure above only shows the widths of the rectangles. Each arrow represents a rectangle, and I've ordered the items by increasing x-centers. One can estimate that the element labelled #1 belongs to a new group from this simple fact: its left coordinate (red guide) is higher than the right coordinate (blue guide) of the element #0. This observation will give us a strategy to detect column gaps.
Then, the same method is applied to detect rows. (Sorting by y-values, identifying gaps based on min-max progression.)
At the end of this process, every object has a (column, row) coordinate pair instead of sparse (x,y) values. So we can compute the final order, i.e. the weights for the comparison function.
Here is my implementation of this algorithm:
// ========================================================
// Up2Bottom and Left2Right Sorting Algorithm
// addressing (weakly) sparse rectangles
// ---
// Usage: Select the objects, then run the script
// Target: InDesign CS4/CS5/CS6/CC
// ========================================================
const CS = +CoordinateSpaces.SPREAD_COORDINATES,
AP_MIN = +AnchorPoint.TOP_LEFT_ANCHOR,
AP_CENTER = +AnchorPoint.CENTER_ANCHOR,
AP_MAX = +AnchorPoint.BOTTOM_RIGHT_ANCHOR;
var sel = app.properties.selection || null,
data = [],
r, i, j, k, t, n, w, vMax;
if( sel && 1 < (n=sel.length) )
{
// Collect coordinates and IDs
// --> {min:[xLeft, yTop], weight:[x,y], max:[xRight,yBottom], id}[]
for(i=0 ; i < n && (t=sel) ; ++i )
{
data = {
min: t.resolve(AP_MIN,CS)[0],
weight: t.resolve(AP_CENTER,CS)[0],
max: t.resolve(AP_MAX,CS)[0],
id: t.id
};
}
// Find rows and columns [i.e. y-weights and x-weights]
// ---
for( j=0 ; j < 2 ; ++j )
{
// Sort by center coordinate
// ---
data.sort(function(a,b){return a.weight - b.weight});
// min > max ==> w++
// ---
for(vMax=(t=data[0]).max, t.weight=(w=0), i=1 ; (i < n)&&(t=data) ; ++i )
{
if( t.min > vMax ){ ++w; vMax=t.max; }
t.weight = w;
}
}
// Compute final weights, clean up data, create ID-to-weight access
// ---
for( i=0 ; (i < n)&&(t=data) ; ++i )
{
w = n*t.weight[1] + t.weight[0]; // final weight (y first)
k = '_'+t.id; // ID key
(t.min.length=0)||(t.weight.length=0)||(t.max.length=0);
delete t.min; delete t.weight; delete t.max; delete t.id;
delete data;
data = w; // ID-to-weight
}
// Apply sort --> r
// ---
r = sel.sort(function(a,b){return data['_'+a.id]-data['_'+b.id];});
// Show the resulting order
// ---
for( i=0 ; i < n ; ++i )
{
app.select(r);
$.sleep(1000);
}
}
@+
Marc