Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

filter data based on enum values - Typescript

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.

like image 577
a2441918 Avatar asked Sep 01 '25 20:09

a2441918


1 Answers

Get the enum names

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

Generalise the filtering

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

Generate an object with the statuses and filtered data

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

  • each name as a key
  • the data filtered with that status as the value

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

Filter out keys without any data

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

like image 122
VLAZ Avatar answered Sep 07 '25 18:09

VLAZ