| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- import { Field, DataFrame, DataFrameDTO, FieldDTO, FieldType } from '../types/dataFrame';
- import { KeyValue, QueryResultMeta, Labels } from '../types/data';
- import { guessFieldTypeFromValue, guessFieldTypeForField, toDataFrameDTO } from './processDataFrame';
- import isArray from 'lodash/isArray';
- import isString from 'lodash/isString';
- import { makeFieldParser } from '../utils/fieldParser';
- import { MutableVector, Vector } from '../types/vector';
- import { ArrayVector } from '../vector/ArrayVector';
- import { vectorToArray } from '../vector/vectorToArray';
- export type MutableField<T = any> = Field<T, MutableVector<T>>;
- type MutableVectorCreator = (buffer?: any[]) => MutableVector;
- export const MISSING_VALUE: any = null;
- export class MutableDataFrame<T = any> implements DataFrame, MutableVector<T> {
- name?: string;
- labels?: Labels;
- refId?: string;
- meta?: QueryResultMeta;
- fields: MutableField[] = [];
- values: KeyValue<MutableVector> = {};
- private first: Vector = new ArrayVector();
- private creator: MutableVectorCreator;
- constructor(source?: DataFrame | DataFrameDTO, creator?: MutableVectorCreator) {
- // This creates the underlying storage buffers
- this.creator = creator
- ? creator
- : (buffer?: any[]) => {
- return new ArrayVector(buffer);
- };
- // Copy values from
- if (source) {
- const { name, labels, refId, meta, fields } = source;
- if (name) {
- this.name = name;
- }
- if (labels) {
- this.labels = labels;
- }
- if (refId) {
- this.refId = refId;
- }
- if (meta) {
- this.meta = meta;
- }
- if (fields) {
- for (const f of fields) {
- this.addField(f);
- }
- }
- }
- // Get Length to show up if you use spread
- Object.defineProperty(this, 'length', {
- enumerable: true,
- get: () => {
- return this.first.length;
- },
- });
- }
- // Defined for Vector interface
- get length() {
- return this.first.length;
- }
- addFieldFor(value: any, name?: string): MutableField {
- return this.addField({
- name: name || '', // Will be filled in
- type: guessFieldTypeFromValue(value),
- });
- }
- addField(f: Field | FieldDTO, startLength?: number): MutableField {
- let buffer: any[] | undefined = undefined;
- if (f.values) {
- if (isArray(f.values)) {
- buffer = f.values as any[];
- } else {
- buffer = (f.values as Vector).toArray();
- }
- }
- let type = f.type;
- if (!type && ('time' === f.name || 'Time' === f.name)) {
- type = FieldType.time;
- } else {
- if (!type && buffer && buffer.length) {
- type = guessFieldTypeFromValue(buffer[0]);
- }
- if (!type) {
- type = FieldType.other;
- }
- }
- // Make sure it has a name
- let name = f.name;
- if (!name) {
- if (type === FieldType.time) {
- name = this.values['Time'] ? `Time ${this.fields.length + 1}` : 'Time';
- } else {
- name = `Field ${this.fields.length + 1}`;
- }
- }
- const field: MutableField = {
- name,
- type,
- config: f.config || {},
- values: this.creator(buffer),
- };
- if (type === FieldType.other) {
- type = guessFieldTypeForField(field);
- if (type) {
- field.type = type;
- }
- }
- this.fields.push(field);
- this.first = this.fields[0].values;
- // The Field Already exists
- if (this.values[name]) {
- console.warn(`Duplicate field names found: ${name}, only the first will be accessible`);
- } else {
- this.values[name] = field.values;
- }
- // Make sure the field starts with a given length
- if (startLength) {
- while (field.values.length < startLength) {
- field.values.add(MISSING_VALUE);
- }
- } else {
- this.validate();
- }
- return field;
- }
- validate() {
- // Make sure all arrays are the same length
- const length = this.fields.reduce((v: number, f) => {
- return Math.max(v, f.values.length);
- }, 0);
- // Add empty elements until everything mastches
- for (const field of this.fields) {
- while (field.values.length !== length) {
- field.values.add(MISSING_VALUE);
- }
- }
- }
- private addMissingFieldsFor(value: any) {
- for (const key of Object.keys(value)) {
- if (!this.values[key]) {
- this.addField({
- name: key,
- type: guessFieldTypeFromValue(value[key]),
- });
- }
- }
- }
- /**
- * Reverse all values
- */
- reverse() {
- for (const f of this.fields) {
- f.values.reverse();
- }
- }
- /**
- * This will add each value to the corresponding column
- */
- appendRow(row: any[]) {
- // Add any extra columns
- for (let i = this.fields.length; i < row.length; i++) {
- this.addField({
- name: `Field ${i + 1}`,
- type: guessFieldTypeFromValue(row[i]),
- });
- }
- // The first line may change the field types
- if (this.length < 1) {
- for (let i = 0; i < this.fields.length; i++) {
- const f = this.fields[i];
- if (!f.type || f.type === FieldType.other) {
- f.type = guessFieldTypeFromValue(row[i]);
- }
- }
- }
- for (let i = 0; i < this.fields.length; i++) {
- const f = this.fields[i];
- let v = row[i];
- if (f.type !== FieldType.string && isString(v)) {
- if (!f.parse) {
- f.parse = makeFieldParser(v, f);
- }
- v = f.parse(v);
- }
- f.values.add(v);
- }
- }
- /**
- * Add all properties of the value as fields on the frame
- */
- add(value: T, addMissingFields?: boolean) {
- if (addMissingFields) {
- this.addMissingFieldsFor(value);
- }
- // Will add one value for every field
- const obj = value as any;
- for (const field of this.fields) {
- let val = obj[field.name];
- if (field.type !== FieldType.string && isString(val)) {
- if (!field.parse) {
- field.parse = makeFieldParser(val, field);
- }
- val = field.parse(val);
- }
- if (val === undefined) {
- val = MISSING_VALUE;
- }
- field.values.add(val);
- }
- }
- set(index: number, value: T, addMissingFields?: boolean) {
- if (index > this.length) {
- throw new Error('Unable ot set value beyond current length');
- }
- if (addMissingFields) {
- this.addMissingFieldsFor(value);
- }
- const obj = (value as any) || {};
- for (const field of this.fields) {
- field.values.set(index, obj[field.name]);
- }
- }
- /**
- * Get an object with a property for each field in the DataFrame
- */
- get(idx: number): T {
- const v: any = {};
- for (const field of this.fields) {
- v[field.name] = field.values.get(idx);
- }
- return v as T;
- }
- toArray(): T[] {
- return vectorToArray(this);
- }
- /**
- * The simplified JSON values used in JSON.stringify()
- */
- toJSON() {
- return toDataFrameDTO(this);
- }
- }
|