2016-06-01 13 views
12

Ho una matrice di stringhe e vorrei escludere valori che iniziano con foo_ O sono più lunghi di 7 caratteri.Il modo più idiomatico per selezionare elementi da una matrice in Golang?

È possibile scorrere ciclicamente ciascun elemento, eseguire la dichiarazione if e aggiungerla a una sezione lungo il percorso. Ma ero curioso di sapere se c'era un modo idiomatico o più golan di realizzarlo.

solo per esempio, la stessa cosa potrebbe essere fatto in Ruby come

my_array.select! { |val| val !~ /^foo_/ && val.length <= 7 } 

risposta

15

Non v'è alcun one-liner, come lo avete in Ruby, ma con una funzione di supporto si può fare quasi altrettanto breve.

Ecco la nostra funzione di supporto che loop su una fetta, e seleziona e restituisce solo gli elementi che soddisfano un criterio catturato da un valore di funzione:

func choose(ss []string, test func(string) bool) (ret []string) { 
    for _, s := range ss { 
     if test(s) { 
      ret = append(ret, s) 
     } 
    } 
    return 
} 

Usando questa funzione di supporto il vostro compito:

ss := []string{"foo_1", "asdf", "loooooooong", "nfoo_1", "foo_2"} 

mytest := func(s string) bool { return !strings.HasPrefix(s, "foo_") && len(s) <= 7 } 
s2 := choose(ss, mytest) 

fmt.Println(s2) 

uscita (provate sul Go Playground):

[asdf nfoo_1] 

Nota:

Se si prevede che saranno selezionati molti elementi, potrebbe essere vantaggioso per assegnare un "grande" fetta ret in anticipo, e utilizzare una semplice assegnazione al posto del append(). E prima di tornare, suddividere lo ret per avere una lunghezza uguale al numero di elementi selezionati.

Nota # 2:

Nel mio esempio ho scelto una funzione test() che indica se un elemento deve essere restituito. Quindi ho dovuto invertire la tua condizione di "esclusione". Ovviamente puoi scrivere la funzione di supporto per aspettarti una funzione tester che indichi cosa escludere (e non cosa includere).

+0

Grazie per la spiegazione! Questo è un bel modo per farlo – user2490003

+1

Upvote per l'uso di 'choose' invece di' filter', molto altro Go idiomatico per "scegliere" cosa si vuole restituire rispetto a "filter". – Verran

+4

@Verran "scegli", per me, implica solo un risultato (forse a caso). Il "filtro", tuttavia, viene utilizzato in tutte le lingue e in tutte le librerie esistenti con una chiara visione di una condizione che consente di far passare alcune cose bloccandone altre. –

2

Non esiste un modo idiomatico per ottenere lo stesso risultato previsto in Go in una singola riga come in Ruby, ma con una funzione di supporto è possibile ottenere la stessa espressività di Ruby.

Si può chiamare questa funzione di supporto come:

Filter(strs, func(v string) bool { 
    return strings.HasPrefix(v, "foo_") // return foo_testfor 
})) 

Ecco l'intero codice:

package main 

import "strings" 
import "fmt" 

// Returns a new slice containing all strings in the 
// slice that satisfy the predicate `f`. 
func Filter(vs []string, f func(string) bool) []string { 
    vsf := make([]string, 0) 
    for _, v := range vs { 
     if f(v) && len(v) > 7 { 
      vsf = append(vsf, v) 
     } 
    } 
    return vsf 
} 

func main() { 

    var strs = []string{"foo1", "foo2", "foo3", "foo3", "foo_testfor", "_foo"} 

    fmt.Println(Filter(strs, func(v string) bool { 
     return strings.HasPrefix(v, "foo_") // return foo_testfor 
    })) 
} 

E l'esempio in esecuzione: Playground

6

Dai un'occhiata alla robpike's filter library. Ciò consentirebbe di fare:

package main 

import (
    "fmt" 
    "strings" 
    "filter" 
) 

func isNoFoo7(a string) bool { 
    return ! strings.HasPrefix(a, "foo_") && len(a) <= 7 
} 

func main() { 
    a := []string{"test", "some_other_test", "foo_etc"} 
    result := Choose(a, isNoFoo7) 
    fmt.Println(result) // [test] 
} 

È interessante notare che il README.md da Rob:

volevo vedere quanto sia stato difficile per implementare questo genere di cose in Go, con un così bello API che potrei gestire. Non è stato difficile. Dopo averlo scritto un paio di anni fa, non ho avuto occasione di usarlo una volta. Invece, uso solo cicli "for". Non dovresti neanche usarlo.

Quindi il modo più idiomatico Secondo Rob sarebbe qualcosa di simile:

func main() { 
    a := []string{"test", "some_other_test", "foo_etc"} 
    nofoos := []string{} 
    for i := range a { 
     if(!strings.HasPrefix(a[i], "foo_") && len(a[i]) <= 7) { 
      nofoos = append(nofoos, a[i]) 
     } 
    } 
    fmt.Println(nofoos) // [test] 
} 

Questo stile è molto simile, se non identico, l'approccio qualsiasi linguaggio C-famiglia prende.

Problemi correlati