@@ -7,14 +7,36 @@ import {timeInterval, utcInterval} from "./time.js";
77export const TypedArray = Object . getPrototypeOf ( Uint8Array ) ;
88const objectToString = Object . prototype . toString ;
99
10+ export function isArray ( value ) {
11+ return value instanceof Array || value instanceof TypedArray ;
12+ }
13+
14+ function isNumberArray ( value ) {
15+ return value instanceof TypedArray && ! isBigIntArray ( value ) ;
16+ }
17+
18+ function isNumberType ( type ) {
19+ return type ?. prototype instanceof TypedArray && ! isBigIntType ( type ) ;
20+ }
21+
22+ function isBigIntArray ( value ) {
23+ return value instanceof BigInt64Array || value instanceof BigUint64Array ;
24+ }
25+
26+ function isBigIntType ( type ) {
27+ return type === BigInt64Array || type === BigUint64Array ;
28+ }
29+
1030// If a reindex is attached to the data, channel values expressed as arrays will
1131// be reindexed when the channels are instantiated. See exclusiveFacets.
1232export const reindex = Symbol ( "reindex" ) ;
1333
1434export function valueof ( data , value , type ) {
1535 const valueType = typeof value ;
1636 return valueType === "string"
17- ? maybeTypedMap ( data , field ( value ) , type )
37+ ? isArrowTable ( data )
38+ ? maybeTypedArrowify ( data . getChild ( value ) , type )
39+ : maybeTypedMap ( data , field ( value ) , type )
1840 : valueType === "function"
1941 ? maybeTypedMap ( data , value , type )
2042 : valueType === "number" || value instanceof Date || valueType === "boolean"
@@ -29,21 +51,25 @@ function maybeTake(values, index) {
2951}
3052
3153function maybeTypedMap ( data , f , type ) {
32- return map ( data , type ?. prototype instanceof TypedArray ? floater ( f ) : f , type ) ;
54+ return map ( data , isNumberType ( type ) ? ( d , i ) => coerceNumber ( f ( d , i ) ) : f , type ) ; // allow conversion from BigInt
3355}
3456
3557function maybeTypedArrayify ( data , type ) {
3658 return type === undefined
3759 ? arrayify ( data ) // preserve undefined type
60+ : isArrowVector ( data )
61+ ? maybeTypedArrowify ( data , type )
3862 : data instanceof type
3963 ? data
40- : type . prototype instanceof TypedArray && ! ( data instanceof TypedArray )
41- ? type . from ( data , coerceNumber )
42- : type . from ( data ) ;
64+ : type . from ( data , isNumberType ( type ) && ! isNumberArray ( data ) ? coerceNumber : undefined ) ;
4365}
4466
45- function floater ( f ) {
46- return ( d , i ) => coerceNumber ( f ( d , i ) ) ;
67+ function maybeTypedArrowify ( vector , type ) {
68+ return vector == null
69+ ? vector
70+ : ( type === undefined || type === Array ) && isArrowDateType ( vector . type )
71+ ? coerceDates ( vector . toArray ( ) )
72+ : maybeTypedArrayify ( vector . toArray ( ) , type ) ;
4773}
4874
4975export const singleton = [ null ] ; // for data-less decoration marks, e.g. frame
@@ -70,7 +96,7 @@ export function percentile(reduce) {
7096
7197// If the values are specified as a typed array, no coercion is required.
7298export function coerceNumbers ( values ) {
73- return values instanceof TypedArray ? values : map ( values , coerceNumber , Float64Array ) ;
99+ return isNumberArray ( values ) ? values : map ( values , coerceNumber , Float64Array ) ;
74100}
75101
76102// Unlike Mark’s number, here we want to convert null and undefined to NaN since
@@ -95,7 +121,7 @@ export function coerceDate(x) {
95121 ? x
96122 : typeof x === "string"
97123 ? isoParse ( x )
98- : x == null || isNaN ( ( x = + x ) )
124+ : x == null || isNaN ( ( x = Number ( x ) ) ) // allow conversion from BigInt
99125 ? undefined
100126 : new Date ( x ) ;
101127}
@@ -130,9 +156,15 @@ export function keyword(input, name, allowed) {
130156 return i ;
131157}
132158
159+ // Like arrayify, but also allows data to be an Apache Arrow Table.
160+ export function dataify ( data ) {
161+ return isArrowTable ( data ) ? data : arrayify ( data ) ;
162+ }
163+
133164// Promotes the specified data to an array as needed.
134165export function arrayify ( values ) {
135- if ( values == null || values instanceof Array || values instanceof TypedArray ) return values ;
166+ if ( values == null || isArray ( values ) ) return values ;
167+ if ( isArrowVector ( values ) ) return maybeTypedArrowify ( values ) ;
136168 switch ( values . type ) {
137169 case "FeatureCollection" :
138170 return values . features ;
@@ -233,22 +265,21 @@ export function maybeZ({z, fill, stroke} = {}) {
233265 return z ;
234266}
235267
268+ export function lengthof ( data ) {
269+ return isArray ( data ) ? data . length : data ?. numRows ;
270+ }
271+
236272// Returns a Uint32Array with elements [0, 1, 2, … data.length - 1].
237273export function range ( data ) {
238- const n = data . length ;
274+ const n = lengthof ( data ) ;
239275 const r = new Uint32Array ( n ) ;
240276 for ( let i = 0 ; i < n ; ++ i ) r [ i ] = i ;
241277 return r ;
242278}
243279
244- // Returns a filtered range of data given the test function.
245- export function where ( data , test ) {
246- return range ( data ) . filter ( ( i ) => test ( data [ i ] , i , data ) ) ;
247- }
248-
249280// Returns an array [values[index[0]], values[index[1]], …].
250281export function take ( values , index ) {
251- return map ( index , ( i ) => values [ i ] , values . constructor ) ;
282+ return isArray ( values ) ? map ( index , ( i ) => values [ i ] , values . constructor ) : map ( index , ( i ) => values . at ( i ) ) ;
252283}
253284
254285// If f does not take exactly one argument, wraps it in a function that uses take.
@@ -575,3 +606,30 @@ export function maybeClip(clip) {
575606 else if ( clip != null ) clip = keyword ( clip , "clip" , [ "frame" , "sphere" ] ) ;
576607 return clip ;
577608}
609+
610+ // https://github.com/observablehq/stdlib/blob/746ca2e69135df6178e4f3a17244def35d8d6b20/src/arrow.js#L4C1-L17C1
611+ function isArrowTable ( value ) {
612+ return (
613+ value &&
614+ typeof value . getChild === "function" &&
615+ typeof value . toArray === "function" &&
616+ value . schema &&
617+ Array . isArray ( value . schema . fields )
618+ ) ;
619+ }
620+
621+ function isArrowVector ( value ) {
622+ return value && typeof value . toArray === "function" && value . type ;
623+ }
624+
625+ // Apache Arrow now represents dates as numbers. We currently only support
626+ // implicit coercion to JavaScript Date objects when the numbers represent
627+ // milliseconds since Unix epoch.
628+ function isArrowDateType ( type ) {
629+ return (
630+ type &&
631+ ( type . typeId === 8 || // date
632+ type . typeId === 10 ) && // timestamp
633+ type . unit === 1 // millisecond
634+ ) ;
635+ }
0 commit comments