@@ -19,6 +19,36 @@ function defaultIndex(n: number): Index<Label> {
1919 return new RangeIndex ( n ) as unknown as Index < Label > ;
2020}
2121
22+ /** True when the value should be treated as missing (null, undefined, or NaN). */
23+ function isMissing ( v : Scalar ) : boolean {
24+ return v === null || v === undefined || ( typeof v === "number" && Number . isNaN ( v ) ) ;
25+ }
26+
27+ /** Compare two scalar values with null/NaN handling for sorting. */
28+ function compareScalars (
29+ a : Scalar ,
30+ b : Scalar ,
31+ ascending : boolean ,
32+ naPosition : "first" | "last" ,
33+ ) : number {
34+ const aNull = isMissing ( a ) ;
35+ const bNull = isMissing ( b ) ;
36+ if ( aNull && bNull ) {
37+ return 0 ;
38+ }
39+ if ( aNull ) {
40+ return naPosition === "first" ? - 1 : 1 ;
41+ }
42+ if ( bNull ) {
43+ return naPosition === "first" ? 1 : - 1 ;
44+ }
45+ if ( a === b ) {
46+ return 0 ;
47+ }
48+ const cmp = ( a as number | string | boolean ) < ( b as number | string | boolean ) ? - 1 : 1 ;
49+ return ascending ? cmp : - cmp ;
50+ }
51+
2252// ─── SeriesOptions ────────────────────────────────────────────────────────────
2353
2454/** Constructor options accepted by `Series`. */
@@ -184,7 +214,10 @@ export class Series<T extends Scalar = Scalar> {
184214
185215 // ─── arithmetic ───────────────────────────────────────────────────────────
186216
187- private _scalarOp ( other : T | Series < T > , fn : ( a : number , b : number ) => number ) : Series < number > {
217+ private _scalarOp (
218+ other : number | Series < Scalar > ,
219+ fn : ( a : number , b : number ) => number ,
220+ ) : Series < number > {
188221 const isScalar = ! ( other instanceof Series ) ;
189222 if ( isScalar ) {
190223 const b = other as number ;
@@ -199,7 +232,7 @@ export class Series<T extends Scalar = Scalar> {
199232 name : this . name ,
200233 } ) ;
201234 }
202- const o = other as Series < T > ;
235+ const o = other as Series < Scalar > ;
203236 if ( o . size !== this . size ) {
204237 throw new RangeError (
205238 `Cannot operate on Series of different sizes: ${ this . size } vs ${ o . size } ` ,
@@ -219,46 +252,49 @@ export class Series<T extends Scalar = Scalar> {
219252 }
220253
221254 /** Element-wise addition. */
222- add ( other : T | Series < T > ) : Series < number > {
255+ add ( other : number | Series < Scalar > ) : Series < number > {
223256 return this . _scalarOp ( other , ( a , b ) => a + b ) ;
224257 }
225258
226259 /** Element-wise subtraction. */
227- sub ( other : T | Series < T > ) : Series < number > {
260+ sub ( other : number | Series < Scalar > ) : Series < number > {
228261 return this . _scalarOp ( other , ( a , b ) => a - b ) ;
229262 }
230263
231264 /** Element-wise multiplication. */
232- mul ( other : T | Series < T > ) : Series < number > {
265+ mul ( other : number | Series < Scalar > ) : Series < number > {
233266 return this . _scalarOp ( other , ( a , b ) => a * b ) ;
234267 }
235268
236269 /** Element-wise division (true division, returns float). */
237- div ( other : T | Series < T > ) : Series < number > {
270+ div ( other : number | Series < Scalar > ) : Series < number > {
238271 return this . _scalarOp ( other , ( a , b ) => a / b ) ;
239272 }
240273
241274 /** Element-wise floor division. */
242- floordiv ( other : T | Series < T > ) : Series < number > {
275+ floordiv ( other : number | Series < Scalar > ) : Series < number > {
243276 return this . _scalarOp ( other , ( a , b ) => Math . floor ( a / b ) ) ;
244277 }
245278
246279 /** Element-wise modulo. */
247- mod ( other : T | Series < T > ) : Series < number > {
280+ mod ( other : number | Series < Scalar > ) : Series < number > {
248281 return this . _scalarOp ( other , ( a , b ) => a % b ) ;
249282 }
250283
251284 /** Element-wise exponentiation. */
252- pow ( other : T | Series < T > ) : Series < number > {
285+ pow ( other : number | Series < Scalar > ) : Series < number > {
253286 return this . _scalarOp ( other , ( a , b ) => a ** b ) ;
254287 }
255288
256289 // ─── comparison ───────────────────────────────────────────────────────────
257290
258- private _cmpOp ( other : T | Series < T > , fn : ( a : T , b : T ) => boolean ) : Series < boolean > {
291+ private _cmpOp (
292+ other : Scalar | Series < Scalar > ,
293+ fn : ( a : Scalar , b : Scalar ) => boolean ,
294+ ) : Series < boolean > {
259295 const isScalar = ! ( other instanceof Series ) ;
260296 if ( isScalar ) {
261- const b = other as T ;
297+ const b = other as Scalar ;
262298 const newData = this . _values . map ( ( a ) => fn ( a , b ) ) ;
263299 return new Series < boolean > ( {
264300 data : newData ,
@@ -267,11 +303,11 @@ export class Series<T extends Scalar = Scalar> {
267303 name : this . name ,
268304 } ) ;
269305 }
270- const o = other as Series < T > ;
306+ const o = other as Series < Scalar > ;
271307 if ( o . size !== this . size ) {
272308 throw new RangeError ( `Cannot compare Series of different sizes: ${ this . size } vs ${ o . size } ` ) ;
273309 }
274- const newData = this . _values . map ( ( a , i ) => fn ( a , o . _values [ i ] as T ) ) ;
310+ const newData = this . _values . map ( ( a , i ) => fn ( a , o . _values [ i ] as Scalar ) ) ;
275311 return new Series < boolean > ( {
276312 data : newData ,
277313 index : this . index ,
@@ -280,15 +316,15 @@ export class Series<T extends Scalar = Scalar> {
280316 } ) ;
281317 }
282318
283- eq ( other : T | Series < T > ) : Series < boolean > {
319+ eq ( other : Scalar | Series < Scalar > ) : Series < boolean > {
284320 return this . _cmpOp ( other , ( a , b ) => a === b ) ;
285321 }
286322
287- ne ( other : T | Series < T > ) : Series < boolean > {
323+ ne ( other : Scalar | Series < Scalar > ) : Series < boolean > {
288324 return this . _cmpOp ( other , ( a , b ) => a !== b ) ;
289325 }
290326
291- lt ( other : T | Series < T > ) : Series < boolean > {
327+ lt ( other : Scalar | Series < Scalar > ) : Series < boolean > {
292328 return this . _cmpOp ( other , ( a , b ) => {
293329 if ( a === null || b === null ) {
294330 return false ;
@@ -297,7 +333,7 @@ export class Series<T extends Scalar = Scalar> {
297333 } ) ;
298334 }
299335
300- le ( other : T | Series < T > ) : Series < boolean > {
336+ le ( other : Scalar | Series < Scalar > ) : Series < boolean > {
301337 return this . _cmpOp ( other , ( a , b ) => {
302338 if ( a === null || b === null ) {
303339 return false ;
@@ -306,7 +342,7 @@ export class Series<T extends Scalar = Scalar> {
306342 } ) ;
307343 }
308344
309- gt ( other : T | Series < T > ) : Series < boolean > {
345+ gt ( other : Scalar | Series < Scalar > ) : Series < boolean > {
310346 return this . _cmpOp ( other , ( a , b ) => {
311347 if ( a === null || b === null ) {
312348 return false ;
@@ -315,7 +351,7 @@ export class Series<T extends Scalar = Scalar> {
315351 } ) ;
316352 }
317353
318- ge ( other : T | Series < T > ) : Series < boolean > {
354+ ge ( other : Scalar | Series < Scalar > ) : Series < boolean > {
319355 return this . _cmpOp ( other , ( a , b ) => {
320356 if ( a === null || b === null ) {
321357 return false ;
@@ -546,28 +582,7 @@ export class Series<T extends Scalar = Scalar> {
546582 /** Return a new Series sorted by values. */
547583 sortValues ( ascending = true , naPosition : "first" | "last" = "last" ) : Series < T > {
548584 const pairs = this . _values . map ( ( v , i ) => ( { v, i } ) ) ;
549- pairs . sort ( ( a , b ) => {
550- const aNull =
551- a . v === null || a . v === undefined || ( typeof a . v === "number" && Number . isNaN ( a . v ) ) ;
552- const bNull =
553- b . v === null || b . v === undefined || ( typeof b . v === "number" && Number . isNaN ( b . v ) ) ;
554- if ( aNull && bNull ) {
555- return 0 ;
556- }
557- if ( aNull ) {
558- return naPosition === "first" ? - 1 : 1 ;
559- }
560- if ( bNull ) {
561- return naPosition === "first" ? 1 : - 1 ;
562- }
563- const av = a . v as number | string | boolean ;
564- const bv = b . v as number | string | boolean ;
565- if ( av === bv ) {
566- return 0 ;
567- }
568- const cmp = av < bv ? - 1 : 1 ;
569- return ascending ? cmp : - cmp ;
570- } ) ;
585+ pairs . sort ( ( a , b ) => compareScalars ( a . v , b . v , ascending , naPosition ) ) ;
571586 return new Series < T > ( {
572587 data : pairs . map ( ( { v } ) => v ) ,
573588 index : this . index . take ( pairs . map ( ( { i } ) => i ) ) ,
@@ -639,8 +654,8 @@ export class Series<T extends Scalar = Scalar> {
639654 // ─── set operations ───────────────────────────────────────────────────────
640655
641656 /** True when `value` exists in this Series. */
642- isin ( values : readonly T [ ] ) : Series < boolean > {
643- const set = new Set < T > ( values ) ;
657+ isin ( values : readonly Scalar [ ] ) : Series < boolean > {
658+ const set = new Set < Scalar > ( values ) ;
644659 return new Series < boolean > ( {
645660 data : this . _values . map ( ( v ) => set . has ( v ) ) ,
646661 index : this . index ,
0 commit comments