I have this dataset:
const dataset = [
{ date: "2022-01-01", category: "red", value: 10 },
{ date: "2022-01-01", category: "blue", value: 20 },
{ date: "2022-01-01", category: "gold", value: 30 },
{ date: "2022-01-01", category: "green", value: 40 },
{ date: "2022-01-02", category: "red", value: 5 },
{ date: "2022-01-02", category: "blue", value: 15 },
{ date: "2022-01-02", category: "gold", value: 25 },
{ date: "2022-01-02", category: "green", value: 35 }
];
And I need to flat the dataset to get this:
const flattenDataset = [
{ "date": "2022-01-01", "red": 10, "blue": 20, "gold": 30, "green": 40 },
{ "date": "2022-01-02", "red": 5, "blue": 15, "gold": 25, "green": 35 }
]
So group dataset by date
s and for each category
create a key with value
as value.
I created this function:
export function flatDataset(
dataset: any,
mainProperty: string,
categoryProperty: string,
valueProperty: string
) {
if (dataset.length === 0 || !mainProperty) {
return (dataset as unknown);
}
const columnToBeFlatValues = uniqBy(dataset, categoryProperty).map(
(d) => d[categoryProperty]
);
const datasetGroupedByMainProperty = groupBy(dataset, mainProperty);
const datasetGroupedByMainCategoryFlat = Object.entries(
datasetGroupedByMainProperty
).map(([date, datasetForDate]) => {
const categoriesObject = columnToBeFlatValues.reduce((acc, value) => {
const datum = datasetForDate.find(
(d) => d[mainProperty] === date && d[categoryProperty] === value
);
acc[value] = datum?.[valueProperty];
return acc;
}, {});
return {
[mainProperty]: date,
...categoriesObject
};
});
return datasetGroupedByMainCategoryFlat;
}
It works but I would like to fix TypeScript.
For example the dataset type should not be any
but an array of objects with keys with name mainProperty
, categoryProperty
, valueProperty
.
For example, dataset could also be:
const dataset = [
{ apple: ..., color: ..., something: ... },
...
];
const flattenDataset = flatDataset(dataset, 'apple', 'color', 'something')
How can I do that?
CodePudding user response:
There's a few way to make it
let obj = { apple: 's', sonic: 'a' , etc: 'd'}
function MyFunction<T, K extends keyof T>(obj: T, ...args:K[]): void { }
MyFunction(obj, 'apple', 'sonic', 'etc')
Using Generics with Rest Arguments. I used this post as example to create the rest args. I recommend you create an interfaces for maintein all sorted
CodePudding user response:
I believe this is the right generic types you're looking for.
The main trick is using { [k in Enum]: string }
, which allows dynamically restricting a key of an object to be a part of a union of strings.
Please note you're using functions without their implementations, so it's hard to actually test it.
function flatDataset<Main extends string, Category extends string, Value extends string>(
dataset: ({ [k in Main | Category]: string } & { [k in Value]: number })[],
mainProperty: Main,
categoryProperty: Category,
valueProperty: Value,
): ({ [k in (typeof dataset)[number][Main]]: string }
& { [k in (typeof dataset)[number][Category]]: number })[]