Transformarea unei colecții
O transformare ia o colecție, de exemplu un slice sau un map, și construiește din ea o colecție nouă. Colecția originală nu este modificată.
În multe limbaje, listele și map-urile vin cu metode gata făcute, precum map, zip, associate și flatten. Go nu are aceste metode încorporate. În schimb, scrii o buclă for scurtă sau o mică funcție helper pe care o poți refolosi. Acesta este stilul Go și este scurt, odată ce știi tiparul.
Go are generice începând cu versiunea 1.18. O funcție generică lucrează cu orice tip de element. Lecțiile de pe această pagină folosesc generice (Go 1.21 sau mai nou), astfel încât un singur helper merge la fel de bine pentru numere, string-uri și struct-uri. Pachetele standard slices, maps și strings ajută și ele la sarcinile obișnuite.
Sintaxa unui helper Map generic
Aceasta este forma unui helper Map refolosibil. T este tipul elementelor de intrare, iar R este tipul elementelor din rezultat. Funcția f este apelată o dată pentru fiecare element, iar fiecare răspuns este adăugat în noul slice.
func Map[T, R any](items []T, f func(T) R) []R {
result := make([]R, 0, len(items))
for _, v := range items {
result = append(result, f(v))
}
return result
}
Map
Operația map aplică aceeași funcție fiecărui element și adună răspunsurile într-un slice nou, de aceeași lungime. De exemplu, poate transforma [1 2 3] în [3 6 9].
Go nu are o metodă map pentru slice-uri. Scrii o buclă care apelează funcția pentru fiecare element și folosește append ca să adauge rezultatul într-un slice nou. Dacă faci asta des, pune bucla într-o funcție helper generică.
Nu confunda acest lucru cu tipul map din Go, care ține perechi cheie:valoare. Aici „map” înseamnă „aplică o funcție fiecărui element”.
Map cu o funcție helper
Helper-ul Map primește un slice și o funcție. Apelează funcția pe fiecare element și returnează un slice nou. Aici înmulțește fiecare număr cu 3.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
// Map construiește un slice nou aplicând f fiecărui element din items.
func Map[T, R any](items []T, f func(T) R) []R {
result := make([]R, 0, len(items))
for _, v := range items {
result = append(result, f(v))
}
return result
}
func main() {
numbers := []int{1, 2, 3}
tripled := Map(numbers, func(x int) int { return x * 3 })
fmt.Println(tripled)
}
Map cu indexul
Uneori contează și poziția unui element. În Go, range îți dă atât indexul, cât și valoarea, așa că o buclă simplă este de ajuns. Aici fiecare valoare este înmulțită cu propriul index.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
func main() {
numbers := []int{1, 2, 3}
result := make([]int, 0, len(numbers))
// range dă indexul și valoarea fiecărui element
for idx, value := range numbers {
result = append(result, value * idx)
}
fmt.Println(result)
}
Sari peste unele rezultate
Uneori unele rezultate trebuie eliminate. Go nu are valori null pentru numere, așa că funcția dată helper-ului returnează două lucruri: rezultatul și un bool care spune dacă acesta trebuie păstrat. Când bool-ul este false, elementul este sărit.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
// MapNotNil păstrează doar rezultatele pentru care f returnează ok == true.
func MapNotNil[T, R any](items []T, f func(T) (R, bool)) []R {
result := make([]R, 0, len(items))
for _, v := range items {
if r, ok := f(v); ok {
result = append(result, r)
}
}
return result
}
func main() {
numbers := []int{1, 2, 3}
result := MapNotNil(numbers, func(x int) (int, bool) {
if x == 2 {
return 0, false // sare peste acest element
}
return x * 3, true
})
fmt.Println(result)
}
Transformă cheile unui map
Ca să schimbi cheile unui map, creează un map nou și copiază fiecare intrare cu noua cheie. Aici strings.ToUpper transformă fiecare cheie în majuscule. fmt.Println afișează un map cu cheile sortate, așa că rezultatul are mereu aceeași ordine.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strings"
)
func main() {
numbersMap := map[string]int{"key1": 1, "key2": 2}
upperKeys := make(map[string]int, len(numbersMap))
// copiază fiecare intrare, dar schimbă cheia în majuscule
for key, value := range numbersMap {
upperKeys[strings.ToUpper(key)] = value
}
fmt.Println(upperKeys)
}
Transformă valorile unui map
Ca să schimbi valorile, faci la fel, dar păstrezi cheia și calculezi o valoare nouă. Aici fiecare valoare crește cu lungimea cheii sale: 1 + len("key1") este 5.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
func main() {
numbersMap := map[string]int{"key1": 1, "key2": 2}
newValues := make(map[string]int, len(numbersMap))
// păstrează cheia, schimbă valoarea
for key, value := range numbersMap {
newValues[key] = value + len(key)
}
fmt.Println(newValues)
}
Zip
Împerecherea (zip) ia două slice-uri și le împerechează elementele după poziție: primul cu primul, al doilea cu al doilea și așa mai departe. Rezultatul are atâtea perechi câte elemente are slice-ul mai scurt.
Go nu are o funcție zip. Definești un struct mic care ține o pereche, afli lungimea mai mică cu funcția încorporată min și parcurgi cu un index ca să construiești perechile.
Împerechează două slice-uri
Struct-ul Pair ține o culoare și un animal. Bucla merge până la lungimea mai mică și construiește câte un Pair pentru fiecare index. Afișarea unui slice de struct-uri arată fiecare pereche între acolade.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
// Pair ține câte un element din fiecare slice.
type Pair struct {
Color string
Animal string
}
func main() {
colors := []string{"roșu", "maro", "gri"}
animals := []string{"vulpe", "urs", "lup"}
// oprește-te la slice-ul mai scurt
n := min(len(colors), len(animals))
pairs := make([]Pair, 0, n)
for i := 0; i < n; i++ {
pairs = append(pairs, Pair{colors[i], animals[i]})
}
fmt.Println(pairs)
}
Împerechează cu o transformare
Nu ai nevoie întotdeauna de perechile în sine. Aici bucla folosește direct ambele elemente ca să construiască o propoziție. animals[i][:1] este prima literă, scrisă cu majusculă folosind strings.ToUpper, iar animals[i][1:] este restul cuvântului.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strings"
)
func main() {
colors := []string{"roșie", "maro", "gri"}
animals := []string{"vulpea", "ursul", "lupul"}
n := min(len(colors), len(animals))
for i := 0; i < n; i++ {
// scrie cu majusculă doar prima literă a numelui animalului
name := strings.ToUpper(animals[i][:1]) + animals[i][1:]
fmt.Println(name + " este " + colors[i])
}
}
Desfă un slice de perechi
Desfacerea (unzip) este operația inversă: un slice de perechi devine două slice-uri. Bucla citește fiecare pereche și adaugă părțile ei în două slice-uri separate.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
type Pair struct {
Word string
Number int
}
func main() {
numberPairs := []Pair{{"one", 1}, {"two", 2}, {"three", 3}}
words := make([]string, 0, len(numberPairs))
numbers := make([]int, 0, len(numberPairs))
// împarte fiecare pereche în cele două părți ale ei
for _, p := range numberPairs {
words = append(words, p.Word)
numbers = append(numbers, p.Number)
}
fmt.Println(words)
fmt.Println(numbers)
}
Associate
Asocierea construiește un map dintr-un slice. Fiecare element îți dă o cheie și o valoare. Un map nu poate ține aceeași cheie de două ori, așa că, dacă două elemente produc aceeași cheie, câștigă cel de mai târziu.
Go nu are funcții associate. Creează un map gol cu make, apoi parcurge slice-ul și atribuie result[key] = value pentru fiecare element.
Elementele ca chei
Elementul însuși devine cheie, iar valoarea este calculată din el. Aici cuvintele sunt chei, iar lungimile lor sunt valori.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
func main() {
numbers := []string{"one", "two", "three"}
lengths := make(map[string]int, len(numbers))
// fiecare cuvânt devine cheie, lungimea lui devine valoare
for _, word := range numbers {
lengths[word] = len(word)
}
fmt.Println(lengths)
}
Elementele ca valori
Elementul devine valoare, iar cheia este calculată din el. Aici cheia este prima literă scrisă cu majusculă. Și two, și three încep cu T, așa că three îl înlocuiește pe two, iar map-ul are doar două intrări.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strings"
)
func main() {
numbers := []string{"one", "two", "three"}
byFirstLetter := make(map[string]string, len(numbers))
// cheia este prima literă; un cuvânt de mai târziu îl înlocuiește pe unul anterior cu aceeași cheie
for _, word := range numbers {
byFirstLetter[strings.ToUpper(word[:1])] = word
}
fmt.Println(byFirstLetter)
}
Calculează și cheia, și valoarea
Poți calcula din element atât cheia, cât și valoarea. Aici cheia este prima literă, iar valoarea este lungimea cuvântului. Din nou câștigă intrarea T de mai târziu.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strings"
)
func main() {
numbers := []string{"one", "two", "three"}
lengthByLetter := make(map[string]int, len(numbers))
// cheia este prima literă, valoarea este lungimea cuvântului
for _, word := range numbers {
lengthByLetter[strings.ToUpper(word[:1])] = len(word)
}
fmt.Println(lengthByLetter)
}
Cheie și valoare dintr-un singur element
Uneori un singur element conține ambele părți. strings.Fields împarte un nume complet la spații într-un slice de cuvinte. Numele de familie devine cheie, iar prenumele devine valoare.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strings"
)
func main() {
names := []string{"Alice Adams", "Brian Brown"}
byLastName := make(map[string]string, len(names))
for _, fullName := range names {
parts := strings.Fields(fullName) // împarte la spații
byLastName[parts[1]] = parts[0] // nume de familie -> prenume
}
fmt.Println(byLastName)
}
Flatten
Aplatizarea (flatten) transformă o colecție imbricată, de exemplu un slice de slice-uri, într-un singur slice simplu, cu toate elementele în ordine.
Go nu are o metodă flatten, dar append poate adăuga un slice întreg dintr-o dată: append(result, inner...). Cele trei puncte împrăștie toate elementele din inner în apel.
Aplatizează un slice de slice-uri
Bucla trece prin fiecare slice interior și adaugă toate elementele lui în result. Două slice-uri interioare de câte trei numere devin un singur slice cu șase numere.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
func main() {
numberSets := [][]int{{1, 2, 3}, {4, 5, 6}}
result := []int{}
// cele trei puncte împrăștie toate elementele slice-ului interior în append
for _, inner := range numberSets {
result = append(result, inner...)
}
fmt.Println(result)
}
Aplatizează valorile din struct-uri
Un flat map ia mai întâi câte un slice din fiecare element, apoi le unește pe toate. În Go citești câmpul care te interesează, c.Values, și îl adaugi cu cele trei puncte. Toate valorile din cele trei containere ajung într-un singur slice.
Ieșire: program Go și rezultatul capturat
package main
import ("fmt")
type Container struct {
Values []string
}
func main() {
containers := []Container{
{Values: []string{"one", "two", "three"}},
{Values: []string{"four", "five"}},
{Values: []string{"six", "șapte", "opt"}},
}
result := []string{}
// ia valorile din Values ale fiecărui container și adaugă-le pe toate într-un singur slice
for _, c := range containers {
result = append(result, c.Values...)
}
fmt.Println(result)
}
Reprezentarea ca string
Deseori vrei să arăți o colecție ca text lizibil, de exemplu cu virgule între elemente.
În Go, strings.Join primește un slice de string-uri și un separator și returnează un singur string. Restul — un prefix, un postfix, o limită sau o transformare — se face cu simplă concatenare de string-uri sau cu o buclă scurtă înainte de unire. Numerele trebuie transformate mai întâi în string-uri, de exemplu cu strconv.Itoa.
Unește cu un separator
strings.Join pune ", " între cuvinte și returnează un singur string.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strings"
)
func main() {
numbers := []string{"one", "two", "three"}
fmt.Println(strings.Join(numbers, ", "))
}
Adaugă un prefix și un postfix
Un prefix și un postfix sunt doar text în jurul rezultatului unit. În Go le adaugi cu +.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strings"
)
func main() {
numbers := []string{"one", "two", "three"}
// adaugă prefixul și postfixul în jurul textului unit
text := "început: " + strings.Join(numbers, " | ") + ": sfârșit"
fmt.Println(text)
}
Limitează numărul de elemente
Pentru un slice lung poate vrei să arăți doar primele câteva elemente. Ia un slice cu primele 10 folosind numbers[:10], transformă fiecare număr în string cu strconv.Itoa, apoi adaugă "...>" la final ca să arăți că restul elementelor au fost tăiate.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strconv"
"strings"
)
func main() {
numbers := make([]int, 0, 100)
for i := 1; i <= 100; i++ {
numbers = append(numbers, i)
}
// ia doar primele 10 numere, ca string-uri
limit := 10
parts := make([]string, 0, limit)
for _, n := range numbers[:limit] {
parts = append(parts, strconv.Itoa(n))
}
if len(numbers) > limit {
parts = append(parts, "<...>")
}
fmt.Println(strings.Join(parts, ", "))
}
Transformă fiecare element înainte de unire
Ca să schimbi fiecare element înainte de unire, construiește într-o buclă un slice nou de string-uri, apoi apelează strings.Join pe el. Aici fiecare cuvânt devine "Element: " plus cuvântul scris cu majuscule.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strings"
)
func main() {
numbers := []string{"one", "two", "three"}
parts := make([]string, 0, len(numbers))
// schimbă mai întâi fiecare cuvânt, apoi unește rezultatele
for _, word := range numbers {
parts = append(parts, "Element: " + strings.ToUpper(word))
}
fmt.Println(strings.Join(parts, ", "))
}