typescript - Angular/RXJS, Bir Observable Nesne Dizisini BELİRLİ Bir String değerine göre nasıl sıralarım?
İlginç bir problemim var (sanırım) ve biraz yardıma bayılırım.
Bir değer temelinde filtrelenmiş bir Observable[DisplayCard[]] döndürüyorum. Bu tamamen iyi çalışıyor. Bu diziyi bir değere göre filtreledikten sonra, şimdi döndürülen diziyi başka bir değerine göre, adı category olan bir değerine göre sıralamak istiyorum. category yalnızca üç String değerinden biri olabilir: ONGOING, UPCOMING ve PAST.
Bu şekilde sıralanacak bir dizi döndürmek istiyorum: DEVAM EDEN --> YAKLAŞAN --> GEÇMİŞ. Yani, "DEVAM EDEN" olan tüm gösterim kartları önde olacak, bunlar bittikten sonra tüm "YAKLAŞAN" kartları ve ardından "GEÇMİŞ" kartlarının gösterilmesini istiyorum.
İşte benim kodum:
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))),
);
Cevap #1
Bunu, tüm olası değerleri temsil eden bir enum oluşturarak CardCategory ve bunların hangi sırayla sıralanması gerektiğini Record<CardCategory, number> kullanarak tanımlayarak başarabilirsiniz:
export enum CardCategory {
OnGoing = 'ONGOING',
Upcoming = 'UPCOMING',
Past = 'PAST',
}
export const CategorySortOrder: Record<CardCategory, number> = {
'ONGOING' : 0,
'UPCOMING' : 1,
'PAST' : 2
}
Sonra bunu kullanarak sıraya koyan bir sıralama fonksiyonu oluştur:
function sortByCardCategory(a: DisplayCard, b: DisplayCard) {
if(a.category === b.category) return 0;
return CategorySortOrder[a.category] > CategorySortOrder[b.category] ? 1 : -1;
}
Artık bunu kolayca şu şekilde sıralayabilirsiniz:
displayCommunityCards$ = this.cardService.displayCards$.pipe(
map(cards => cards
.filter(c => c.type === CardType.CommunityEvent)
.sort(sortByCardCategory)
)
);
İşte çalışan bir StackBlitz demosu.
Record türünü kullanmak tamamen gerekli olmasa da, tüm CardCategory değerleri için bir sıralama düzeni tanımladığınızı garanti eder. Örneğin, yeni bir CardCategory eklediğinizde, typescript CategorySortOrder'un tüm CardCategory değerlerini tanımlamadığı hakkında bir hata verecektir.
Property 'MYNEWCAT' is missing in type
'{
UPCOMING: number;
ONGOING: number;
PAST: number;
}'
but required in type 'Record<CardCategory, number>'. (2741)
Cevap #2
Bunu basitçe üç dizi oluşturarak ve bunları sırayla birleştirerek yapabilirsiniz. Bu, O(n) süresinde yapılabilir.
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]);Yukarıdaki kod parçasında kullanılacak bellek daha fazla olacaktır.
Böyle sıralanacak bir dizi döndürmek istiyorum: DEVAM EDİYOR --> GELECEK --> GEÇMİŞ. Yani, "DEVAM EDİYOR" olan tüm gösterim kartları en önde olacak, bunlar bittikten sonra tüm "GELECEK" kartlarını ve sonra da tüm "GEÇMİŞ" kartlarının görünmesini istiyorum.
Başka ilginç bir yol, bir nesnede "ongoing", "upcoming" ve "past" anahtarlarını oluşturmaktır. Her biri için bir değer olarak bir dizi oluşturabilir ve bir şeyi bulduğunuzda diziyi doldurabilirsiniz. Yaklaşan etkinlikleri ne zaman göstermeye başlayacağınızı ve geçmiş olanları ne zaman başlatacağınızı kontrol etmeniz gerekir.
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);İşiniz bittiğinde, sadece anahtarı alın ve verileri görünüme gönderin.
