@@ -660,35 +660,77 @@ namespace ts {
660660 }
661661
662662 /**
663- * Creates a new array with duplicate entries removed.
663+ * Deduplicates an array that has already been sorted.
664+ */
665+ export function deduplicateSorted < T > ( array : SortedReadonlyArray < T > , comparer : EqualityComparer < T > | Comparer < T > ) {
666+ if ( ! array ) return undefined ;
667+ if ( array . length === 0 ) return [ ] ;
668+
669+ let last = array [ 0 ] ;
670+ const deduplicated : T [ ] = [ last ] ;
671+ for ( let i = 1 ; i < array . length ; i ++ ) {
672+ switch ( comparer ( last , array [ i ] ) ) {
673+ // equality comparison
674+ case true :
675+
676+ // relational comparison
677+ case Comparison . LessThan :
678+ case Comparison . EqualTo :
679+ continue ;
680+ }
681+
682+ deduplicated . push ( last = array [ i ] ) ;
683+ }
684+
685+ return deduplicated ;
686+ }
687+
688+ /**
689+ * Deduplicates an unsorted array.
664690 * @param equalityComparer An optional `EqualityComparer` used to determine if two values are duplicates.
665691 * @param comparer An optional `Comparer` used to sort entries before comparison. If supplied,
666692 * results are returned in the original order found in `array`.
667693 */
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 ) ;
694+ export function deduplicate < T > ( array : ReadonlyArray < T > , equalityComparer : EqualityComparer < T > , comparer ?: Comparer < T > ) : T [ ] {
695+ return ! array ? undefined :
696+ array . length === 0 ? [ ] :
697+ array . length === 1 ? array . slice ( ) :
698+ comparer ? deduplicateRelational ( array , equalityComparer , comparer ) :
699+ deduplicateEquality ( array , equalityComparer ) ;
672700 }
673701
674- function deduplicateWorker < T > ( array : ReadonlyArray < T > , equalityComparer : EqualityComparer < T > = equateValues , comparer : Comparer < T > ) {
702+ function deduplicateRelational < T > ( array : ReadonlyArray < T > , equalityComparer : EqualityComparer < T > , comparer : Comparer < T > ) {
675703 // Perform a stable sort of the array. This ensures the first entry in a list of
676704 // duplicates remains the first entry in the result.
677705 const indices = sequence ( 0 , array . length ) ;
678706 stableSortIndices ( array , indices , comparer ) ;
679707
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;
685- }
708+ let last = array [ indices [ 0 ] ] ;
709+ const deduplicated : number [ ] = [ indices [ 0 ] ] ;
710+ for ( let i = 1 ; i < indices . length ; i ++ ) {
711+ const index = indices [ i ] ;
712+ const item = array [ index ] ;
713+ if ( ! equalityComparer ( last , item ) ) {
714+ deduplicated . push ( index ) ;
715+ last = item ;
686716 }
687- deduplicated . push ( sourceIndex ) ;
688717 }
689718
690- // return deduplicated items in original order
691- return deduplicated . sort ( ) . map ( i => array [ i ] ) ;
719+ // restore original order
720+ deduplicated . sort ( ) ;
721+ return deduplicated . map ( i => array [ i ] ) ;
722+ }
723+
724+ function deduplicateEquality < T > ( array : ReadonlyArray < T > , equalityComparer : EqualityComparer < T > ) {
725+ const result : T [ ] = [ ] ;
726+ for ( const item of array ) {
727+ pushIfUnique ( result , item , equalityComparer ) ;
728+ }
729+ return result ;
730+ }
731+
732+ export function sortAndDeduplicate < T > ( array : ReadonlyArray < T > , comparer : Comparer < T > , equalityComparer ?: EqualityComparer < T > ) {
733+ return deduplicateSorted ( sort ( array , comparer ) , equalityComparer || comparer ) ;
692734 }
693735
694736 export function arrayIsEqualTo < T > ( array1 : ReadonlyArray < T > , array2 : ReadonlyArray < T > , equalityComparer : ( a : T , b : T ) => boolean = equateValues ) : boolean {
@@ -827,15 +869,6 @@ namespace ts {
827869 return to ;
828870 }
829871
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-
839872 /**
840873 * @return Whether the value was added.
841874 */
@@ -878,13 +911,20 @@ namespace ts {
878911 indices . sort ( ( x , y ) => comparer ( array [ x ] , array [ y ] ) || compareValues ( x , y ) ) ;
879912 }
880913
914+ /**
915+ * Returns a new sorted array.
916+ */
917+ export function sort < T > ( array : ReadonlyArray < T > , comparer : Comparer < T > ) {
918+ return array . slice ( ) . sort ( comparer ) as ReadonlyArray < T > as SortedReadonlyArray < T > ;
919+ }
920+
881921 /**
882922 * Stable sort of an array. Elements equal to each other maintain their relative position in the array.
883923 */
884924 export function stableSort < T > ( array : ReadonlyArray < T > , comparer : Comparer < T > ) {
885925 const indices = sequence ( 0 , array . length ) ;
886926 stableSortIndices ( array , indices , comparer ) ;
887- return indices . map ( i => array [ i ] ) ;
927+ return indices . map ( i => array [ i ] ) as ReadonlyArray < T > as SortedReadonlyArray < T > ;
888928 }
889929
890930 export function rangeEquals < T > ( array1 : ReadonlyArray < T > , array2 : ReadonlyArray < T > , pos : number , end : number ) {
@@ -969,25 +1009,22 @@ namespace ts {
9691009 * @param array A sorted array whose first element must be no larger than number
9701010 * @param number The value to be searched for in the array.
9711011 */
972- export function binarySearch < T > ( array : ReadonlyArray < T > , value : T , comparer ?: Comparer < T > , offset ?: number ) : number {
1012+ export function binarySearch < T , U > ( array : ReadonlyArray < T > , value : T , keySelector : Selector < T , U > , keyComparer : Comparer < U > , offset ?: number ) : number {
9731013 if ( ! array || array . length === 0 ) {
9741014 return - 1 ;
9751015 }
9761016
9771017 let low = offset || 0 ;
9781018 let high = array . length - 1 ;
979- comparer = comparer !== undefined
980- ? comparer
981- : ( v1 , v2 ) => ( v1 < v2 ? - 1 : ( v1 > v2 ? 1 : 0 ) ) ;
982-
1019+ const key = keySelector ( value ) ;
9831020 while ( low <= high ) {
9841021 const middle = low + ( ( high - low ) >> 1 ) ;
985- const midValue = array [ middle ] ;
1022+ const midKey = keySelector ( array [ middle ] ) ;
9861023
987- if ( comparer ( midValue , value ) === 0 ) {
1024+ if ( keyComparer ( midKey , key ) === 0 ) {
9881025 return middle ;
9891026 }
990- else if ( comparer ( midValue , value ) > 0 ) {
1027+ else if ( keyComparer ( midKey , key ) > 0 ) {
9911028 high = middle - 1 ;
9921029 }
9931030 else {
@@ -2452,8 +2489,8 @@ namespace ts {
24522489 return flatten < string > ( results ) ;
24532490
24542491 function visitDirectory ( path : string , absolutePath : string , depth : number | undefined ) {
2455- let { files , directories } = getFileSystemEntries ( path ) ;
2456- files = files . slice ( ) . sort ( comparer ) ;
2492+ const entries = getFileSystemEntries ( path ) ;
2493+ const files = sort ( entries . files , comparer ) ;
24572494
24582495 for ( const current of files ) {
24592496 const name = combinePaths ( path , current ) ;
@@ -2478,7 +2515,7 @@ namespace ts {
24782515 }
24792516 }
24802517
2481- directories = directories . slice ( ) . sort ( comparer ) ;
2518+ const directories = sort ( entries . directories , comparer ) ;
24822519 for ( const current of directories ) {
24832520 const name = combinePaths ( path , current ) ;
24842521 const absoluteName = combinePaths ( absolutePath , current ) ;
0 commit comments