FieldValues: Use plain arrays instead of Vector (part 1 of 2) (#66187)
Co-authored-by: Leon Sorokin <leeoniya@gmail.com> Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
co-authored by
Leon Sorokin
Torkel Ödegaard
parent
4f5b80095e
commit
b8188eead4
@@ -1,4 +1,4 @@
|
||||
import { Vector, QueryResultMeta } from '../types';
|
||||
import { QueryResultMeta } from '../types';
|
||||
import { Field, FieldType, DataFrame } from '../types/dataFrame';
|
||||
import { FunctionalVector } from '../vector/FunctionalVector';
|
||||
import { vectorToArray } from '../vector/vectorToArray';
|
||||
@@ -10,10 +10,12 @@ export type ValueConverter<T = any> = (val: unknown) => T;
|
||||
|
||||
const NOOP: ValueConverter = (v) => v;
|
||||
|
||||
class ArrayPropertyVector<T = any> implements Vector<T> {
|
||||
class ArrayPropertyVector<T = any> extends FunctionalVector<T> {
|
||||
converter = NOOP;
|
||||
|
||||
constructor(private source: any[], private prop: string) {}
|
||||
constructor(private source: any[], private prop: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this.source.length;
|
||||
|
||||
@@ -235,7 +235,7 @@ describe('DataFrame JSON', () => {
|
||||
},
|
||||
};
|
||||
|
||||
expect(dataFrameToJSON(inputFrame)).toStrictEqual(expectedJSON);
|
||||
expect(dataFrameToJSON(inputFrame)).toEqual(expectedJSON);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,20 +2,20 @@ import { isString } from 'lodash';
|
||||
|
||||
import { QueryResultMeta } from '../types/data';
|
||||
import { Field, DataFrame, DataFrameDTO, FieldDTO, FieldType } from '../types/dataFrame';
|
||||
import { MutableVector, Vector } from '../types/vector';
|
||||
import { Vector } from '../types/vector';
|
||||
import { makeFieldParser } from '../utils/fieldParser';
|
||||
import { ArrayVector } from '../vector/ArrayVector';
|
||||
import { FunctionalVector } from '../vector/FunctionalVector';
|
||||
|
||||
import { guessFieldTypeFromValue, guessFieldTypeForField, toDataFrameDTO } from './processDataFrame';
|
||||
|
||||
export type MutableField<T = any> = Field<T, MutableVector<T>>;
|
||||
export type MutableField<T = any> = Field<T>;
|
||||
|
||||
type MutableVectorCreator = (buffer?: any[]) => MutableVector;
|
||||
type MutableVectorCreator = (buffer?: any[]) => Vector;
|
||||
|
||||
export const MISSING_VALUE = undefined; // Treated as connected in new graph panel
|
||||
|
||||
export class MutableDataFrame<T = any> extends FunctionalVector<T> implements DataFrame, MutableVector<T> {
|
||||
export class MutableDataFrame<T = any> extends FunctionalVector<T> implements DataFrame {
|
||||
name?: string;
|
||||
refId?: string;
|
||||
meta?: QueryResultMeta;
|
||||
@@ -148,15 +148,6 @@ export class MutableDataFrame<T = any> extends FunctionalVector<T> implements Da
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse all values
|
||||
*/
|
||||
reverse() {
|
||||
for (const f of this.fields) {
|
||||
f.values.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
private parsers: Map<Field, (v: string) => any> | undefined = undefined;
|
||||
|
||||
/**
|
||||
@@ -210,10 +201,25 @@ export class MutableDataFrame<T = any> extends FunctionalVector<T> implements Da
|
||||
}
|
||||
}
|
||||
|
||||
/** support standard array push syntax */
|
||||
push(...vals: T[]): number {
|
||||
for (const v of vals) {
|
||||
this.add(v);
|
||||
}
|
||||
return this.length;
|
||||
}
|
||||
|
||||
reverse() {
|
||||
for (const field of this.fields) {
|
||||
field.values.reverse();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add values from an object to corresponding fields. Similar to appendRow but does not create new fields.
|
||||
*/
|
||||
add(value: T) {
|
||||
add(value: T): void {
|
||||
// Will add one value for every field
|
||||
const obj = value as any;
|
||||
for (const field of this.fields) {
|
||||
|
||||
@@ -332,7 +332,7 @@ describe('align frames', () => {
|
||||
const v = new ArrayVector([null, null, null, 4, null]);
|
||||
expect(isLikelyAscendingVector(v)).toBeTruthy();
|
||||
expect(isLikelyAscendingVector(new ArrayVector([4]))).toBeTruthy();
|
||||
expect(isLikelyAscendingVector(new ArrayVector([]))).toBeTruthy();
|
||||
expect(isLikelyAscendingVector(new ArrayVector([] as any[]))).toBeTruthy();
|
||||
});
|
||||
|
||||
it('middle values', () => {
|
||||
|
||||
@@ -418,7 +418,7 @@ describe('Reducer Transformer', () => {
|
||||
"CA",
|
||||
"NY",
|
||||
"CA",
|
||||
,
|
||||
undefined,
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -426,8 +426,8 @@ describe('Reducer Transformer', () => {
|
||||
"name": "country",
|
||||
"type": "string",
|
||||
"values": [
|
||||
,
|
||||
,
|
||||
undefined,
|
||||
undefined,
|
||||
"USA",
|
||||
"USA",
|
||||
],
|
||||
|
||||
@@ -136,7 +136,13 @@ export interface Field<T = any, V = Vector<T>> {
|
||||
* Meta info about how field and how to display it
|
||||
*/
|
||||
config: FieldConfig;
|
||||
values: V; // The raw field values
|
||||
|
||||
/**
|
||||
* The raw field values
|
||||
* In Grafana 10, this accepts both simple arrays and the Vector interface
|
||||
* In Grafana 11, the Vector interface will be removed
|
||||
*/
|
||||
values: V | T[];
|
||||
|
||||
/**
|
||||
* When type === FieldType.Time, this can optionally store
|
||||
|
||||
@@ -1,35 +1,99 @@
|
||||
export interface Vector<T = any> {
|
||||
declare global {
|
||||
interface Array<T> {
|
||||
/** @deprecated this only exists to help migrate Vector to Array */
|
||||
get(idx: number): T;
|
||||
/** @deprecated this only exists to help migrate Vector to Array */
|
||||
set(idx: number, value: T): void;
|
||||
/** @deprecated this only exists to help migrate Vector to Array */
|
||||
add(value: T): void;
|
||||
/** @deprecated this only exists to help migrate Vector to Array */
|
||||
toArray(): T[];
|
||||
}
|
||||
}
|
||||
|
||||
// JS original sin
|
||||
// this if condition is because Jest will re-exec this block multiple times (in a browser this only runs once)
|
||||
if (!Object.getOwnPropertyDescriptor(Array.prototype, 'toArray')) {
|
||||
Object.defineProperties(Array.prototype, {
|
||||
get: {
|
||||
value: function (idx: number): any {
|
||||
return (this as any)[idx];
|
||||
},
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
},
|
||||
set: {
|
||||
value: function (idx: number, value: any) {
|
||||
(this as any)[idx] = value;
|
||||
},
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
},
|
||||
add: {
|
||||
value: function (value: any) {
|
||||
(this as any).push(value);
|
||||
},
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
},
|
||||
toArray: {
|
||||
value: function () {
|
||||
return this;
|
||||
},
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** @deprecated use a simple Array<T> */
|
||||
export interface Vector<T = any> extends Array<T> {
|
||||
length: number;
|
||||
|
||||
/**
|
||||
* Access the value by index (Like an array)
|
||||
*
|
||||
* @deprecated use a simple Array<T>
|
||||
*/
|
||||
get(index: number): T;
|
||||
|
||||
/**
|
||||
* Set a value
|
||||
*
|
||||
* @deprecated use a simple Array<T>
|
||||
*/
|
||||
set: (index: number, value: T) => void;
|
||||
|
||||
/**
|
||||
* Adds the value to the vector
|
||||
* Same as Array.push()
|
||||
*
|
||||
* @deprecated use a simple Array<T>
|
||||
*/
|
||||
add: (value: T) => void;
|
||||
|
||||
/**
|
||||
* Get the results as an array.
|
||||
*
|
||||
* @deprecated use a simple Array<T>
|
||||
*/
|
||||
toArray(): T[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Apache arrow vectors are Read/Write
|
||||
*
|
||||
* @deprecated -- this is now part of the base Vector interface
|
||||
*/
|
||||
export interface ReadWriteVector<T = any> extends Vector<T> {
|
||||
set: (index: number, value: T) => void;
|
||||
}
|
||||
export interface ReadWriteVector<T = any> extends Vector<T> {}
|
||||
|
||||
/**
|
||||
* Vector with standard manipulation functions
|
||||
*
|
||||
* @deprecated -- this is now part of the base Vector interface
|
||||
*/
|
||||
export interface MutableVector<T = any> extends ReadWriteVector<T> {
|
||||
/**
|
||||
* Adds the value to the vector
|
||||
*/
|
||||
add: (value: T) => void;
|
||||
|
||||
/**
|
||||
* modifies the vector so it is now the opposite order
|
||||
*/
|
||||
reverse: () => void;
|
||||
}
|
||||
export interface MutableVector<T = any> extends ReadWriteVector<T> {}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Vector } from '../types/vector';
|
||||
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
import { vectorToArray } from './vectorToArray';
|
||||
|
||||
interface AppendedVectorInfo<T> {
|
||||
@@ -13,11 +14,12 @@ interface AppendedVectorInfo<T> {
|
||||
* RAM -- rather than allocate a new array the size of all previous arrays, this just
|
||||
* points the correct index to their original array values
|
||||
*/
|
||||
export class AppendedVectors<T = any> implements Vector<T> {
|
||||
export class AppendedVectors<T = any> extends FunctionalVector<T> {
|
||||
length = 0;
|
||||
source: Array<AppendedVectorInfo<T>> = [];
|
||||
|
||||
constructor(startAt = 0) {
|
||||
super();
|
||||
this.length = startAt;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { Field, FieldType } from '../types';
|
||||
|
||||
import { ArrayVector } from './ArrayVector';
|
||||
|
||||
describe('ArrayVector', () => {
|
||||
it('should init 150k with 65k Array.push() chonking', () => {
|
||||
const arr = Array.from({ length: 150e3 }, (v, i) => i);
|
||||
const av = new ArrayVector(arr);
|
||||
|
||||
expect(av.toArray()).toEqual(arr);
|
||||
});
|
||||
|
||||
it('should support add and push', () => {
|
||||
const av = new ArrayVector<number>();
|
||||
av.add(1);
|
||||
av.push(2);
|
||||
av.push(3, 4);
|
||||
|
||||
expect(av.toArray()).toEqual([1, 2, 3, 4]);
|
||||
});
|
||||
|
||||
it('typescript should not re-define the ArrayVector<T> based on input to the constructor', () => {
|
||||
const field: Field<number> = {
|
||||
name: 'test',
|
||||
config: {},
|
||||
type: FieldType.number,
|
||||
values: new ArrayVector(), // this defaults to `new ArrayVector<any>()`
|
||||
};
|
||||
expect(field).toBeDefined();
|
||||
|
||||
// Before collapsing Vector, ReadWriteVector, and MutableVector these all worked fine
|
||||
field.values = new ArrayVector();
|
||||
field.values = new ArrayVector(undefined);
|
||||
field.values = new ArrayVector([1, 2, 3]);
|
||||
field.values = new ArrayVector([]);
|
||||
field.values = new ArrayVector([1, undefined]);
|
||||
field.values = new ArrayVector([null]);
|
||||
field.values = new ArrayVector(['a', 'b', 'c']);
|
||||
expect(field.values.length).toBe(3);
|
||||
});
|
||||
});
|
||||
@@ -1,43 +1,38 @@
|
||||
import { MutableVector } from '../types/vector';
|
||||
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
* @deprecated use a simple Array<T>
|
||||
*/
|
||||
export class ArrayVector<T = any> extends FunctionalVector<T> implements MutableVector<T> {
|
||||
buffer: T[];
|
||||
export class ArrayVector<T = any> extends Array<T> {
|
||||
get buffer() {
|
||||
return this;
|
||||
}
|
||||
|
||||
constructor(buffer?: T[]) {
|
||||
set buffer(values: any[]) {
|
||||
this.length = 0;
|
||||
|
||||
const len = values?.length;
|
||||
|
||||
if (len) {
|
||||
let chonkSize = 65e3;
|
||||
let numChonks = Math.ceil(len / chonkSize);
|
||||
|
||||
for (let chonkIdx = 0; chonkIdx < numChonks; chonkIdx++) {
|
||||
this.push.apply(this, values.slice(chonkIdx * chonkSize, (chonkIdx + 1) * chonkSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This any type is here to make the change type changes in v10 non breaking for plugins.
|
||||
* Before you could technically assign field.values any typed ArrayVector no matter what the Field<T> T type was.
|
||||
*/
|
||||
constructor(buffer?: any[]) {
|
||||
super();
|
||||
this.buffer = buffer ? buffer : [];
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this.buffer.length;
|
||||
}
|
||||
|
||||
add(value: T) {
|
||||
this.buffer.push(value);
|
||||
}
|
||||
|
||||
get(index: number): T {
|
||||
return this.buffer[index];
|
||||
}
|
||||
|
||||
set(index: number, value: T) {
|
||||
this.buffer[index] = value;
|
||||
}
|
||||
|
||||
reverse() {
|
||||
this.buffer.reverse();
|
||||
}
|
||||
|
||||
toArray(): T[] {
|
||||
return this.buffer;
|
||||
this.buffer = buffer ?? [];
|
||||
}
|
||||
|
||||
toJSON(): T[] {
|
||||
return this.buffer;
|
||||
return [...this]; // copy to avoid circular reference (only for jest)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { Vector } from '../types/vector';
|
||||
import { BinaryOperation } from '../utils/binaryOperators';
|
||||
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
import { vectorToArray } from './vectorToArray';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export class BinaryOperationVector implements Vector<number> {
|
||||
constructor(private left: Vector<number>, private right: Vector<number>, private operation: BinaryOperation) {}
|
||||
export class BinaryOperationVector extends FunctionalVector<number> {
|
||||
constructor(private left: Vector<number>, private right: Vector<number>, private operation: BinaryOperation) {
|
||||
super();
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this.left.length;
|
||||
|
||||
@@ -20,6 +20,9 @@ describe('Check Circular Vector', () => {
|
||||
|
||||
v.add(8);
|
||||
expect(v.toArray()).toEqual([6, 7, 8]);
|
||||
|
||||
v.push(9, 10);
|
||||
expect(v.toArray()).toEqual([8, 9, 10]);
|
||||
});
|
||||
|
||||
it('should grow buffer until it hits capacity (append)', () => {
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { MutableVector } from '../types/vector';
|
||||
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
import { vectorToArray } from './vectorToArray';
|
||||
|
||||
interface CircularOptions<T> {
|
||||
buffer?: T[];
|
||||
@@ -18,7 +15,7 @@ interface CircularOptions<T> {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class CircularVector<T = any> extends FunctionalVector<T> implements MutableVector<T> {
|
||||
export class CircularVector<T = any> extends FunctionalVector<T> {
|
||||
private buffer: T[];
|
||||
private index: number;
|
||||
private capacity: number;
|
||||
@@ -43,7 +40,7 @@ export class CircularVector<T = any> extends FunctionalVector<T> implements Muta
|
||||
* * head vs tail
|
||||
* * growing buffer vs overwriting values
|
||||
*/
|
||||
private getAddFunction() {
|
||||
private getAddFunction(): (value: T) => void {
|
||||
// When we are not at capacity, it should actually modify the buffer
|
||||
if (this.capacity > this.buffer.length) {
|
||||
if (this.tail) {
|
||||
@@ -114,31 +111,18 @@ export class CircularVector<T = any> extends FunctionalVector<T> implements Muta
|
||||
}
|
||||
|
||||
reverse() {
|
||||
this.buffer.reverse();
|
||||
return this.buffer.reverse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the value to the buffer
|
||||
*/
|
||||
add: (value: T) => void;
|
||||
|
||||
get(index: number) {
|
||||
return this.buffer[(index + this.index) % this.buffer.length];
|
||||
}
|
||||
|
||||
set(index: number, value: T) {
|
||||
set(index: number, value: any) {
|
||||
this.buffer[(index + this.index) % this.buffer.length] = value;
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this.buffer.length;
|
||||
}
|
||||
|
||||
toArray(): T[] {
|
||||
return vectorToArray(this);
|
||||
}
|
||||
|
||||
toJSON(): T[] {
|
||||
return vectorToArray(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { Vector } from '../types/vector';
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export class ConstantVector<T = any> implements Vector<T> {
|
||||
constructor(private value: T, private len: number) {}
|
||||
export class ConstantVector<T = any> extends FunctionalVector<T> {
|
||||
constructor(private value: T, private len: number) {
|
||||
super();
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this.len;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Vector } from '../types';
|
||||
import { vectorToArray } from './vectorToArray';
|
||||
|
||||
/** @public */
|
||||
export abstract class FunctionalVector<T = any> implements Vector<T>, Iterable<T> {
|
||||
export abstract class FunctionalVector<T = any> implements Vector<T> {
|
||||
abstract get length(): number;
|
||||
|
||||
abstract get(index: number): T;
|
||||
@@ -15,32 +15,176 @@ export abstract class FunctionalVector<T = any> implements Vector<T>, Iterable<T
|
||||
}
|
||||
}
|
||||
|
||||
set(index: number, value: any): void {
|
||||
throw 'unsupported operation';
|
||||
}
|
||||
|
||||
add(value: T): void {
|
||||
throw 'unsupported operation';
|
||||
}
|
||||
|
||||
push(...vals: T[]): number {
|
||||
for (const v of vals) {
|
||||
this.add(v);
|
||||
}
|
||||
return this.length;
|
||||
}
|
||||
|
||||
// Implement "iterable protocol"
|
||||
[Symbol.iterator]() {
|
||||
return this.iterator();
|
||||
}
|
||||
|
||||
forEach(iterator: (row: T) => void) {
|
||||
forEach(iterator: (row: T, index: number, array: T[]) => void): void {
|
||||
return vectorator(this).forEach(iterator);
|
||||
}
|
||||
|
||||
map<V>(transform: (item: T, index: number) => V) {
|
||||
map<V>(transform: (item: T, index: number, array: T[]) => V): V[] {
|
||||
return vectorator(this).map(transform);
|
||||
}
|
||||
|
||||
filter(predicate: (item: T) => boolean): T[] {
|
||||
filter(predicate: (item: T, index: number, array: T[]) => boolean): T[] {
|
||||
return vectorator(this).filter(predicate);
|
||||
}
|
||||
|
||||
at(index: number): T | undefined {
|
||||
return this.get(index);
|
||||
}
|
||||
|
||||
toArray(): T[] {
|
||||
return vectorToArray(this);
|
||||
}
|
||||
|
||||
join(separator?: string | undefined): string {
|
||||
return this.toArray().join(separator);
|
||||
}
|
||||
|
||||
toJSON(): any {
|
||||
return this.toArray();
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
// Method not implemented
|
||||
//--------------------------
|
||||
|
||||
[n: number]: T;
|
||||
|
||||
pop(): T | undefined {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
concat(...items: Array<ConcatArray<T>>): T[];
|
||||
concat(...items: Array<T | ConcatArray<T>>): T[] {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
reverse(): T[] {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
shift(): T | undefined {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
slice(start?: number | undefined, end?: number | undefined): T[] {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
sort(compareFn?: ((a: T, b: T) => number) | undefined): this {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
splice(start: number, deleteCount?: number | undefined): T[];
|
||||
splice(start: number, deleteCount: number, ...items: T[]): T[] {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
unshift(...items: T[]): number {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
fill(value: T, start?: number | undefined, end?: number | undefined): this {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
copyWithin(target: number, start: number, end?: number | undefined): this {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
[Symbol.unscopables](): {
|
||||
copyWithin: boolean;
|
||||
entries: boolean;
|
||||
fill: boolean;
|
||||
find: boolean;
|
||||
findIndex: boolean;
|
||||
keys: boolean;
|
||||
values: boolean;
|
||||
} {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Delegated Array function -- these will not be efficient :grimmice:
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
indexOf(searchElement: T, fromIndex?: number | undefined): number {
|
||||
return this.toArray().indexOf(searchElement, fromIndex);
|
||||
}
|
||||
lastIndexOf(searchElement: T, fromIndex?: number | undefined): number {
|
||||
return this.toArray().lastIndexOf(searchElement, fromIndex);
|
||||
}
|
||||
every<S extends T>(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any): this is S[];
|
||||
every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
|
||||
every(predicate: any, thisArg?: unknown): boolean {
|
||||
return this.toArray().every(predicate, thisArg);
|
||||
}
|
||||
some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean {
|
||||
return this.toArray().some(predicate, thisArg);
|
||||
}
|
||||
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
|
||||
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
|
||||
reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
|
||||
reduce(callbackfn: unknown, initialValue?: unknown): T {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
|
||||
reduceRight(
|
||||
callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T,
|
||||
initialValue: T
|
||||
): T;
|
||||
reduceRight<U>(
|
||||
callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U,
|
||||
initialValue: U
|
||||
): U;
|
||||
reduceRight(callbackfn: unknown, initialValue?: unknown): T {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
find<S extends T>(
|
||||
predicate: (this: void, value: T, index: number, obj: T[]) => value is S,
|
||||
thisArg?: any
|
||||
): S | undefined;
|
||||
find(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): T | undefined {
|
||||
return this.toArray().find(predicate, thisArg);
|
||||
}
|
||||
findIndex(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): number {
|
||||
return this.toArray().findIndex(predicate, thisArg);
|
||||
}
|
||||
entries(): IterableIterator<[number, T]> {
|
||||
return this.toArray().entries();
|
||||
}
|
||||
keys(): IterableIterator<number> {
|
||||
return this.toArray().keys();
|
||||
}
|
||||
values(): IterableIterator<T> {
|
||||
return this.toArray().values();
|
||||
}
|
||||
includes(searchElement: T, fromIndex?: number | undefined): boolean {
|
||||
return this.toArray().includes(searchElement, fromIndex);
|
||||
}
|
||||
flatMap<U, This = undefined>(
|
||||
callback: (this: This, value: T, index: number, array: T[]) => U | readonly U[],
|
||||
thisArg?: This | undefined
|
||||
): U[] {
|
||||
return this.toArray().flatMap(callback, thisArg);
|
||||
}
|
||||
flat<A, D extends number = 1>(this: A, depth?: D | undefined): Array<FlatArray<A, D>> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
const emptyarray: any[] = [];
|
||||
|
||||
/**
|
||||
* Use functional programming with your vector
|
||||
*/
|
||||
@@ -52,25 +196,26 @@ export function vectorator<T>(vector: Vector<T>) {
|
||||
}
|
||||
},
|
||||
|
||||
forEach(iterator: (row: T) => void) {
|
||||
forEach(iterator: (row: T, index: number, array: T[]) => void): void {
|
||||
for (let i = 0; i < vector.length; i++) {
|
||||
iterator(vector.get(i));
|
||||
iterator(vector.get(i), i, emptyarray);
|
||||
}
|
||||
},
|
||||
|
||||
map<V>(transform: (item: T, index: number) => V) {
|
||||
map<V>(transform: (item: T, index: number, array: T[]) => V): V[] {
|
||||
const result: V[] = [];
|
||||
for (let i = 0; i < vector.length; i++) {
|
||||
result.push(transform(vector.get(i), i));
|
||||
result.push(transform(vector.get(i), i, emptyarray));
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/** Add a predicate where you return true if it should *keep* the value */
|
||||
filter(predicate: (item: T) => boolean): T[] {
|
||||
filter(predicate: (item: T, index: number, array: T[]) => boolean): T[] {
|
||||
const result: T[] = [];
|
||||
let count = 0;
|
||||
for (const val of this) {
|
||||
if (predicate(val)) {
|
||||
if (predicate(val, count++, emptyarray)) {
|
||||
result.push(val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import { FunctionalVector } from './FunctionalVector';
|
||||
* IndexVector is a simple vector implementation that returns the index value
|
||||
* for each element in the vector. It is functionally equivolant a vector backed
|
||||
* by an array with values: `[0,1,2,...,length-1]`
|
||||
*
|
||||
* @deprecated use a simple Arrays
|
||||
*/
|
||||
export class IndexVector extends FunctionalVector<number> {
|
||||
constructor(private len: number) {
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { Vector } from '../types';
|
||||
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
import { vectorToArray } from './vectorToArray';
|
||||
|
||||
/**
|
||||
* RowVector makes the row values look like a vector
|
||||
* @internal
|
||||
*/
|
||||
export class RowVector implements Vector {
|
||||
constructor(private columns: Vector[]) {}
|
||||
export class RowVector extends FunctionalVector<number> {
|
||||
constructor(private columns: Vector[]) {
|
||||
super();
|
||||
}
|
||||
|
||||
rowIndex = 0;
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import { Vector } from '../types/vector';
|
||||
|
||||
import { FunctionalVector } from './FunctionalVector';
|
||||
import { vectorToArray } from './vectorToArray';
|
||||
|
||||
/**
|
||||
* Values are returned in the order defined by the input parameter
|
||||
*
|
||||
* @deprecated use a simple Arrays
|
||||
*/
|
||||
export class SortedVector<T = any> implements Vector<T> {
|
||||
constructor(private source: Vector<T>, private order: number[]) {}
|
||||
export class SortedVector<T = any> extends FunctionalVector<T> {
|
||||
constructor(private source: Vector<T>, private order: number[]) {
|
||||
super();
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this.source.length;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Vector } from '../types/vector';
|
||||
|
||||
/** @deprecated use a simple Arrays */
|
||||
export function vectorToArray<T>(v: Vector<T>): T[] {
|
||||
const arr: T[] = Array(v.length);
|
||||
for (let i = 0; i < v.length; i++) {
|
||||
|
||||
@@ -59,9 +59,9 @@ describe('nullInsertThreshold Transformer', () => {
|
||||
|
||||
const result = applyNullInsertThreshold({ frame: df });
|
||||
|
||||
expect(result.fields[0].values.toArray()).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
expect(result.fields[1].values.toArray()).toStrictEqual([4, null, 6, null, null, null, null, null, null, 8]);
|
||||
expect(result.fields[2].values.toArray()).toStrictEqual(['a', null, 'b', null, null, null, null, null, null, 'c']);
|
||||
expect(result.fields[0].values).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
expect(result.fields[1].values).toEqual([4, null, 6, null, null, null, null, null, null, 8]);
|
||||
expect(result.fields[2].values).toEqual(['a', null, 'b', null, null, null, null, null, null, 'c']);
|
||||
});
|
||||
|
||||
test('should insert nulls at +threshold between adjacent > threshold: 2', () => {
|
||||
@@ -76,9 +76,9 @@ describe('nullInsertThreshold Transformer', () => {
|
||||
|
||||
const result = applyNullInsertThreshold({ frame: df });
|
||||
|
||||
expect(result.fields[0].values.toArray()).toStrictEqual([5, 7, 9, 11]);
|
||||
expect(result.fields[1].values.toArray()).toStrictEqual([4, 6, null, 8]);
|
||||
expect(result.fields[2].values.toArray()).toStrictEqual(['a', 'b', null, 'c']);
|
||||
expect(result.fields[0].values).toEqual([5, 7, 9, 11]);
|
||||
expect(result.fields[1].values).toEqual([4, 6, null, 8]);
|
||||
expect(result.fields[2].values).toEqual(['a', 'b', null, 'c']);
|
||||
});
|
||||
|
||||
test('should insert nulls at +interval between adjacent > interval: 1', () => {
|
||||
@@ -93,9 +93,9 @@ describe('nullInsertThreshold Transformer', () => {
|
||||
|
||||
const result = applyNullInsertThreshold({ frame: df });
|
||||
|
||||
expect(result.fields[0].values.toArray()).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
expect(result.fields[1].values.toArray()).toStrictEqual([4, null, 6, null, null, null, null, null, null, 8]);
|
||||
expect(result.fields[2].values.toArray()).toStrictEqual(['a', null, 'b', null, null, null, null, null, null, 'c']);
|
||||
expect(result.fields[0].values).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
expect(result.fields[1].values).toEqual([4, null, 6, null, null, null, null, null, null, 8]);
|
||||
expect(result.fields[2].values).toEqual(['a', null, 'b', null, null, null, null, null, null, 'c']);
|
||||
});
|
||||
|
||||
test('should insert leading null at beginning +interval when timeRange.from.valueOf() exceeds threshold', () => {
|
||||
@@ -115,8 +115,8 @@ describe('nullInsertThreshold Transformer', () => {
|
||||
refFieldPseudoMax: 13,
|
||||
});
|
||||
|
||||
expect(result.fields[0].values.toArray()).toStrictEqual([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
|
||||
expect(result.fields[1].values.toArray()).toStrictEqual([
|
||||
expect(result.fields[0].values).toEqual([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
|
||||
expect(result.fields[1].values).toEqual([
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
@@ -133,7 +133,7 @@ describe('nullInsertThreshold Transformer', () => {
|
||||
null,
|
||||
8,
|
||||
]);
|
||||
expect(result.fields[2].values.toArray()).toStrictEqual([
|
||||
expect(result.fields[2].values).toEqual([
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
@@ -164,35 +164,9 @@ describe('nullInsertThreshold Transformer', () => {
|
||||
|
||||
const result = applyNullInsertThreshold({ frame: df, refFieldName: null, refFieldPseudoMax: 13 });
|
||||
|
||||
expect(result.fields[0].values.toArray()).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
|
||||
expect(result.fields[1].values.toArray()).toStrictEqual([
|
||||
4,
|
||||
null,
|
||||
6,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
8,
|
||||
null,
|
||||
null,
|
||||
]);
|
||||
expect(result.fields[2].values.toArray()).toStrictEqual([
|
||||
'a',
|
||||
null,
|
||||
'b',
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
'c',
|
||||
null,
|
||||
null,
|
||||
]);
|
||||
expect(result.fields[0].values).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
|
||||
expect(result.fields[1].values).toEqual([4, null, 6, null, null, null, null, null, null, 8, null, null]);
|
||||
expect(result.fields[2].values).toEqual(['a', null, 'b', null, null, null, null, null, null, 'c', null, null]);
|
||||
|
||||
// should work for frames with 1 datapoint
|
||||
const df2 = new MutableDataFrame({
|
||||
@@ -208,9 +182,9 @@ describe('nullInsertThreshold Transformer', () => {
|
||||
// we get 12 nulls instead of the additional 1
|
||||
const result2 = applyNullInsertThreshold({ frame: df2, refFieldName: null, refFieldPseudoMax: 2.5 });
|
||||
|
||||
expect(result2.fields[0].values.toArray()).toStrictEqual([1, 2]);
|
||||
expect(result2.fields[1].values.toArray()).toStrictEqual([1, null]);
|
||||
expect(result2.fields[2].values.toArray()).toStrictEqual(['a', null]);
|
||||
expect(result2.fields[0].values).toEqual([1, 2]);
|
||||
expect(result2.fields[1].values).toEqual([1, null]);
|
||||
expect(result2.fields[2].values).toEqual(['a', null]);
|
||||
});
|
||||
|
||||
test('should not insert trailing null at end +interval when timeRange.to.valueOf() equals threshold', () => {
|
||||
@@ -225,9 +199,9 @@ describe('nullInsertThreshold Transformer', () => {
|
||||
|
||||
const result = applyNullInsertThreshold({ frame: df, refFieldName: null, refFieldPseudoMax: 2 });
|
||||
|
||||
expect(result.fields[0].values.toArray()).toStrictEqual([1]);
|
||||
expect(result.fields[1].values.toArray()).toStrictEqual([1]);
|
||||
expect(result.fields[2].values.toArray()).toStrictEqual(['a']);
|
||||
expect(result.fields[0].values).toEqual([1]);
|
||||
expect(result.fields[1].values).toEqual([1]);
|
||||
expect(result.fields[2].values).toEqual(['a']);
|
||||
});
|
||||
|
||||
// TODO: make this work
|
||||
@@ -243,9 +217,9 @@ describe('nullInsertThreshold Transformer', () => {
|
||||
|
||||
const result = applyNullInsertThreshold({ frame: df });
|
||||
|
||||
expect(result.fields[0].values.toArray()).toStrictEqual([5, 6, 7, 8, 11]);
|
||||
expect(result.fields[1].values.toArray()).toStrictEqual([4, null, 6, null, 8]);
|
||||
expect(result.fields[2].values.toArray()).toStrictEqual(['a', null, 'b', null, 'c']);
|
||||
expect(result.fields[0].values).toEqual([5, 6, 7, 8, 11]);
|
||||
expect(result.fields[1].values).toEqual([4, null, 6, null, 8]);
|
||||
expect(result.fields[2].values).toEqual(['a', null, 'b', null, 'c']);
|
||||
});
|
||||
|
||||
test('should noop on 0 datapoints', () => {
|
||||
|
||||
@@ -26,9 +26,9 @@ describe('nullToValue Transformer', () => {
|
||||
|
||||
const result = nullToValue(applyNullInsertThreshold({ frame: df }));
|
||||
|
||||
expect(result.fields[0].values.toArray()).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
expect(result.fields[1].values.toArray()).toStrictEqual([4, 0, 6, 0, 0, 0, 0, 0, 0, 8]);
|
||||
expect(result.fields[2].values.toArray()).toStrictEqual(['a', 0, 'b', 0, 0, 0, 0, 0, 0, 'c']);
|
||||
expect(result.fields[0].values).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
expect(result.fields[1].values).toEqual([4, 0, 6, 0, 0, 0, 0, 0, 0, 8]);
|
||||
expect(result.fields[2].values).toEqual(['a', 0, 'b', 0, 0, 0, 0, 0, 0, 'c']);
|
||||
});
|
||||
|
||||
test('should change all nulls to configured positive value', () => {
|
||||
@@ -53,9 +53,9 @@ describe('nullToValue Transformer', () => {
|
||||
|
||||
const result = nullToValue(applyNullInsertThreshold({ frame: df }));
|
||||
|
||||
expect(result.fields[0].values.toArray()).toStrictEqual([5, 7, 9, 11]);
|
||||
expect(result.fields[1].values.toArray()).toStrictEqual([4, 6, 1, 8]);
|
||||
expect(result.fields[2].values.toArray()).toStrictEqual(['a', 'b', 1, 'c']);
|
||||
expect(result.fields[0].values).toEqual([5, 7, 9, 11]);
|
||||
expect(result.fields[1].values).toEqual([4, 6, 1, 8]);
|
||||
expect(result.fields[2].values).toEqual(['a', 'b', 1, 'c']);
|
||||
});
|
||||
|
||||
test('should change all nulls to configured negative value', () => {
|
||||
@@ -70,9 +70,9 @@ describe('nullToValue Transformer', () => {
|
||||
|
||||
const result = nullToValue(applyNullInsertThreshold({ frame: df }));
|
||||
|
||||
expect(result.fields[0].values.toArray()).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
expect(result.fields[1].values.toArray()).toStrictEqual([4, -1, 6, -1, -1, -1, -1, -1, -1, 8]);
|
||||
expect(result.fields[2].values.toArray()).toStrictEqual(['a', -1, 'b', -1, -1, -1, -1, -1, -1, 'c']);
|
||||
expect(result.fields[0].values).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
expect(result.fields[1].values).toEqual([4, -1, 6, -1, -1, -1, -1, -1, -1, 8]);
|
||||
expect(result.fields[2].values).toEqual(['a', -1, 'b', -1, -1, -1, -1, -1, -1, 'c']);
|
||||
});
|
||||
|
||||
test('should have no effect without nulls', () => {
|
||||
@@ -87,8 +87,8 @@ describe('nullToValue Transformer', () => {
|
||||
|
||||
const result = nullToValue(applyNullInsertThreshold({ frame: df, refFieldName: null }));
|
||||
|
||||
expect(result.fields[0].values.toArray()).toStrictEqual([1, 2, 3]);
|
||||
expect(result.fields[1].values.toArray()).toStrictEqual([4, 6, 8]);
|
||||
expect(result.fields[2].values.toArray()).toStrictEqual(['a', 'b', 'c']);
|
||||
expect(result.fields[0].values).toEqual([1, 2, 3]);
|
||||
expect(result.fields[1].values).toEqual([4, 6, 8]);
|
||||
expect(result.fields[2].values).toEqual(['a', 'b', 'c']);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user