mirror of
https://github.com/grafana/grafana.git
synced 2024-12-23 15:40:19 -06:00
DataFrame: Improve typing of arrayToDataFrame helper and fix null/undefined handling (#83104)
This commit is contained in:
parent
4b2ef36165
commit
d48bf34227
@ -9,8 +9,7 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"packages/grafana-data/src/dataframe/ArrayDataFrame.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"packages/grafana-data/src/dataframe/CircularDataFrame.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
|
@ -101,4 +101,44 @@ describe('Array DataFrame', () => {
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('Handles first null value', () => {
|
||||
const f = arrayToDataFrame([null, { id: 'abc' }]);
|
||||
expect(f).toMatchInlineSnapshot(`
|
||||
{
|
||||
"fields": [
|
||||
{
|
||||
"config": {},
|
||||
"name": "id",
|
||||
"type": "string",
|
||||
"values": [
|
||||
null,
|
||||
"abc",
|
||||
],
|
||||
},
|
||||
],
|
||||
"length": 2,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('Handles first undefined value', () => {
|
||||
const f = arrayToDataFrame([undefined, { id: 'abc' }]);
|
||||
expect(f).toMatchInlineSnapshot(`
|
||||
{
|
||||
"fields": [
|
||||
{
|
||||
"config": {},
|
||||
"name": "id",
|
||||
"type": "string",
|
||||
"values": [
|
||||
undefined,
|
||||
"abc",
|
||||
],
|
||||
},
|
||||
],
|
||||
"length": 2,
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
@ -21,11 +21,13 @@ export class ArrayDataFrame<T = any> implements DataFrame {
|
||||
}
|
||||
|
||||
/**
|
||||
* arrayToDataFrame will convert any array into a DataFrame
|
||||
* arrayToDataFrame will convert any array into a DataFrame.
|
||||
* @param source - can be an array of objects or an array of simple values.
|
||||
* @param names - will be used for ordering of fields. Source needs to be array of objects if names are provided.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function arrayToDataFrame(source: any[], names?: string[]): DataFrame {
|
||||
export function arrayToDataFrame(source: Array<Record<string, unknown>> | unknown[], names?: string[]): DataFrame {
|
||||
const df: DataFrame = {
|
||||
fields: [],
|
||||
length: source.length,
|
||||
@ -34,30 +36,46 @@ export function arrayToDataFrame(source: any[], names?: string[]): DataFrame {
|
||||
return df;
|
||||
}
|
||||
|
||||
// If names are provided then we assume the source is an array of objects with the names as keys (field names). This
|
||||
// makes ordering of the fields predictable.
|
||||
if (names) {
|
||||
if (!isObjectArray(source)) {
|
||||
throw new Error('source is not an array of objects');
|
||||
}
|
||||
|
||||
for (const name of names) {
|
||||
df.fields.push(
|
||||
makeFieldFromValues(
|
||||
name,
|
||||
source.map((v) => v[name])
|
||||
source.map((v) => (v ? v[name] : v))
|
||||
)
|
||||
);
|
||||
}
|
||||
return df;
|
||||
}
|
||||
|
||||
const first = source.find((v) => v != null); // first not null|undefined
|
||||
if (first != null) {
|
||||
if (typeof first === 'object') {
|
||||
df.fields = Object.keys(first).map((name) => {
|
||||
return makeFieldFromValues(
|
||||
name,
|
||||
source.map((v) => v[name])
|
||||
);
|
||||
});
|
||||
} else {
|
||||
df.fields.push(makeFieldFromValues(TIME_SERIES_VALUE_FIELD_NAME, source));
|
||||
}
|
||||
const firstDefined = source.find((v) => v); // first not null|undefined
|
||||
// This means if the source is lots of null/undefined values we throw that away and return empty dataFrame. This is
|
||||
// different to how we preserve null/undefined values if there is some defined rows. Not sure this inconsistency
|
||||
// is by design or not.
|
||||
if (firstDefined === null) {
|
||||
return df;
|
||||
}
|
||||
|
||||
// If is an array of objects we use the keys as field names.
|
||||
if (isObjectArray(source)) {
|
||||
// We need to do this to please TS. We know source is array of objects and that there is some object in there but
|
||||
// TS still thinks it can all be undefined|nulls.
|
||||
const first = source.find((v) => v);
|
||||
df.fields = Object.keys(first || {}).map((name) => {
|
||||
return makeFieldFromValues(
|
||||
name,
|
||||
source.map((v) => (v ? v[name] : v))
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Otherwise source should be an array of simple values, so we create single field data frame.
|
||||
df.fields.push(makeFieldFromValues(TIME_SERIES_VALUE_FIELD_NAME, source));
|
||||
}
|
||||
return df;
|
||||
}
|
||||
@ -67,3 +85,8 @@ function makeFieldFromValues(name: string, values: unknown[]): Field {
|
||||
f.type = guessFieldTypeForField(f) ?? FieldType.other;
|
||||
return f;
|
||||
}
|
||||
|
||||
function isObjectArray(arr: unknown[]): arr is Array<Record<string, unknown> | null | undefined> {
|
||||
const first = arr.find((v) => v); // first not null|undefined
|
||||
return arr.length > 0 && typeof first === 'object';
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user