Filtrarea unei colecții
Filtrarea înseamnă alegerea elementelor dintr-o colecție care îndeplinesc o condiție.
Condiția se scrie ca o funcție care primește un element și returnează true (îl păstrăm) sau false (îl sărim). O astfel de funcție se numește predicat.
Filtrarea construiește un slice nou. Slice-ul original nu este modificat.
Go nu are o metodă filter încorporată pentru slice-uri. Filtrezi cu o buclă for și un if, sau scrii o singură dată un mic helper generic și îl refolosești. Helper-ul de mai jos funcționează pentru un slice de orice tip.
Biblioteca standard are slices.DeleteFunc(), care elimină elementele ce îndeplinesc o condiție. Ai grijă: aceasta modifică slice-ul original în loc să creeze unul nou.
Sintaxa helper-ului generic Filter
T reprezintă tipul elementelor. keep este predicatul: elementele pentru care returnează true sunt copiate în result.
func Filter[T any](items []T, keep func(T) bool) []T {
var result []T
for _, v := range items {
if keep(v) {
result = append(result, v)
}
}
return result
}
Filtrare cu un predicat
Cel mai simplu mod de a filtra este să pasezi un predicat helper-ului Filter. Helper-ul parcurge slice-ul și păstrează fiecare element care trece testul.
Map-urile nu au un helper propriu. Ca să filtrezi un map, parcurge-l cu range și copiază perechile cheie:valoare potrivite într-un map nou.
Dacă în condiție ai nevoie și de poziția elementului, folosește indexul dat de range împreună cu valoarea.
Ca să păstrezi elementele care nu îndeplinesc condiția, pune ! în fața condiției. Go nu are un filterNot separat.
Un slice de tip []any poate conține valori de tipuri diferite. O aserțiune de tip precum v.(string) verifică dacă o valoare este un string și o dă înapoi cu acel tip. Un pointer poate fi nil, ceea ce înseamnă „nicio valoare”; compară cu nil ca să sari peste astfel de intrări.
Filtrarea unui slice
Păstrează doar cuvintele mai lungi de 3 litere. Condiția len(s) > 3 este pasată lui Filter ca funcție.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
// Filter returnează un slice nou cu doar elementele pentru care keep() spune da.
func Filter[T any](items []T, keep func(T) bool) []T {
var result []T
for _, v := range items {
if keep(v) {
result = append(result, v)
}
}
return result
}
func main() {
words := []string{"one", "two", "three", "four"}
longerThan3 := Filter(words, func(s string) bool { return len(s) > 3 })
fmt.Println(longerThan3)
}
Filtrarea unui map
Parcurge map-ul și păstrează perechile a căror cheie se termină cu "1" și a căror valoare este mai mare decât 10. strings.HasSuffix() verifică sfârșitul unui string.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strings"
)
func main() {
numbersMap := map[string]int{"key1": 1, "key2": 2, "key3": 3, "key11": 11}
// Un map nu are un filtru încorporat, deci parcurge-l și copiază perechile potrivite într-un map nou.
filteredMap := map[string]int{}
for key, value := range numbersMap {
if strings.HasSuffix(key, "1") && value > 10 {
filteredMap[key] = value
}
}
fmt.Println(filteredMap)
}
Filtrare folosind indexul
range dă indexul și valoarea. Păstrează un cuvânt doar când indexul lui nu este 0 și are mai puțin de 5 litere.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
func main() {
numbers := []string{"one", "two", "three", "four"}
// range dă atât indexul, cât și valoarea, deci condiția le poate folosi pe amândouă.
var filteredIdx []string
for index, s := range numbers {
if index != 0 && len(s) < 5 {
filteredIdx = append(filteredIdx, s)
}
}
fmt.Println(filteredIdx)
}
Filtrare cu o condiție negată
Elimină cuvintele cu 3 litere sau mai puține. Condiția len(s) = 3 este negată cu !, așa că rămân doar cuvintele mai lungi.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
func Filter[T any](items []T, keep func(T) bool) []T {
var result []T
for _, v := range items {
if keep(v) {
result = append(result, v)
}
}
return result
}
func main() {
numbers := []string{"one", "two", "three", "four"}
// Go nu are filterNot: neagă condiția cu ! și folosește Filter.
filteredNot := Filter(numbers, func(s string) bool { return !(len(s) <= 3) })
fmt.Println(filteredNot)
}
Filtrare după tip
Slice-ul conține nil, un număr, două string-uri și un float. Aserțiunea de tip v.(string) împreună cu valoarea ok găsește string-urile. Fiecare este afișat cu majuscule.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strings"
)
func main() {
numbers := []any{nil, 1, "two", 3.0, "four"}
// v.(string) întreabă: este această valoare un string? ok este true doar când este.
for _, v := range numbers {
if s, ok := v.(string); ok {
fmt.Println(strings.ToUpper(s))
}
}
}
Eliminarea valorilor nil
Slice-ul conține pointeri către string-uri, iar unii dintre ei sunt nil. Păstrează pointerii care nu sunt nil și adună valorile către care arată cu *p.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
func main() {
one, two := "one", "two"
// Un pointer poate fi nil, ceea ce înseamnă „nicio valoare”.
nullableList := []*string{nil, &one, nil, &two}
var values []string
for _, p := range nullableList {
if p != nil {
values = append(values, *p)
}
}
fmt.Println(values)
}
Partiționare
Uneori vrei ambele grupuri: elementele care îndeplinesc condiția și cele care nu o îndeplinesc.
Go nu are un partition încorporat. Un mic helper generic face asta: parcurge o singură dată și adaugă fiecare element fie în slice-ul match, fie în slice-ul rest. Helper-ul returnează ambele slice-uri, pentru că o funcție Go poate returna mai multe valori.
Împărțirea unui slice în două
Cuvintele mai lungi de 3 litere ajung în match; toate celelalte ajung în rest. Ambele slice-uri sunt afișate.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
// Partition împarte items în două slice-uri: cele care se potrivesc și restul.
func Partition[T any](items []T, test func(T) bool) ([]T, []T) {
var match, rest []T
for _, v := range items {
if test(v) {
match = append(match, v)
} else {
rest = append(rest, v)
}
}
return match, rest
}
func main() {
numbers := []string{"one", "two", "three", "four"}
match, rest := Partition(numbers, func(s string) bool { return len(s) > 3 })
fmt.Println(match)
fmt.Println(rest)
}
Predicate de verificare
Uneori nu ai nevoie de elementele potrivite, ci doar de un răspuns da sau nu.
- any: trece cel puțin un element testul?
slices.ContainsFunc()din Go răspunde la această întrebare. - none: nu trece niciun element testul? Pune
!în fața luislices.ContainsFunc(). - all: trece fiecare element testul? Go nu are o funcție pentru asta, așa că folosește un mic helper generic
All.
Fără o condiție, „există vreun element?” înseamnă len(s) > 0, iar „nu există niciun element?” înseamnă len(s) == 0.
Pe un slice gol, All returnează true. Nu există niciun element care să cadă testul, deci testul nu este încălcat niciodată. Aceasta se numește adevăr vid.
any
Verifică dacă cel puțin un cuvânt se termină cu "e", apoi verifică dacă slice-ul are vreun element.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"slices"
"strings"
)
func main() {
numbers := []string{"one", "two", "three", "four"}
// slices.ContainsFunc este true dacă cel puțin un element trece testul.
fmt.Println(slices.ContainsFunc(numbers, func(s string) bool { return strings.HasSuffix(s, "e") }))
// „există vreun element?” este doar o verificare a lungimii.
fmt.Println(len(numbers) > 0)
}
none
Verifică faptul că niciun cuvânt nu se termină cu "a", apoi verifică dacă slice-ul este gol.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"slices"
"strings"
)
func main() {
numbers := []string{"one", "two", "three", "four"}
// „none” este opusul lui „any”: niciun element nu trece testul.
fmt.Println(!slices.ContainsFunc(numbers, func(s string) bool { return strings.HasSuffix(s, "a") }))
// „nu există niciun element?” este doar o verificare a lungimii.
fmt.Println(len(numbers) == 0)
}
all
Verifică dacă fiecare cuvânt se termină cu "e". Apoi apelează All pe un slice gol, ceea ce dă true.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"strings"
)
// All este true doar când fiecare element trece testul.
func All[T any](items []T, test func(T) bool) bool {
for _, v := range items {
if !test(v) {
return false
}
}
return true
}
func main() {
numbers := []string{"one", "two", "three", "four"}
empty := []string{}
fmt.Println(All(numbers, func(s string) bool { return strings.HasSuffix(s, "e") }))
// Un slice gol nu are niciun element care să cadă testul, deci All este true.
fmt.Println(All(empty, func(s string) bool { return strings.HasSuffix(s, "e") }))
}