我有这个数据集:
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 }
];
我需要创建一个堆叠条形图。为此,我使用了d3 stack()
函数。我需要的结果是:
const stackedDataset = [
{ date: "2022-01-01", category: "red", value: 10, start: 0, end: 10 },
{ date: "2022-01-02", category: "red", value: 5, start: 0, end: 5 },
{ date: "2022-01-01", category: "blue", value: 20, start: 10, end: 30 },
{ date: "2022-01-02", category: "blue", value: 15, start: 5, end: 20 },
{ date: "2022-01-01", category: "gold", value: 30, start: 30, end: 60 },
{ date: "2022-01-02", category: "gold", value: 25, start: 20, end: 45 },
{ date: "2022-01-01", category: "green", value: 40, start: 60, end: 100 },
{ date: "2022-01-02", category: "green", value: 35, start: 45, end: 80 }
]
因此,相同的数据,但start
和end
属性由d3计算。
我创建了一个函数,它接受输入dataset
并返回stackedDataset
:
export function getStackedSeries(dataset: Datum[]) {
const categories = uniq(dataset.map((d) => d[CATEGORY])) as string[];
const datasetGroupedByDateFlat = flatDataset(dataset);
const stackGenerator = d3.stack().keys(categories);
const seriesRaw = stackGenerator(
datasetGroupedByDateFlat as Array<Dictionary<number>>
);
const series = seriesRaw.flatMap((serie, si) => {
const category = categories[si];
const result = serie.map((s, sj) => {
return {
[DATE]: datasetGroupedByDateFlat[sj][DATE] as string,
[CATEGORY]: category,
[VALUE]: datasetGroupedByDateFlat[sj][category] as number,
start: s[0] || 0,
end: s[1] || 0
};
});
return result;
});
return series;
}
export function flatDataset(
dataset: Datum[]
): Array<Dictionary<string | number>> {
if (dataset.length === 0 || !DATE) {
return (dataset as unknown) as Array<Dictionary<string | number>>;
}
const columnToBeFlatValues = uniqBy(dataset, CATEGORY).map(
(d) => d[CATEGORY]
);
const datasetGroupedByDate = groupBy(dataset, DATE);
const datasetGroupedByMainCategoryFlat = Object.entries(
datasetGroupedByDate
).map(([date, datasetForDate]) => {
const categoriesObject = columnToBeFlatValues.reduce((acc, value) => {
const datum = datasetForDate.find(
(d) => d[DATE] === date && d[CATEGORY] === value
);
acc[value] = datum?.[VALUE];
return acc;
}, {} as Dictionary<string | number | undefined>);
return {
[DATE]: date,
...categoriesObject
};
});
return datasetGroupedByMainCategoryFlat as Array<Dictionary<string | number>>;
}
正如你所看到的,这些函数是Datum
类型特有的。有没有办法修改它们,使它们适用于至少有三个字段date, category, value
的泛型类型T
?
我的意思是,我想有这样的东西:
interface StackedStartEnd {
start: number
end: number
}
function getStackedSeries<T>(dataset: T[]): T extends StackedStartEnd
显然,这段代码应该被重构以使其更通用:
{
[DATE]: ...,
[CATEGORY]: ...,
[VALUE]: ...,
start: ...,
end: ...,
}
Here工作代码。
我不是一个TypeScriptMaven,所以我需要一些帮助。老实说,我试图做的是修改函数签名,但我失败了,无论如何,我想使函数尽可能通用,我不知道如何开始。我需要传递给函数也使用列名?
非常感谢您的光临
2条答案
按热度按时间jrcvhitl1#
我尝试了一个更通用的方法,因为你建议混合两个函数。默认情况下,似乎你的
getStackedSeries
函数不需要知道date
和category
属性,你可以使用泛型类型来确保只有value
属性,因为我们需要知道它来计算start
和end
值。完整的实现可通过here on codesandbox查看。
getStackedSeries()
现在接收扩展Datum
类型的data
属性,该属性为:有了这个属性和第二个属性
groupByProperty
,我们就可以定义groupBy子句并返回所有由flatMap
表示的flatten。您可能注意到返回类型现在是由typescript通过使用泛型
<T>
动态定义的。您也可以键入函数的这一部分,但让编译器为您工作并根据输入自动生成类型是有意义的。
xwbd5t1u2#
您可以按开始/结束日期进行分组,然后按类别对结果集进行另一次分组。
第一个