Skip to content

Commit 0eb6d62

Browse files
authored
feat: List scalars (#36)
1 parent 9b41287 commit 0eb6d62

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

src/scalar/list.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import test from 'ava';
2+
3+
import { Int64 } from './int64.js';
4+
import { List } from './list.js';
5+
6+
test('list', (t) => {
7+
const l = new List(Int64);
8+
t.deepEqual(l, new List(Int64));
9+
10+
l.value = [new Int64(17), new Int64(19), new Int64(null)];
11+
t.is(l.length, 3);
12+
});
13+
14+
test('list equality', (t) => {
15+
const l1 = new List(Int64);
16+
l1.value = [new Int64(17), new Int64(19), new Int64(null)];
17+
18+
const l2 = new List(Int64);
19+
l2.value = [new Int64(17), new Int64(19), new Int64(null)];
20+
21+
t.deepEqual(l1, l2);
22+
});
23+
24+
test('list inequality', (t) => {
25+
const l1 = new List(Int64);
26+
l1.value = new Int64(4);
27+
28+
const l2 = new List(Int64);
29+
l2.value = new Int64(7);
30+
31+
t.notDeepEqual(l1, l2);
32+
});
33+
34+
test('list equality when invalid', (t) => {
35+
const l1 = new List(Int64);
36+
l1.value = new Int64(null);
37+
38+
const l2 = new List(Int64);
39+
l2.value = new Int64(null);
40+
41+
t.deepEqual(l1, l2);
42+
});

src/scalar/list.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { DataType, List as ArrowList } from '@apache-arrow/esnext-esm';
2+
3+
import { Scalar } from './scalar.js';
4+
import { isInvalid, NULL_VALUE } from './util.js';
5+
6+
type TVector<T extends Scalar<unknown>> = T[];
7+
8+
export class List<T extends Scalar<unknown>> implements Scalar<TVector<T>> {
9+
private _type: new (value?: unknown) => T;
10+
private _valid = false;
11+
private _value: TVector<T> = [];
12+
13+
constructor(scalarType: new (value?: unknown) => T, initialValue?: unknown) {
14+
this._type = scalarType;
15+
if (!isInvalid(initialValue)) this.value = initialValue;
16+
}
17+
18+
get dataType(): DataType {
19+
return new ArrowList(this._type.prototype.dataType);
20+
}
21+
22+
set value(inputValue: unknown) {
23+
if (isInvalid(inputValue)) {
24+
this._valid = false;
25+
this._value = [];
26+
return;
27+
}
28+
29+
const inputArray = Array.isArray(inputValue) ? inputValue : [inputValue];
30+
const temporaryVector: TVector<T> = [];
31+
32+
if (inputArray.length > 0) {
33+
const firstItemScalar = new this._type();
34+
firstItemScalar.value = inputArray[0];
35+
const firstItemType = Object.getPrototypeOf(firstItemScalar).constructor;
36+
37+
for (const item of inputArray) {
38+
const scalar = new this._type();
39+
scalar.value = item;
40+
41+
if (Object.getPrototypeOf(scalar).constructor !== firstItemType) {
42+
throw new Error(
43+
`Type mismatch: All items should be of the same type as the first item. Expected type ${
44+
firstItemType.name
45+
}, but got ${Object.getPrototypeOf(scalar).constructor.name}`,
46+
);
47+
}
48+
49+
temporaryVector.push(scalar);
50+
}
51+
52+
this._value = temporaryVector;
53+
this._valid = true; // List becomes valid if we successfully process values
54+
return;
55+
}
56+
57+
this._valid = true; // An empty list is valid
58+
}
59+
60+
get valid(): boolean {
61+
return this._valid;
62+
}
63+
64+
get value(): TVector<T> {
65+
return this._value;
66+
}
67+
68+
toString(): string {
69+
if (!this._valid) {
70+
return NULL_VALUE;
71+
}
72+
return `[${this._value.map((v) => v.toString()).join(', ')}]`;
73+
}
74+
75+
get length(): number {
76+
return this._value.length;
77+
}
78+
79+
// If you need an equality method, you can add an equals method similar to the Python __eq__
80+
equals(other: List<T>): boolean {
81+
if (!other) return false;
82+
if (this.constructor !== other.constructor) return false;
83+
if (this._valid !== other.valid) return false;
84+
return JSON.stringify(this._value) === JSON.stringify(other.value); // crude equality check for objects, might need refinement.
85+
}
86+
}

0 commit comments

Comments
 (0)