MutableDataFrame.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. import { Field, DataFrame, DataFrameDTO, FieldDTO, FieldType } from '../types/dataFrame';
  2. import { KeyValue, QueryResultMeta, Labels } from '../types/data';
  3. import { guessFieldTypeFromValue, guessFieldTypeForField, toDataFrameDTO } from './processDataFrame';
  4. import isArray from 'lodash/isArray';
  5. import isString from 'lodash/isString';
  6. import { makeFieldParser } from '../utils/fieldParser';
  7. import { MutableVector, Vector } from '../types/vector';
  8. import { ArrayVector } from '../vector/ArrayVector';
  9. import { vectorToArray } from '../vector/vectorToArray';
  10. export type MutableField<T = any> = Field<T, MutableVector<T>>;
  11. type MutableVectorCreator = (buffer?: any[]) => MutableVector;
  12. export const MISSING_VALUE: any = null;
  13. export class MutableDataFrame<T = any> implements DataFrame, MutableVector<T> {
  14. name?: string;
  15. labels?: Labels;
  16. refId?: string;
  17. meta?: QueryResultMeta;
  18. fields: MutableField[] = [];
  19. values: KeyValue<MutableVector> = {};
  20. private first: Vector = new ArrayVector();
  21. private creator: MutableVectorCreator;
  22. constructor(source?: DataFrame | DataFrameDTO, creator?: MutableVectorCreator) {
  23. // This creates the underlying storage buffers
  24. this.creator = creator
  25. ? creator
  26. : (buffer?: any[]) => {
  27. return new ArrayVector(buffer);
  28. };
  29. // Copy values from
  30. if (source) {
  31. const { name, labels, refId, meta, fields } = source;
  32. if (name) {
  33. this.name = name;
  34. }
  35. if (labels) {
  36. this.labels = labels;
  37. }
  38. if (refId) {
  39. this.refId = refId;
  40. }
  41. if (meta) {
  42. this.meta = meta;
  43. }
  44. if (fields) {
  45. for (const f of fields) {
  46. this.addField(f);
  47. }
  48. }
  49. }
  50. // Get Length to show up if you use spread
  51. Object.defineProperty(this, 'length', {
  52. enumerable: true,
  53. get: () => {
  54. return this.first.length;
  55. },
  56. });
  57. }
  58. // Defined for Vector interface
  59. get length() {
  60. return this.first.length;
  61. }
  62. addFieldFor(value: any, name?: string): MutableField {
  63. return this.addField({
  64. name: name || '', // Will be filled in
  65. type: guessFieldTypeFromValue(value),
  66. });
  67. }
  68. addField(f: Field | FieldDTO, startLength?: number): MutableField {
  69. let buffer: any[] | undefined = undefined;
  70. if (f.values) {
  71. if (isArray(f.values)) {
  72. buffer = f.values as any[];
  73. } else {
  74. buffer = (f.values as Vector).toArray();
  75. }
  76. }
  77. let type = f.type;
  78. if (!type && ('time' === f.name || 'Time' === f.name)) {
  79. type = FieldType.time;
  80. } else {
  81. if (!type && buffer && buffer.length) {
  82. type = guessFieldTypeFromValue(buffer[0]);
  83. }
  84. if (!type) {
  85. type = FieldType.other;
  86. }
  87. }
  88. // Make sure it has a name
  89. let name = f.name;
  90. if (!name) {
  91. if (type === FieldType.time) {
  92. name = this.values['Time'] ? `Time ${this.fields.length + 1}` : 'Time';
  93. } else {
  94. name = `Field ${this.fields.length + 1}`;
  95. }
  96. }
  97. const field: MutableField = {
  98. name,
  99. type,
  100. config: f.config || {},
  101. values: this.creator(buffer),
  102. };
  103. if (type === FieldType.other) {
  104. type = guessFieldTypeForField(field);
  105. if (type) {
  106. field.type = type;
  107. }
  108. }
  109. this.fields.push(field);
  110. this.first = this.fields[0].values;
  111. // The Field Already exists
  112. if (this.values[name]) {
  113. console.warn(`Duplicate field names found: ${name}, only the first will be accessible`);
  114. } else {
  115. this.values[name] = field.values;
  116. }
  117. // Make sure the field starts with a given length
  118. if (startLength) {
  119. while (field.values.length < startLength) {
  120. field.values.add(MISSING_VALUE);
  121. }
  122. } else {
  123. this.validate();
  124. }
  125. return field;
  126. }
  127. validate() {
  128. // Make sure all arrays are the same length
  129. const length = this.fields.reduce((v: number, f) => {
  130. return Math.max(v, f.values.length);
  131. }, 0);
  132. // Add empty elements until everything mastches
  133. for (const field of this.fields) {
  134. while (field.values.length !== length) {
  135. field.values.add(MISSING_VALUE);
  136. }
  137. }
  138. }
  139. private addMissingFieldsFor(value: any) {
  140. for (const key of Object.keys(value)) {
  141. if (!this.values[key]) {
  142. this.addField({
  143. name: key,
  144. type: guessFieldTypeFromValue(value[key]),
  145. });
  146. }
  147. }
  148. }
  149. /**
  150. * Reverse all values
  151. */
  152. reverse() {
  153. for (const f of this.fields) {
  154. f.values.reverse();
  155. }
  156. }
  157. /**
  158. * This will add each value to the corresponding column
  159. */
  160. appendRow(row: any[]) {
  161. // Add any extra columns
  162. for (let i = this.fields.length; i < row.length; i++) {
  163. this.addField({
  164. name: `Field ${i + 1}`,
  165. type: guessFieldTypeFromValue(row[i]),
  166. });
  167. }
  168. // The first line may change the field types
  169. if (this.length < 1) {
  170. for (let i = 0; i < this.fields.length; i++) {
  171. const f = this.fields[i];
  172. if (!f.type || f.type === FieldType.other) {
  173. f.type = guessFieldTypeFromValue(row[i]);
  174. }
  175. }
  176. }
  177. for (let i = 0; i < this.fields.length; i++) {
  178. const f = this.fields[i];
  179. let v = row[i];
  180. if (f.type !== FieldType.string && isString(v)) {
  181. if (!f.parse) {
  182. f.parse = makeFieldParser(v, f);
  183. }
  184. v = f.parse(v);
  185. }
  186. f.values.add(v);
  187. }
  188. }
  189. /**
  190. * Add all properties of the value as fields on the frame
  191. */
  192. add(value: T, addMissingFields?: boolean) {
  193. if (addMissingFields) {
  194. this.addMissingFieldsFor(value);
  195. }
  196. // Will add one value for every field
  197. const obj = value as any;
  198. for (const field of this.fields) {
  199. let val = obj[field.name];
  200. if (field.type !== FieldType.string && isString(val)) {
  201. if (!field.parse) {
  202. field.parse = makeFieldParser(val, field);
  203. }
  204. val = field.parse(val);
  205. }
  206. if (val === undefined) {
  207. val = MISSING_VALUE;
  208. }
  209. field.values.add(val);
  210. }
  211. }
  212. set(index: number, value: T, addMissingFields?: boolean) {
  213. if (index > this.length) {
  214. throw new Error('Unable ot set value beyond current length');
  215. }
  216. if (addMissingFields) {
  217. this.addMissingFieldsFor(value);
  218. }
  219. const obj = (value as any) || {};
  220. for (const field of this.fields) {
  221. field.values.set(index, obj[field.name]);
  222. }
  223. }
  224. /**
  225. * Get an object with a property for each field in the DataFrame
  226. */
  227. get(idx: number): T {
  228. const v: any = {};
  229. for (const field of this.fields) {
  230. v[field.name] = field.values.get(idx);
  231. }
  232. return v as T;
  233. }
  234. toArray(): T[] {
  235. return vectorToArray(this);
  236. }
  237. /**
  238. * The simplified JSON values used in JSON.stringify()
  239. */
  240. toJSON() {
  241. return toDataFrameDTO(this);
  242. }
  243. }