@@ -159,12 +159,6 @@ namespace ts {
159159 return < Path > getCanonicalFileName ( nonCanonicalizedPath ) ;
160160 }
161161
162- export const enum Comparison {
163- LessThan = - 1 ,
164- EqualTo = 0 ,
165- GreaterThan = 1
166- }
167-
168162 export function length ( array : ReadonlyArray < any > ) {
169163 return array ? array . length : 0 ;
170164 }
@@ -301,17 +295,33 @@ namespace ts {
301295 Debug . fail ( ) ;
302296 }
303297
304- export function contains < T > ( array : ReadonlyArray < T > , value : T ) : boolean {
305- if ( array ) {
306- for ( const v of array ) {
307- if ( v === value ) {
308- return true ;
309- }
298+ function containsWithoutEqualityComparer < T > ( array : ReadonlyArray < T > , value : T ) {
299+ for ( const v of array ) {
300+ if ( v === value ) {
301+ return true ;
310302 }
311303 }
312304 return false ;
313305 }
314306
307+ function containsWithEqualityComparer < T > ( array : ReadonlyArray < T > , value : T , equalityComparer : EqualityComparer < T > ) {
308+ for ( const v of array ) {
309+ if ( equalityComparer ( v , value ) ) {
310+ return true ;
311+ }
312+ }
313+ return false ;
314+ }
315+
316+ export function contains < T > ( array : ReadonlyArray < T > , value : T , equalityComparer ?: EqualityComparer < T > ) : boolean {
317+ if ( array ) {
318+ return equalityComparer
319+ ? containsWithEqualityComparer ( array , value , equalityComparer )
320+ : containsWithoutEqualityComparer ( array , value ) ;
321+ }
322+ return false ;
323+ }
324+
315325 export function indexOf < T > ( array : ReadonlyArray < T > , value : T ) : number {
316326 if ( array ) {
317327 for ( let i = 0 ; i < array . length ; i ++ ) {
@@ -649,21 +659,36 @@ namespace ts {
649659 return [ ...array1 , ...array2 ] ;
650660 }
651661
652- // TODO: fixme (N^2) - add optional comparer so collection can be sorted before deduplication.
653- export function deduplicate < T > ( array : ReadonlyArray < T > , equalityComparer : ( a : T , b : T ) => boolean = equateValues ) : T [ ] {
654- let result : T [ ] ;
655- if ( array ) {
656- result = [ ] ;
657- loop: for ( const item of array ) {
658- for ( const res of result ) {
659- if ( equalityComparer ( res , item ) ) {
660- continue loop;
661- }
662+ /**
663+ * Creates a new array with duplicate entries removed.
664+ * @param equalityComparer An optional `EqualityComparer` used to determine if two values are duplicates.
665+ * @param comparer An optional `Comparer` used to sort entries before comparison. If supplied,
666+ * results are returned in the original order found in `array`.
667+ */
668+ export function deduplicate < T > ( array : ReadonlyArray < T > , equalityComparer ?: EqualityComparer < T > , comparer ?: Comparer < T > ) : T [ ] {
669+ if ( ! array ) return undefined ;
670+ if ( ! comparer ) return addRangeIfUnique ( [ ] , array , equalityComparer ) ;
671+ return deduplicateWorker ( array , equalityComparer , comparer ) ;
672+ }
673+
674+ function deduplicateWorker < T > ( array : ReadonlyArray < T > , equalityComparer : EqualityComparer < T > = equateValues , comparer : Comparer < T > ) {
675+ // Perform a stable sort of the array. This ensures the first entry in a list of
676+ // duplicates remains the first entry in the result.
677+ const indices = sequence ( 0 , array . length ) ;
678+ stableSortIndices ( array , indices , comparer ) ;
679+
680+ const deduplicated : number [ ] = [ ] ;
681+ loop: for ( const sourceIndex of indices ) {
682+ for ( const targetIndex of deduplicated ) {
683+ if ( equalityComparer ( array [ sourceIndex ] , array [ targetIndex ] ) ) {
684+ continue loop;
662685 }
663- result . push ( item ) ;
664686 }
687+ deduplicated . push ( sourceIndex ) ;
665688 }
666- return result ;
689+
690+ // return deduplicated items in original order
691+ return deduplicated . sort ( ) . map ( i => array [ i ] ) ;
667692 }
668693
669694 export function arrayIsEqualTo < T > ( array1 : ReadonlyArray < T > , array2 : ReadonlyArray < T > , equalityComparer : ( a : T , b : T ) => boolean = equateValues ) : boolean {
@@ -731,7 +756,7 @@ namespace ts {
731756 * are not present in `arrayA` but are present in `arrayB`. Assumes both arrays are sorted
732757 * based on the provided comparer.
733758 */
734- export function relativeComplement < T > ( arrayA : T [ ] | undefined , arrayB : T [ ] | undefined , comparer : Comparer < T > = compareValues , offsetA = 0 , offsetB = 0 ) : T [ ] | undefined {
759+ export function relativeComplement < T > ( arrayA : T [ ] | undefined , arrayB : T [ ] | undefined , comparer : Comparer < T > , offsetA = 0 , offsetB = 0 ) : T [ ] | undefined {
735760 if ( ! arrayB || ! arrayA || arrayB . length === 0 || arrayA . length === 0 ) return arrayB ;
736761 const result : T [ ] = [ ] ;
737762 outer: for ( ; offsetB < arrayB . length ; offsetB ++ ) {
@@ -795,19 +820,27 @@ namespace ts {
795820 start = start === undefined ? 0 : toOffset ( from , start ) ;
796821 end = end === undefined ? from . length : toOffset ( from , end ) ;
797822 for ( let i = start ; i < end && i < from . length ; i ++ ) {
798- const v = from [ i ] ;
799- if ( v !== undefined ) {
823+ if ( from [ i ] !== undefined ) {
800824 to . push ( from [ i ] ) ;
801825 }
802826 }
803827 return to ;
804828 }
805829
830+ function addRangeIfUnique < T > ( to : T [ ] , from : ReadonlyArray < T > , equalityComparer ?: EqualityComparer < T > ) : T [ ] | undefined {
831+ for ( let i = 0 ; i < from . length ; i ++ ) {
832+ if ( from [ i ] !== undefined ) {
833+ pushIfUnique ( to , from [ i ] , equalityComparer ) ;
834+ }
835+ }
836+ return to ;
837+ }
838+
806839 /**
807840 * @return Whether the value was added.
808841 */
809- export function pushIfUnique < T > ( array : T [ ] , toAdd : T ) : boolean {
810- if ( contains ( array , toAdd ) ) {
842+ export function pushIfUnique < T > ( array : T [ ] , toAdd : T , equalityComparer ?: EqualityComparer < T > ) : boolean {
843+ if ( contains ( array , toAdd , equalityComparer ) ) {
811844 return false ;
812845 }
813846 else {
@@ -819,24 +852,39 @@ namespace ts {
819852 /**
820853 * Unlike `pushIfUnique`, this can take `undefined` as an input, and returns a new array.
821854 */
822- export function appendIfUnique < T > ( array : T [ ] | undefined , toAdd : T ) : T [ ] {
855+ export function appendIfUnique < T > ( array : T [ ] | undefined , toAdd : T , equalityComparer ?: EqualityComparer < T > ) : T [ ] {
823856 if ( array ) {
824- pushIfUnique ( array , toAdd ) ;
857+ pushIfUnique ( array , toAdd , equalityComparer ) ;
825858 return array ;
826859 }
827860 else {
828861 return [ toAdd ] ;
829862 }
830863 }
831864
865+ /**
866+ * Creates an array of integers starting at `from` (inclusive) and ending at `to` (exclusive).
867+ */
868+ function sequence ( from : number , to : number ) {
869+ const numbers : number [ ] = [ ] ;
870+ for ( let i = from ; i < to ; i ++ ) {
871+ numbers . push ( i ) ;
872+ }
873+ return numbers ;
874+ }
875+
876+ function stableSortIndices < T > ( array : ReadonlyArray < T > , indices : number [ ] , comparer : Comparer < T > ) {
877+ // sort indices by value then position
878+ indices . sort ( ( x , y ) => comparer ( array [ x ] , array [ y ] ) || compareValues ( x , y ) ) ;
879+ }
880+
832881 /**
833882 * Stable sort of an array. Elements equal to each other maintain their relative position in the array.
834883 */
835- export function stableSort < T > ( array : ReadonlyArray < T > , comparer : Comparer < T > = compareValues ) {
836- return array
837- . map ( ( _ , i ) => i ) // create array of indices
838- . sort ( ( x , y ) => comparer ( array [ x ] , array [ y ] ) || compareValues ( x , y ) ) // sort indices by value then position
839- . map ( i => array [ i ] ) ; // get sorted array
884+ export function stableSort < T > ( array : ReadonlyArray < T > , comparer : Comparer < T > ) {
885+ const indices = sequence ( 0 , array . length ) ;
886+ stableSortIndices ( array , indices , comparer ) ;
887+ return indices . map ( i => array [ i ] ) ;
840888 }
841889
842890 export function rangeEquals < T > ( array1 : ReadonlyArray < T > , array2 : ReadonlyArray < T > , pos : number , end : number ) {
@@ -914,9 +962,6 @@ namespace ts {
914962 return result ;
915963 }
916964
917- export type Comparer < T > = ( a : T , b : T ) => Comparison ;
918- export type EqualityComparer < T > = ( a : T , b : T ) => boolean ;
919-
920965 /**
921966 * Performs a binary search, finding the index at which 'value' occurs in 'array'.
922967 * If no such index is found, returns the 2's-complement of first index at which
@@ -1483,7 +1528,7 @@ namespace ts {
14831528 return headChain ;
14841529 }
14851530
1486- function equateValues < T > ( a : T , b : T ) {
1531+ export function equateValues < T > ( a : T , b : T ) {
14871532 return a === b ;
14881533 }
14891534
@@ -1512,17 +1557,23 @@ namespace ts {
15121557 return equateValues ( a , b ) ;
15131558 }
15141559
1515- /**
1516- * Compare two values for their order relative to each other.
1517- */
1518- export function compareValues < T > ( a : T , b : T ) {
1560+ function compareComparableValues ( a : string , b : string ) : Comparison ;
1561+ function compareComparableValues ( a : number , b : number ) : Comparison ;
1562+ function compareComparableValues ( a : string | number , b : string | number ) {
15191563 return a === b ? Comparison . EqualTo :
15201564 a === undefined ? Comparison . LessThan :
15211565 b === undefined ? Comparison . GreaterThan :
15221566 a < b ? Comparison . LessThan :
15231567 Comparison . GreaterThan ;
15241568 }
15251569
1570+ /**
1571+ * Compare two values for their order relative to each other.
1572+ */
1573+ export function compareValues ( a : number , b : number ) {
1574+ return compareComparableValues ( a , b ) ;
1575+ }
1576+
15261577 /**
15271578 * Compare two strings using a case-insensitive ordinal comparison.
15281579 *
@@ -1555,7 +1606,7 @@ namespace ts {
15551606 * value of each code-point.
15561607 */
15571608 export function compareStringsCaseSensitive ( a : string , b : string ) {
1558- return compareValues ( a , b ) ;
1609+ return compareComparableValues ( a , b ) ;
15591610 }
15601611
15611612 /**
@@ -2488,7 +2539,11 @@ namespace ts {
24882539 if ( ! extraFileExtensions || extraFileExtensions . length === 0 || ! needAllExtensions ) {
24892540 return needAllExtensions ? allSupportedExtensions : supportedTypeScriptExtensions ;
24902541 }
2491- return deduplicate ( [ ...allSupportedExtensions , ...extraFileExtensions . map ( e => e . extension ) ] ) ;
2542+ return deduplicate (
2543+ [ ...allSupportedExtensions , ...extraFileExtensions . map ( e => e . extension ) ] ,
2544+ equateStringsCaseSensitive ,
2545+ compareStringsCaseSensitive
2546+ ) ;
24922547 }
24932548
24942549 export function hasJavaScriptFileExtension ( fileName : string ) {
0 commit comments