【问题标题】:filter data based on enum values - Typescript根据枚举值过滤数据 - 打字稿
【发布时间】:2020-10-03 01:33:24
【问题描述】:

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

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 个属性,我不想每次都在对象中输入键名。请指教。

【问题讨论】:

  • 是的,您可以这样做。枚举是一个运行时对象。试试console.log(ArchiveStatus) 看看它是什么样子的。您可以像使用任何其他对象一样使用它(例如使用 Object.entires 等)。

标签: typescript enums


【解决方案1】:

获取枚举名称

如果要根据枚举动态创建对象中的所有键,则需要get the enum keys。没有内置方法可以做到这一点 - 问题是 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

Playground Link

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))
}

Playground Link

使用状态和过滤数据生成一个对象

使用上面的工具,只需将它们放在一起即可。我们可以遍历所有的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);

Playground Link

过滤掉没有任何数据的键

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

//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);

Playground Link

【讨论】:

  • 只是一个小问题:对于这个数据,[{name:'A', Archive_Status: 0}, {name: 'B', Archive_Status: 0}],没有 Archive_Status 的值为1. 所以最终结果应该只包含 Current,而 Archive 根本不应该存在。在您提供的解决方案中,响应包含 Archive 作为空数组。我们可以解决它吗?
  • @a2441918 是的,这是可能的。我们可以过滤掉任何我们不需要的对。我会更新答案。
  • 谢谢。如果过滤后的数组为空,则我们根本不需要该键。谢谢
猜你喜欢
  • 2020-12-30
  • 2020-04-30
  • 1970-01-01
  • 2019-09-25
  • 2022-01-22
  • 2019-09-26
  • 2019-03-04
  • 1970-01-01
相关资源
最近更新 更多