1
1
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2
2
// See LICENSE in the project root for license information.
3
3
4
+ /**
5
+ * Options of {@link Sort.sortKeys}
6
+ *
7
+ * @public
8
+ */
9
+ export interface ISortKeysOptions {
10
+ /**
11
+ * Whether or not to recursively sort keys, both in objects and arrays
12
+ * @defaultValue false
13
+ */
14
+ deep ?: boolean ;
15
+ /**
16
+ * Custom compare function when sorting the keys
17
+ *
18
+ * @defaultValue Sort.compareByValue
19
+ * @param x - Key name
20
+ * @param y - Key name
21
+ * @returns -1 if `x` is smaller than `y`, 1 if `x` is greater than `y`, or 0 if the values are equal.
22
+ */
23
+ compare ?: ( x : string , y : string ) => number ;
24
+ }
25
+
26
+ interface ISortKeysContext {
27
+ cache : SortKeysCache ;
28
+ options : ISortKeysOptions ;
29
+ }
30
+
31
+ type SortKeysCache = WeakMap <
32
+ Partial < Record < string , unknown > > | unknown [ ] ,
33
+ Partial < Record < string , unknown > > | unknown [ ]
34
+ > ;
35
+
4
36
/**
5
37
* Operations for sorting collections.
6
38
*
@@ -235,6 +267,9 @@ export class Sort {
235
267
/**
236
268
* Sort the keys given in an object
237
269
*
270
+ * @param object - The object to be sorted
271
+ * @param options - The options for sort keys
272
+ *
238
273
* @example
239
274
*
240
275
* ```ts
@@ -243,80 +278,85 @@ export class Sort {
243
278
*/
244
279
public static sortKeys < T extends Partial < Record < string , unknown > > | unknown [ ] > (
245
280
object : T ,
246
- { deep , compare } : { deep ?: boolean ; compare ?: ( x : string , y : string ) => number } = {
281
+ options : ISortKeysOptions = {
247
282
deep : false ,
248
283
compare : Sort . compareByValue
249
284
}
250
285
) : T {
251
- function isPlainObject ( obj : unknown ) : obj is object {
252
- return obj !== null && typeof obj === 'object' ;
253
- }
254
286
if ( ! isPlainObject ( object ) && ! Array . isArray ( object ) ) {
255
287
throw new TypeError ( `Expected object or array` ) ;
256
288
}
289
+ const cache : SortKeysCache = new WeakMap ( ) ;
290
+ const context : ISortKeysContext = {
291
+ cache,
292
+ options
293
+ } ;
257
294
258
- const cache : WeakMap <
259
- Partial < Record < string , unknown > > | unknown [ ] ,
260
- Partial < Record < string , unknown > > | unknown [ ]
261
- > = new WeakMap ( ) ;
295
+ return Array . isArray ( object )
296
+ ? ( innerSortArray ( object , context ) as T )
297
+ : ( innerSortKeys ( object , context ) as T ) ;
298
+ }
299
+ }
300
+ function isPlainObject ( obj : unknown ) : obj is object {
301
+ return obj !== null && typeof obj === 'object' ;
302
+ }
262
303
263
- function innerSortArray ( arr : unknown [ ] ) : unknown [ ] {
264
- const resultFromCache : undefined | Partial < Record < string , unknown > > | unknown [ ] = cache . get ( arr ) ;
265
- if ( resultFromCache !== undefined ) {
266
- return resultFromCache as unknown [ ] ;
267
- }
268
- const result : unknown [ ] = [ ] ;
269
- cache . set ( arr , result ) ;
270
- if ( deep ) {
271
- result . push (
272
- ...arr . map ( ( entry ) => {
273
- if ( Array . isArray ( entry ) ) {
274
- return innerSortArray ( entry ) ;
275
- } else if ( isPlainObject ( entry ) ) {
276
- return innerSortKeys ( entry ) ;
277
- }
278
- return entry ;
279
- } )
280
- ) ;
281
- } else {
282
- result . push ( ...arr ) ;
283
- }
304
+ function innerSortArray ( arr : unknown [ ] , context : ISortKeysContext ) : unknown [ ] {
305
+ const resultFromCache : undefined | Partial < Record < string , unknown > > | unknown [ ] = context . cache . get ( arr ) ;
306
+ if ( resultFromCache !== undefined ) {
307
+ return resultFromCache as unknown [ ] ;
308
+ }
309
+ const result : unknown [ ] = [ ] ;
310
+ context . cache . set ( arr , result ) ;
311
+ if ( context . options . deep ) {
312
+ result . push (
313
+ ...arr . map ( ( entry ) => {
314
+ if ( Array . isArray ( entry ) ) {
315
+ return innerSortArray ( entry , context ) ;
316
+ } else if ( isPlainObject ( entry ) ) {
317
+ return innerSortKeys ( entry , context ) ;
318
+ }
319
+ return entry ;
320
+ } )
321
+ ) ;
322
+ } else {
323
+ result . push ( ...arr ) ;
324
+ }
284
325
285
- return result ;
286
- }
287
- function innerSortKeys ( obj : Partial < Record < string , unknown > > ) : Partial < Record < string , unknown > > {
288
- const resultFromCache : undefined | Partial < Record < string , unknown > > | unknown [ ] = cache . get ( obj ) ;
289
- if ( resultFromCache !== undefined ) {
290
- return resultFromCache as Partial < Record < string , unknown > > ;
291
- }
292
- const result : Partial < Record < string , unknown > > = { } ;
293
- const keys : string [ ] = Object . keys ( obj ) . sort ( compare ) ;
326
+ return result ;
327
+ }
328
+ function innerSortKeys (
329
+ obj : Partial < Record < string , unknown > > ,
330
+ context : ISortKeysContext
331
+ ) : Partial < Record < string , unknown > > {
332
+ const resultFromCache : undefined | Partial < Record < string , unknown > > | unknown [ ] = context . cache . get ( obj ) ;
333
+ if ( resultFromCache !== undefined ) {
334
+ return resultFromCache as Partial < Record < string , unknown > > ;
335
+ }
336
+ const result : Partial < Record < string , unknown > > = { } ;
337
+ const keys : string [ ] = Object . keys ( obj ) . sort ( context . options . compare ) ;
294
338
295
- cache . set ( obj , result ) ;
339
+ context . cache . set ( obj , result ) ;
296
340
297
- for ( const key of keys ) {
298
- const value : unknown = obj [ key ] ;
299
- let newValue : unknown ;
300
- if ( deep ) {
301
- if ( Array . isArray ( value ) ) {
302
- newValue = innerSortArray ( value ) ;
303
- } else if ( isPlainObject ( value ) ) {
304
- newValue = innerSortKeys ( value ) ;
305
- } else {
306
- newValue = value ;
307
- }
308
- } else {
309
- newValue = value ;
310
- }
311
- Object . defineProperty ( result , key , {
312
- ...Object . getOwnPropertyDescriptor ( obj , key ) ,
313
- value : newValue
314
- } ) ;
341
+ for ( const key of keys ) {
342
+ const value : unknown = obj [ key ] ;
343
+ let newValue : unknown ;
344
+ if ( context . options . deep ) {
345
+ if ( Array . isArray ( value ) ) {
346
+ newValue = innerSortArray ( value , context ) ;
347
+ } else if ( isPlainObject ( value ) ) {
348
+ newValue = innerSortKeys ( value , context ) ;
349
+ } else {
350
+ newValue = value ;
315
351
}
316
-
317
- return result ;
352
+ } else {
353
+ newValue = value ;
318
354
}
319
-
320
- return Array . isArray ( object ) ? ( innerSortArray ( object ) as T ) : ( innerSortKeys ( object ) as T ) ;
355
+ Object . defineProperty ( result , key , {
356
+ ...Object . getOwnPropertyDescriptor ( obj , key ) ,
357
+ value : newValue
358
+ } ) ;
321
359
}
360
+
361
+ return result ;
322
362
}
0 commit comments