I have an interesting problem (I think) and would love some help.
I am returning an Observable[DisplayCard[]] that is filtered based on a value. That all works great. After I filter that array based on a value, I NOW want to sort the returned array by another one of it's values called category
. category
can only be one of three String values: ONGOING
, UPCOMING
, and PAST
.
I want to return an array that will sort like this: ONGOING --> UPCOMING --> PAST. So, all the display cards that have "ONGOING" will be in front, after there is no more of those, I want all the "UPCOMING" cards and then after those, I want all the "PAST" cards to show.
Here is my code:
displayCommunityCards$ = this.cardService.displayCards$
.pipe(
map(communityEventsCards => communityEventsCards.filter(community => community.type === 'COMMUNITY EVENT')
.sort((a, b) => {
return ???
}),
tap(data => console.log('Community Cards: ', JSON.stringify(data))),
);
You can achieve this by creating an enum to represent all possible values for CardCategory
and defining the order they should be sorted in using Record<CardCategory, number>
:
export enum CardCategory {
OnGoing = 'ONGOING',
Upcoming = 'UPCOMING',
Past = 'PAST',
}
export const CategorySortOrder: Record<CardCategory, number> = {
'ONGOING' : 0,
'UPCOMING' : 1,
'PAST' : 2
}
Then create a sort function that uses that to sort:
function sortByCardCategory(a: DisplayCard, b: DisplayCard) {
if(a.category === b.category) return 0;
return CategorySortOrder[a.category] > CategorySortOrder[b.category] ? 1 : -1;
}
You can now easily sort like this:
displayCommunityCards$ = this.cardService.displayCards$.pipe(
map(cards => cards
.filter(c => c.type === CardType.CommunityEvent)
.sort(sortByCardCategory)
)
);
Here's a working StackBlitz demo.
Using the Record
type isn't completely necessary, but it does guarantee that you define a sort order for all values of CardCategory
. For example, if you added a new CardCategory
, typescript will throw an error about CategorySortOrder
not defining all values of CardCategory
.
Property 'MYNEWCAT' is missing in type
'{
UPCOMING: number;
ONGOING: number;
PAST: number;
}'
but required in type 'Record<CardCategory, number>'. (2741)
You can simply do this by creating three array and merge them in order. It can be done in O(n).
const upcoming = [],
past = [],
ongoing = [];
const events = [{event: "upcoming", id: 1}, {event: "upcoming", id: 2}, {event: "past", id: 3}, {event: "past", id: 4}, {event: "ongoing", id: 5}];
events.forEach(el => {
el.event === "upcoming" ? upcoming.push(el) :
el.event === "past" ? past.push(el) :
el.event === "ongoing" ? ongoing.push(el) : false;
});
console.log([...ongoing, ...upcoming, ...past]);
In the above snippet the memory used will be more.
I want to return an array that will sort like this: ONGOING --> UPCOMING --> PAST. So, all the display cards that have "ONGOING" will be in front, after there is no more of those, I want all the "UPCOMING" cards and then after those, I want all the "PAST" cards to show.
Another interesting way could be creating keys in an object with "ongoing", "upcoming" and "past". You can create an array as value for each and push things once you find it. You need to check when to start show the upcoming events and when to start past.
const result = {
upcoming: [],
past: [],
ongoing: []
}
const events = [{event: "upcoming", id: 1}, {event: "upcoming", id: 2}, {event: "past", id: 3}, {event: "past", id: 4}, {event: "ongoing", id: 5}];
events.forEach(el => {
el.event === "upcoming" ? result.upcoming.push(el) :
el.event === "past" ? result.past.push(el) :
el.event === "ongoing" ? result.ongoing.push(el) : false;
});
console.log(result);
When you are done, just take the key and throw the data to the view.
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