@@ -7,14 +7,36 @@ import {timeInterval, utcInterval} from "./time.js";
7
7
export const TypedArray = Object . getPrototypeOf ( Uint8Array ) ;
8
8
const objectToString = Object . prototype . toString ;
9
9
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
+
10
30
// If a reindex is attached to the data, channel values expressed as arrays will
11
31
// be reindexed when the channels are instantiated. See exclusiveFacets.
12
32
export const reindex = Symbol ( "reindex" ) ;
13
33
14
34
export function valueof ( data , value , type ) {
15
35
const valueType = typeof value ;
16
36
return valueType === "string"
17
- ? maybeTypedMap ( data , field ( value ) , type )
37
+ ? isArrowTable ( data )
38
+ ? maybeTypedArrowify ( data . getChild ( value ) , type )
39
+ : maybeTypedMap ( data , field ( value ) , type )
18
40
: valueType === "function"
19
41
? maybeTypedMap ( data , value , type )
20
42
: valueType === "number" || value instanceof Date || valueType === "boolean"
@@ -29,21 +51,25 @@ function maybeTake(values, index) {
29
51
}
30
52
31
53
function 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
33
55
}
34
56
35
57
function maybeTypedArrayify ( data , type ) {
36
58
return type === undefined
37
59
? arrayify ( data ) // preserve undefined type
60
+ : isArrowVector ( data )
61
+ ? maybeTypedArrowify ( data , type )
38
62
: data instanceof type
39
63
? 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 ) ;
43
65
}
44
66
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 ) ;
47
73
}
48
74
49
75
export const singleton = [ null ] ; // for data-less decoration marks, e.g. frame
@@ -70,7 +96,7 @@ export function percentile(reduce) {
70
96
71
97
// If the values are specified as a typed array, no coercion is required.
72
98
export function coerceNumbers ( values ) {
73
- return values instanceof TypedArray ? values : map ( values , coerceNumber , Float64Array ) ;
99
+ return isNumberArray ( values ) ? values : map ( values , coerceNumber , Float64Array ) ;
74
100
}
75
101
76
102
// Unlike Mark’s number, here we want to convert null and undefined to NaN since
@@ -95,7 +121,7 @@ export function coerceDate(x) {
95
121
? x
96
122
: typeof x === "string"
97
123
? isoParse ( x )
98
- : x == null || isNaN ( ( x = + x ) )
124
+ : x == null || isNaN ( ( x = Number ( x ) ) ) // allow conversion from BigInt
99
125
? undefined
100
126
: new Date ( x ) ;
101
127
}
@@ -130,9 +156,15 @@ export function keyword(input, name, allowed) {
130
156
return i ;
131
157
}
132
158
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
+
133
164
// Promotes the specified data to an array as needed.
134
165
export 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 ) ;
136
168
switch ( values . type ) {
137
169
case "FeatureCollection" :
138
170
return values . features ;
@@ -233,22 +265,21 @@ export function maybeZ({z, fill, stroke} = {}) {
233
265
return z ;
234
266
}
235
267
268
+ export function lengthof ( data ) {
269
+ return isArray ( data ) ? data . length : data ?. numRows ;
270
+ }
271
+
236
272
// Returns a Uint32Array with elements [0, 1, 2, … data.length - 1].
237
273
export function range ( data ) {
238
- const n = data . length ;
274
+ const n = lengthof ( data ) ;
239
275
const r = new Uint32Array ( n ) ;
240
276
for ( let i = 0 ; i < n ; ++ i ) r [ i ] = i ;
241
277
return r ;
242
278
}
243
279
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
-
249
280
// Returns an array [values[index[0]], values[index[1]], …].
250
281
export 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 ) ) ;
252
283
}
253
284
254
285
// If f does not take exactly one argument, wraps it in a function that uses take.
@@ -575,3 +606,30 @@ export function maybeClip(clip) {
575
606
else if ( clip != null ) clip = keyword ( clip , "clip" , [ "frame" , "sphere" ] ) ;
576
607
return clip ;
577
608
}
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