196
196
*/
197
197
orderByFilter . $inject = [ '$parse' ] ;
198
198
function orderByFilter ( $parse ) {
199
- return function ( array , sortPredicate , reverseOrder ) {
199
+ return function ( array , sortPredicate , compareFn ) {
200
200
201
201
if ( array == null ) return array ;
202
202
if ( ! isArrayLike ( array ) ) {
@@ -206,11 +206,13 @@ function orderByFilter($parse) {
206
206
if ( ! isArray ( sortPredicate ) ) { sortPredicate = [ sortPredicate ] ; }
207
207
if ( sortPredicate . length === 0 ) { sortPredicate = [ '+' ] ; }
208
208
209
- var predicates = processPredicates ( sortPredicate , reverseOrder ) ;
210
- // Add a predicate at the end that evaluates to the element index. This makes the
211
- // sort stable as it works as a tie-breaker when all the input predicates cannot
212
- // distinguish between two elements.
213
- predicates . push ( { get : function ( ) { return { } ; } , descending : reverseOrder ? - 1 : 1 } ) ;
209
+ var predicates = processPredicates ( sortPredicate ) ;
210
+
211
+ // Define the `compare()` function. Use a default comparator if none is specified.
212
+ // A value of `true` means: Use the default comparator, but reverse the order.
213
+ var compare = isFunction ( compareFn ) ? compareFn :
214
+ compareFn ? reverseDefaultCompare :
215
+ defaultCompare ;
214
216
215
217
// The next three lines are a version of a Swartzian Transform idiom from Perl
216
218
// (sometimes called the Decorate-Sort-Undecorate idiom)
@@ -222,26 +224,29 @@ function orderByFilter($parse) {
222
224
return array ;
223
225
224
226
function getComparisonObject ( value , index ) {
227
+ // NOTE: We are adding an extra `tieBreaker` value based on the element's index.
228
+ // This will be used to keep the sort stable when all the input predicates cannot
229
+ // distinguish between two elements.
225
230
return {
226
231
value : value ,
232
+ tieBreaker : { value : index , type : 'number' } ,
227
233
predicateValues : predicates . map ( function ( predicate ) {
228
234
return getPredicateValue ( predicate . get ( value ) , index ) ;
229
235
} )
230
236
} ;
231
237
}
232
238
233
239
function doComparison ( v1 , v2 ) {
234
- var result = 0 ;
235
240
for ( var index = 0 , length = predicates . length ; index < length ; ++ index ) {
236
- result = compare ( v1 . predicateValues [ index ] , v2 . predicateValues [ index ] ) * predicates [ index ] . descending ;
237
- if ( result ) break ;
241
+ var result = compare ( v1 . predicateValues [ index ] , v2 . predicateValues [ index ] ) * predicates [ index ] . descending ;
242
+ if ( result ) return result ;
238
243
}
239
- return result ;
244
+
245
+ return compare ( v1 . tieBreaker , v2 . tieBreaker ) ;
240
246
}
241
247
} ;
242
248
243
- function processPredicates ( sortPredicate , reverseOrder ) {
244
- reverseOrder = reverseOrder ? - 1 : 1 ;
249
+ function processPredicates ( sortPredicate ) {
245
250
return sortPredicate . map ( function ( predicate ) {
246
251
var descending = 1 , get = identity ;
247
252
@@ -260,7 +265,7 @@ function orderByFilter($parse) {
260
265
}
261
266
}
262
267
}
263
- return { get : get , descending : descending * reverseOrder } ;
268
+ return { get : get , descending : descending } ;
264
269
} ) ;
265
270
}
266
271
@@ -277,7 +282,7 @@ function orderByFilter($parse) {
277
282
278
283
function objectValue ( value , index ) {
279
284
// If `valueOf` is a valid function use that
280
- if ( typeof value . valueOf === 'function' ) {
285
+ if ( isFunction ( value . valueOf ) ) {
281
286
value = value . valueOf ( ) ;
282
287
if ( isPrimitive ( value ) ) return value ;
283
288
}
@@ -295,23 +300,36 @@ function orderByFilter($parse) {
295
300
if ( value === null ) {
296
301
type = 'string' ;
297
302
value = 'null' ;
298
- } else if ( type === 'string' ) {
299
- value = value . toLowerCase ( ) ;
300
303
} else if ( type === 'object' ) {
301
304
value = objectValue ( value , index ) ;
302
305
}
303
306
return { value : value , type : type } ;
304
307
}
305
308
306
- function compare ( v1 , v2 ) {
309
+ function defaultCompare ( v1 , v2 ) {
307
310
var result = 0 ;
308
- if ( v1 . type === v2 . type ) {
309
- if ( v1 . value !== v2 . value ) {
310
- result = v1 . value < v2 . value ? - 1 : 1 ;
311
+
312
+ var type1 = v1 . type ;
313
+ var type2 = v2 . type ;
314
+ var value1 = v1 . value ;
315
+ var value2 = v2 . value ;
316
+
317
+ if ( type1 === type2 ) {
318
+ if ( type1 === 'string' ) {
319
+ value1 = value1 . toLowerCase ( ) ;
320
+ value2 = value2 . toLowerCase ( ) ;
321
+ }
322
+ if ( value1 !== value2 ) {
323
+ result = value1 < value2 ? - 1 : 1 ;
311
324
}
312
325
} else {
313
- result = v1 . type < v2 . type ? - 1 : 1 ;
326
+ result = type1 < type2 ? - 1 : 1 ;
314
327
}
328
+
315
329
return result ;
316
330
}
331
+
332
+ function reverseDefaultCompare ( v1 , v2 ) {
333
+ return - 1 * defaultCompare ( v1 , v2 ) ;
334
+ }
317
335
}
0 commit comments