From 56938638a6044ff4a0c8ddb224b783116ea76f9e Mon Sep 17 00:00:00 2001 From: Kemal Hadimli Date: Wed, 9 Aug 2023 17:29:42 +0100 Subject: [PATCH] feat: List scalars --- src/scalar/list.test.ts | 42 ++++++++++++++++++++ src/scalar/list.ts | 86 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 src/scalar/list.test.ts create mode 100644 src/scalar/list.ts diff --git a/src/scalar/list.test.ts b/src/scalar/list.test.ts new file mode 100644 index 0000000..214f601 --- /dev/null +++ b/src/scalar/list.test.ts @@ -0,0 +1,42 @@ +import test from 'ava'; + +import { Int64 } from './int64.js'; +import { List } from './list.js'; + +test('list', (t) => { + const l = new List(Int64); + t.deepEqual(l, new List(Int64)); + + l.value = [new Int64(17), new Int64(19), new Int64(null)]; + t.is(l.length, 3); +}); + +test('list equality', (t) => { + const l1 = new List(Int64); + l1.value = [new Int64(17), new Int64(19), new Int64(null)]; + + const l2 = new List(Int64); + l2.value = [new Int64(17), new Int64(19), new Int64(null)]; + + t.deepEqual(l1, l2); +}); + +test('list inequality', (t) => { + const l1 = new List(Int64); + l1.value = new Int64(4); + + const l2 = new List(Int64); + l2.value = new Int64(7); + + t.notDeepEqual(l1, l2); +}); + +test('list equality when invalid', (t) => { + const l1 = new List(Int64); + l1.value = new Int64(null); + + const l2 = new List(Int64); + l2.value = new Int64(null); + + t.deepEqual(l1, l2); +}); diff --git a/src/scalar/list.ts b/src/scalar/list.ts new file mode 100644 index 0000000..f1c3f9e --- /dev/null +++ b/src/scalar/list.ts @@ -0,0 +1,86 @@ +import { DataType, List as ArrowList } from '@apache-arrow/esnext-esm'; + +import { Scalar } from './scalar.js'; +import { isInvalid, NULL_VALUE } from './util.js'; + +type TVector> = T[]; + +export class List> implements Scalar> { + private _type: new (value?: unknown) => T; + private _valid = false; + private _value: TVector = []; + + constructor(scalarType: new (value?: unknown) => T, initialValue?: unknown) { + this._type = scalarType; + if (!isInvalid(initialValue)) this.value = initialValue; + } + + get dataType(): DataType { + return new ArrowList(this._type.prototype.dataType); + } + + set value(inputValue: unknown) { + if (isInvalid(inputValue)) { + this._valid = false; + this._value = []; + return; + } + + const inputArray = Array.isArray(inputValue) ? inputValue : [inputValue]; + const temporaryVector: TVector = []; + + if (inputArray.length > 0) { + const firstItemScalar = new this._type(); + firstItemScalar.value = inputArray[0]; + const firstItemType = Object.getPrototypeOf(firstItemScalar).constructor; + + for (const item of inputArray) { + const scalar = new this._type(); + scalar.value = item; + + if (Object.getPrototypeOf(scalar).constructor !== firstItemType) { + throw new Error( + `Type mismatch: All items should be of the same type as the first item. Expected type ${ + firstItemType.name + }, but got ${Object.getPrototypeOf(scalar).constructor.name}`, + ); + } + + temporaryVector.push(scalar); + } + + this._value = temporaryVector; + this._valid = true; // List becomes valid if we successfully process values + return; + } + + this._valid = true; // An empty list is valid + } + + get valid(): boolean { + return this._valid; + } + + get value(): TVector { + return this._value; + } + + toString(): string { + if (!this._valid) { + return NULL_VALUE; + } + return `[${this._value.map((v) => v.toString()).join(', ')}]`; + } + + get length(): number { + return this._value.length; + } + + // If you need an equality method, you can add an equals method similar to the Python __eq__ + equals(other: List): boolean { + if (!other) return false; + if (this.constructor !== other.constructor) return false; + if (this._valid !== other.valid) return false; + return JSON.stringify(this._value) === JSON.stringify(other.value); // crude equality check for objects, might need refinement. + } +}