A way to force an arbitrary sort with Range.sort is to append a temporary column to the range and use it for sorting. A column can be inserted on the fly and then removed, to avoid corrupting the data placed next to the range that is being sorted.
Here is an example that sorts A2:A7 by the length of content.
function sortRangeByLength() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("A2:A7");
sheet.insertColumnAfter(range.getLastColumn());
var keys = range.getValues().map(function(row) {
return [row[0].length];
});
range.offset(0, range.getNumColumns(), range.getNumRows(), 1).setValues(keys);
var extendedRange = range.offset(0, 0, range.getHeight(), range.getWidth() + 1);
extendedRange.sort({"column": extendedRange.getWidth(), "ascending": true});
range.offset(0, 1).clear();
sheet.deleteColumn(extendedRange.getLastColumn());
}
The range extendedRange is range with additional column, used for sorting and then removed. Calling clear on the column which is to be removed may look redundant but it prevents unneeded formatting from spilling over to the column to the right (a column inserted to the right of a range inherits its formatting).
For some sorts there may be no obvious key, just a compare function. In that case, a key can be computed from the compare function as follows:
function sortRangeByLength() {
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getRange("A2:A7");
sheet.insertColumnAfter(range.getLastColumn());
// begin computing the keys
var indexed = range.getValues().map(function(row, i) {
return {"index": i, "content": row};
});
indexed.sort(function(a, b) {
return (a.content[0].length < b.content[0].length ? -1 : (a.content[0].length > b.content[0].length ? 1 : 0));
});
var keys = Array(indexed.length);
for (i in indexed) {
keys[indexed[i].index] = [i];
}
// proceed as before
range.offset(0, range.getNumColumns(), range.getNumRows(), 1).setValues(keys);
var extendedRange = range.offset(0, 0, range.getHeight(), range.getWidth() + 1);
extendedRange.sort({"column": extendedRange.getWidth(), "ascending": true});
range.offset(0, 1).clear();
sheet.deleteColumn(extendedRange.getLastColumn());
}
The approach is standard: created an "indexed" array, which contains the data and the index of each row. Sort this array according to your algorithm. The resulting permutation of indexes shows what row went where. It needs to be inverted to become the "sort by this ascending" key column. This inversion is keys[indexed[i].index] = [i];
range.sortin your question. I think your only recourse then, to sort on arbitrary fields of the data such as length is to somehow serialize your desired dimension in a manner that it can be sorted ascending or descending, as sftp has indicated. - tehhowch