@@ -712,11 +712,73 @@ export class Series<T extends Scalar = Scalar> {
712712
713713 /** Return a new Series sorted by values. */
714714 sortValues ( ascending = true , naPosition : "first" | "last" = "last" ) : Series < T > {
715- const pairs = this . _values . map ( ( v , i ) => ( { v, i } ) ) ;
716- pairs . sort ( ( a , b ) => compareScalars ( a . v , b . v , ascending , naPosition ) ) ;
715+ const n = this . _values . length ;
716+ const vals = this . _values ;
717+
718+ // Pre-partition NaN/null/undefined from finite values in one pass.
719+ // This removes the NaN check from the comparator's hot path.
720+ const finBuf = new Uint32Array ( n ) ;
721+ const nanBuf = new Uint32Array ( n ) ;
722+ let finCount = 0 ;
723+ let nanCount = 0 ;
724+ for ( let i = 0 ; i < n ; i ++ ) {
725+ const v = vals [ i ] ;
726+ if ( v === null || v === undefined || ( typeof v === "number" && Number . isNaN ( v ) ) ) {
727+ nanBuf [ nanCount ++ ] = i ;
728+ } else {
729+ finBuf [ finCount ++ ] = i ;
730+ }
731+ }
732+
733+ // Sort the finite-index slice in-place using an indirect comparator.
734+ // Dispatching to one of two monomorphic comparators avoids a per-call
735+ // branch on `ascending` inside the sort's hot loop.
736+ const finSlice = finBuf . subarray ( 0 , finCount ) ;
737+ if ( ascending ) {
738+ finSlice . sort ( ( a , b ) => {
739+ const av = vals [ a ] as number | string | boolean ;
740+ const bv = vals [ b ] as number | string | boolean ;
741+ return av < bv ? - 1 : av > bv ? 1 : 0 ;
742+ } ) ;
743+ } else {
744+ finSlice . sort ( ( a , b ) => {
745+ const av = vals [ a ] as number | string | boolean ;
746+ const bv = vals [ b ] as number | string | boolean ;
747+ return av > bv ? - 1 : av < bv ? 1 : 0 ;
748+ } ) ;
749+ }
750+
751+ // Build the output permutation and gather values in a single pass.
752+ const perm = new Array < number > ( n ) ;
753+ const outData = new Array < T > ( n ) ;
754+ let pos = 0 ;
755+ if ( naPosition === "first" ) {
756+ for ( let i = 0 ; i < nanCount ; i ++ ) {
757+ const idx = nanBuf [ i ] ! ;
758+ perm [ pos ] = idx ;
759+ outData [ pos ++ ] = vals [ idx ] as T ;
760+ }
761+ for ( let i = 0 ; i < finCount ; i ++ ) {
762+ const idx = finSlice [ i ] ! ;
763+ perm [ pos ] = idx ;
764+ outData [ pos ++ ] = vals [ idx ] as T ;
765+ }
766+ } else {
767+ for ( let i = 0 ; i < finCount ; i ++ ) {
768+ const idx = finSlice [ i ] ! ;
769+ perm [ pos ] = idx ;
770+ outData [ pos ++ ] = vals [ idx ] as T ;
771+ }
772+ for ( let i = 0 ; i < nanCount ; i ++ ) {
773+ const idx = nanBuf [ i ] ! ;
774+ perm [ pos ] = idx ;
775+ outData [ pos ++ ] = vals [ idx ] as T ;
776+ }
777+ }
778+
717779 return new Series < T > ( {
718- data : pairs . map ( ( { v } ) => v ) ,
719- index : this . index . take ( pairs . map ( ( { i } ) => i ) ) ,
780+ data : outData ,
781+ index : this . index . take ( perm ) ,
720782 dtype : this . dtype ,
721783 name : this . name ,
722784 } ) ;
0 commit comments