I have an enum with two keys. I would like to filter my data based on the keys of the enum and return an object.
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)
Expectyed Result:
{ "Current": [ { "name": "A", "Archive_Status": 0 }, { "name": "B", "Archive_Status": 0 } ], "Archive": [ { "name": "C", "Archive_Status": 1 }, { "name": "D", "Archive_Status": 1 } ] }
I am able to achieve it this way. But I want to dynamically create new properties from the enum and filter the data based on the Archive_Status and add it to this object. For example, if the enum has 10 properties, I do not want to type in the key names every time into the object. Please advice.
If you want to dynamically create all keys in an object according to an enum, you need to get the enum keys. There is no built in way to do that - the problem is that TypeScript generates a double binding between keys and values to ensure the uniqueness of both.
enum ArchiveStatus {
Current = 0,
Archive = 1,
}
effectively compiles to:
ArchiveStatus = {
"Current": 0,
"Archive": 1,
0: "Current",
1: "Archive",
}
However, if the enum values are all numbers, you can filter them out with Object.values(ArchiveStatus).filter(x => typeof x !== "number")
which will leave you with the names only:
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 demonstration:
const ArchiveStatus = {
"Current": 0,
"Archive": 1,
0: "Current",
1: "Archive",
}
const enumNames = Object.values(ArchiveStatus)
.filter(x => typeof x !== "number");
console.log(enumNames);
Now the filter calls can be generalised to call them with any of the names of the enum. That is what we want to be doing because we already have the names.
This is easily doable using a curried function. Here is your code refactored to use it:
//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
With the tools above it's a matter of putting it all together. We can iterate over all of the enumNames
and create an object with
JavaScript provides a convenience method for creating objects called Object.fromEntries
- it takes an array of pairs. For TypeScript it's an array of tuples of the type [key: string, value: any]
. We can create such a structure and then give it to Object.fromEntries
to generate the objects for us:
//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
If needed, we can skip any keys where the value has no data. We can just remove pairs where the second item is an empty array:
//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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With