首页 > 解决方案 > 根据枚举值过滤数据 - 打字稿

问题描述

我有一个带有两个键的枚举。我想根据枚举的键过滤我的数据并返回一个对象。

enum ArchiveStatus {
  Current = 0,
  Archive = 1,
}

const data = [{name:'A', Archive_Status: 0}, {name: 'B', Archive_Status: 0}, {name: 'C', Archive_Status: 1}, {name: 'D', Archive_Status: 1}];

const filteredData = {
    Current: data.filter(item => item.Archive_Status === ArchiveStatus.Current),
    Archive: data.filter(item => item.Archive_Status === ArchiveStatus.Archive)
}

console.log(filteredData)

预期结果:

{ "Current": [ { "name": "A", "Archive_Status": 0 }, { "name": "B", "Archive_Status": 0 } ], "Archive": [ { "name": "C", "Archive_Status": 1 }, { "name": "D", "Archive_Status": 1 } ] }

我能够以这种方式实现它。但我想从枚举动态创建新属性并根据 Archive_Status 过滤数据并将其添加到此对象。例如,如果枚举有 10 个属性,我不想每次都在对象中输入键名。请指教。

标签: typescriptenums

解决方案


获取枚举名称

如果要根据枚举动态创建对象中的所有键,则需要获取枚举键。没有内置的方法可以做到这一点 - 问题是 TypeScript 在键和值之间生成双重绑定以确保两者的唯一性。

enum ArchiveStatus {
  Current = 0,
  Archive = 1,
}

有效地编译为:

ArchiveStatus = {
  "Current": 0,
  "Archive": 1,
  0: "Current",
  1: "Archive",
}

但是,如果枚举值都是数字,您可以将它们过滤掉,Object.values(ArchiveStatus).filter(x => typeof x !== "number")只留下名称:

enum ArchiveStatus {
  Current = 0,
  Archive =  1
}

//create a type for the names for type safety later
type ArchiveStatusName = keyof typeof ArchiveStatus;

const enumNames = Object.values(ArchiveStatus)
    .filter(x => typeof x !== "number") as ArchiveStatusName[]; //type assertion needed as compiler cannot determine this is safe

游乐场链接

JavaScript 演示:

const ArchiveStatus = {
  "Current": 0,
  "Archive": 1,
  0: "Current",
  1: "Archive",
}

const enumNames = Object.values(ArchiveStatus)
  .filter(x => typeof x !== "number");

console.log(enumNames);

泛化过滤

现在过滤器调用可以概括为使用枚举的任何名称来调用它们。这就是我们想要做的,因为我们已经有了名字。

使用柯里化函数很容易做到这一点。这是重构以使用它的代码:

//formalised the interface for the data
interface DataItem {
    name: string;
    Archive_Status: ArchiveStatus;
}

//generalised filter function for each status
const statusFilter = (status: ArchiveStatus) => (item: DataItem) : boolean => 
    item.Archive_Status === status;

//using the filters
const filteredData = {
    Current: data.filter(statusFilter(ArchiveStatus.Current)),
    Archive: data.filter(statusFilter(ArchiveStatus.Archive))
}

游乐场链接

生成具有状态和过滤数据的对象

使用上面的工具,只需将它们放在一起即可。我们可以遍历所有的enumNames并创建一个对象

  • 每个名字作为一个键
  • 以该状态作为值过滤的数据

JavaScript 提供了一种创建对象的便捷方法,称为Object.fromEntries- 它采用一对数组。对于 TypeScript,它是一个类型为 的元组数组[key: string, value: any]。我们可以创建这样一个结构,然后将其Object.fromEntries用于为我们生成对象:

//create key-value pairs for the dynamically created object
const pairs = enumNames.map(name => [
    name, 
    data.filter(statusFilter(ArchiveStatus[name]))
]);

const filteredData = Object.fromEntries(pairs);

游乐场链接

过滤掉没有任何数据的键

如果需要,我们可以跳过值没有数据的任何键。我们可以删除第二项是空数组的对:

//create key-value pairs for the dynamically created object
const pairs = enumNames.map(name => [
    name, 
    data.filter(statusFilter(ArchiveStatus[name]))
])
  .filter(([, value]) => value.length > 0); // remove any pair where the value is an empty array

const filteredData = Object.fromEntries(pairs);

游乐场链接


推荐阅读