Second arguments for Array.prototype.sort: map function
On Mon, Feb 20, 2012 at 11:52 AM, Xavier MONTILLET <xavierm02.net at gmail.com> wrote:
what I would like is to be able to do this:
var sortedArray = array.sort( compare, f );
I think the above is not very readable.
The following is more readable and shows the order in which the more modular parts are being applied.
var sortedArray = array.map(f).sort(compare);
Peter
I think I misunderstood your intent. Is what you want equivalent to the following?
arr.map(function(el){return {a:el,b:f(el)};}).sort(function(x, y) {return x.b > y.b;}).map(function(el){return el.a;});
Peter
Yes. I don't want the transformed array sorted, I want the original array sorted by its transofmation.
Peter got it right (except he uses > instead of a given function to compare
the two elements).
Here's and example:
var array = [ { v: 3.2 }, { v: 1.3 }, { v: 2.1 } ]; var compare = function ( a, b ) { return a - b; }; var transform = function ( x ) { return { v: x.v - Math.floor( x.v ) }; };
var sortedArray = array.sort( compare, transform );
console.log( array.map( transform ) );// [ { v: .2 }, { v: .3 }, { v: .1 } ] // you can see to sort it you need to take the last element and put it at the beginning // so you do that on array: console.log( sortedArray );// [ { v: 2.1 }, {v: 3.2 }, {v: 1.3 } ]
So you sort the transformed array and you apply the exact same modifications to the original .
Is it clearer?
Sent from my smartphone.
Xavier MONTILLET wrote:
Here's an example:
var array = [ { v: 3.2 }, { v: 1.3 }, { v: 2.1 } ]; var compare = function ( a, b ) { return a - b; }; var transform = function ( x ) { return { v: x.v - Math.floor( x.v ) }; };
var sortedArray = array.sort( compare, transform );
console.log( array.map( transform ) );// [ { v: .2 }, { v: .3 }, { v: .1 } ] // you can see to sort it you need to take the last element and put it at the beginning // so you do that on array: console.log( sortedArray );// [ { v: 2.1 }, {v: 3.2 }, {v: 1.3 } ]
So you sort the transformed array and you apply the exact same modifications to the original .
Is it clearer?
Yes, clear enough -- however adding new trailing arguments to an existing method is treacherous. It's likely calls in the wild pass a second argument by mistake. Less likely but still possible: an implementation has unwisely extended sort to have an optional second parameter already. This goes against the now-explicit NOTE in ES5:
NOTE Implementations that add additional capabilities to the set of built-in functions are encouraged to do so by adding new functions rather than adding new parameters to existing functions.
We could rule this out for the top five browser engines, but the possibility of extant code passing an extra actual parameter remains.
Better to have a new sortWithTransform method if necessary.
On Tue, Feb 28, 2012 at 3:05 AM, Brendan Eich <brendan at mozilla.org> wrote:
Yes, clear enough -- however adding new trailing arguments to an existing method is treacherous. It's likely calls in the wild pass a second argument by mistake. Less likely but still possible: an implementation has unwisely extended sort to have an optional second parameter already. This goes against the now-explicit NOTE in ES5:
NOTE Implementations that add additional capabilities to the set of built-in functions are encouraged to do so by adding new functions rather than adding new parameters to existing functions.
We could rule this out for the top five browser engines, but the possibility of extant code passing an extra actual parameter remains.
Why rule this out? IIRC, the rationale is that JS code is generally encouraged to use "feature detection" style to attempt to detect presence of new or optional functionality. The encouraged styles I've seen are
- if ('methodName' in obj)...
- if (obj.methodName)...
- if (typeof obj.methodName === 'function')...
I have not seen people "&&" these with a check of "obj.methodName.length === expectedArity". I would prefer to avoid encouraging such an additional check -- especially as function arity has historically been unreliable as an indicator of anything.
Better to have a new sortWithTransform method if necessary.
If necessary, sure. This one doesn't seem so IMO.
Mark S. Miller wrote:
We could rule this out for the top five browser engines, but the possibility of extant code passing an extra actual parameter remains.
Why rule this out?
Just as a thought-experiment, to evaluate Xavier's proposal. Not seriously!
I think it'd be nice to have a second argument for Array.prototype.sort. It'd be a function used to map the values before the comparison.
var array = [ /* w/e / ];// array containing objects var f = function ( item ) { / w/e */ };// a function that takes an item from array as argument and returns something else
Let's say you want to sort items of array by their image by f. You could do this:
var sortedArray = array.sort( function ( a, b ) { return compare( f( a ), f( b ) ); } );
But then, you would call several times f on the exact same item, so it's not good for perfs. So you would write this:
var comparisons = [ ]; array.map( f ).sort( function ( a, b ) { var comparison = compare( a, b ); comparisons.push( comparison ); return comparison; } ); var index = 0; var sortedArray = array.sort( function ( a, b ) { return comparisons[ i++ ]; } );
It would produce the same result but probably faster. And what I would like is to be able to do this:
var sortedArray = array.sort( compare, f );