Gruparea unei colecții
Gruparea înseamnă sortarea elementelor în găleți. Alegi o cheie pentru fiecare element, iar toate elementele cu aceeași cheie ajung în aceeași găleată.
Cheia este calculată de o funcție mică ce primește un element și returnează cheia lui. De exemplu, prima literă a unui cuvânt.
Map.groupBy() face toată treaba. Îi dai un tablou și o funcție de cheie, iar el returnează un Map de la cheie la un tablou cu elementele care au acea cheie.
Un Map își păstrează cheile în ordinea în care au fost adăugate prima dată, așa că rezultatul afișat arată mereu la fel. console.log() afișează un Map astfel: Map(1) { 'a' => [ 1 ] } — mai întâi numărul de chei, apoi fiecare cheie și valoarea ei. Când o linie ar deveni prea lungă, cheile sunt afișate câte una pe linie.
Există și Object.groupBy(), care returnează un obiect simplu în loc de un Map. Exemplele de aici folosesc Map.groupBy(), pentru că un Map acceptă chei de orice tip și afișează câte chei are.
Gruparea după cheie
Cea mai simplă grupare păstrează elementele așa cum sunt și doar decide în ce găleată ajunge fiecare.
Dă tabloul și o funcție de cheie lui Map.groupBy(). Rezultatul este un Map de la cheie la tabloul elementelor cu acea cheie. Elementele dintr-un grup păstrează ordinea pe care o aveau în datele de intrare.
Uneori vrei să schimbi elementele în timpul grupării, de exemplu să le stochezi cu majuscule. Map.groupBy() nu are niciun parametru pentru asta. Scrie tu bucla: calculează cheia, calculează noua valoare și adaugă noua valoare în grup. groups.get(key) ?? [] dă grupul care există deja sau un tablou nou, gol, pentru o cheie pe care nu ai mai întâlnit-o.
Gruparea cuvintelor după prima literă
Funcția de cheie ia prima literă a cuvântului cu w[0] și o transformă în majusculă cu toUpperCase(). Cuvintele care încep cu aceeași literă ajung în același tablou. Cheile se afișează în ordinea în care au fost întâlnite prima dată: 'O', 'T', 'F'.
Ieșire: program TypeScript și rezultatul capturat
const words: string[] = ["one", "two", "three", "four", "five"]
// Map.groupBy() apelează funcția de cheie pentru fiecare cuvânt și adună grupurile.
const byFirstLetter = Map.groupBy(words, w => w[0].toUpperCase())
console.log(byFirstLetter)
Gruparea cu schimbarea valorilor
Cheia este prima literă scrisă cu literă mică. Valoarea stocată este cuvântul întreg scris cu majuscule. O buclă for...of face ambii pași și adaugă noua valoare în tabloul obținut din groups.get(key) ?? [].
Ieșire: program TypeScript și rezultatul capturat
const words: string[] = ["one", "two", "three", "four", "five"]
// Map.groupBy() nu poate schimba valorile, așa că Map-ul se construiește cu o buclă.
const groups = new Map<string, string[]>()
for (const w of words) {
const key = w[0] // prima literă, scrisă cu literă mică
const value = w.toUpperCase() // valoarea stocată în grup
const group = groups.get(key) ?? []
group.push(value)
groups.set(key, group)
}
console.log(groups)
Numărarea fiecărui grup
Adesea nu ai nevoie de elementele fiecărui grup, ci doar de câte sunt.
Nu există eachCount(). Folosește în schimb un Map<string, number>. Citirea unei chei care nu se află în Map dă undefined, nu 0, așa că scrie (counts.get(key) ?? 0) + 1. Operatorul ?? furnizează valoarea de pornire 0 pentru primul element al unui grup.
Numărarea cuvintelor după prima literă
Fiecare cuvânt adaugă 1 la numărătoarea primei sale litere. Map-ul ajunge să aibă câte un număr pentru fiecare literă: 'o' => 1, 't' => 2 și 'f' => 2.
Ieșire: program TypeScript și rezultatul capturat
const words: string[] = ["one", "two", "three", "four", "five"]
// get() dă undefined pentru o cheie nouă, așa că ?? 0 pornește numărătoarea de la 0.
const counts = new Map<string, number>()
for (const w of words) {
const key = w[0]
counts.set(key, (counts.get(key) ?? 0) + 1)
}
console.log(counts)
Plierea și reducerea pe grup
Plierea unui grup înseamnă combinarea elementelor lui într-o singură valoare, pas cu pas. Un total curent este exemplul clasic: pornești de la 0 și aduni fiecare element.
reduce() face exact acest lucru. El poartă o singură valoare — acumulatorul — prin tot tabloul. Aici acumulatorul este un Map care păstrează valoarea curentă a fiecărui grup. Pentru fiecare element calculezi cheia lui și actualizezi valoarea stocată sub acea cheie.
Când valoarea curentă are o valoare de pornire, cum este 0 pentru o sumă, ?? 0 o furnizează pentru o cheie care nu există încă. Când chiar primul element trebuie să înceapă valoarea, compară rezultatul lui get() cu undefined pentru a deosebi cele două cazuri.
Suma fiecărui grup
Fiecare număr este pus în grupul "even" sau "odd" și adunat la suma acelui grup cu acc.set(key, (acc.get(key) ?? 0) + n). O cheie nouă pornește de la 0, așa că nu este nevoie de niciun caz special. Primul număr este 3, deci 'odd' este prima cheie a Map-ului.
Ieșire: program TypeScript și rezultatul capturat
const numbers: number[] = [3, 4, 5, 6, 7, 8]
// reduce() pliază tabloul într-un singur Map de sume curente.
const sums = numbers.reduce((acc, n) => {
const key = n % 2 === 0 ? "even" : "odd"
return acc.set(key, (acc.get(key) ?? 0) + n)
}, new Map<string, number>())
console.log(sums)
Construirea unui șir de caractere pentru fiecare grup
Primul număr al unui grup începe șirul de caractere. Fiecare număr următor este unit cu "+". acc.get(key) dă undefined pentru un grup care este încă gol, iar asta deosebește cele două cazuri. String(n) transformă un număr în text.
Ieșire: program TypeScript și rezultatul capturat
const numbers: number[] = [3, 4, 5, 6, 7, 8]
const chains = numbers.reduce((acc, n) => {
const key = n % 2 === 0 ? "even" : "odd"
const current = acc.get(key)
// undefined înseamnă că acesta este primul număr al grupului.
return acc.set(key, current === undefined ? String(n) : current + "+" + n)
}, new Map<string, string>())
console.log(chains)