I have encountered odd behavior of golang 1.16. For one of my projects I needed to iterate over runes in string. So, I created a simple iterator looks like:
func iterRunes(s string) <-chan rune {
runes := make(chan rune);
go func() {
defer close(runes);
for _, char := range s {
runes <- char;
}
} ()
return runes;
}
It works perfectly, but I also needed to enumerate values yielding from this one. So, I have written another one looks like:
func enumRunes(runes <-chan rune) <-chan struct {int; rune} {
eRunes := make(chan struct {int; rune});
go func() {
defer close(eRunes);
i := 0;
for r := range runes {
eRunes <- struct {int; rune} {i, r};
i++;
}
} ()
return eRunes;
}
It also works perfectly. But if I try to combine them like enumRunes(iterRunes(pattern)), I encounter a problem. I have two code snippet to show.
The first one is:
tmp := enumRunes(iterRunes(pattern))
z := <-tmp;
for z.int < utf8.RuneCountInString(pattern) {
fmt.Println("z = <- tmp;")
fmt.Println(z)
z = <- tmp;
}
And its output is like:
...
z = <- tmp;
{0 0}
{0 0}
z = <- tmp;
{0 0}
{0 0}
z = <- tmp;
{0 0}
{0 0}
z = <- tmp;
...
Here z is of type struct. So, I got into an infinite loop because the struct is not updated for some reason.
And the second snippet:
tmp := iterRunes(pattern)
z := <-tmp;
for qq := 0; qq < utf8.RuneCountInString(pattern); qq++ {
fmt.Println("z = <- tmp;")
fmt.Println(z)
z = <- tmp;
}
Here z is of type rune and it works fine.
The question is why z in the first snippet isn't updated.
Thank you in advance.
I hope you know that (ref)
A receive from a closed channel returns the zero value immediately
With that, let's look at your problem!
z.int < utf8.RuneCountInString(pattern)
Here this would always be satisfied as long as z.int is lesser than the rune count of the string, even if that value was a default 0.
With this context, you can now see that even though the channel was closed, since you're reading from it, it'll return an empty {0, 0} struct. Which sets z.int to 0 and the loop continues to infinity.
I have an array of strings, and I'd like to exclude values that start in foo_ OR are longer than 7 characters.
I can loop through each element, run the if statement, and add it to a slice along the way. But I was curious if there was an idiomatic or more golang-like way of accomplishing that.
Just for example, the same thing might be done in Ruby as
my_array.select! { |val| val !~ /^foo_/ && val.length <= 7 }
There is no one-liner as you have it in Ruby, but with a helper function you can make it almost as short.
Here's our helper function that loops over a slice, and selects and returns only the elements that meet a criteria captured by a function value:
func filter(ss []string, test func(string) bool) (ret []string) {
for _, s := range ss {
if test(s) {
ret = append(ret, s)
}
}
return
}
Starting with Go 1.18, we can write it generic so it will work with all types, not just string:
func filter[T any](ss []T, test func(T) bool) (ret []T) {
for _, s := range ss {
if test(s) {
ret = append(ret, s)
}
}
return
}
Using this helper function your task:
ss := []string{"foo_1", "asdf", "loooooooong", "nfoo_1", "foo_2"}
mytest := func(s string) bool { return !strings.HasPrefix(s, "foo_") && len(s) <= 7 }
s2 := filter(ss, mytest)
fmt.Println(s2)
Output (try it on the Go Playground, or the generic version: Go Playground):
[asdf nfoo_1]
Note:
If it is expected that many elements will be selected, it might be profitable to allocate a "big" ret slice beforehand, and use simple assignment instead of the append(). And before returning, slice the ret to have a length equal to the number of selected elements.
Note #2:
In my example I chose a test() function which tells if an element is to be returned. So I had to invert your "exclusion" condition. Obviously you may write the helper function to expect a tester function which tells what to exclude (and not what to include).
Have a look at robpike's filter library. This would allow you to do:
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]
}
Interestingly enough the README.md by Rob:
I wanted to see how hard it was to implement this sort of thing in Go, with as nice an API as I could manage. It wasn't hard.
Having written it a couple of years ago, I haven't had occasion to use it once. Instead, I just use "for" loops.
You shouldn't use it either.
So the most idiomatic way according to Rob would be something like:
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]
}
This style is very similar, if not identical, to the approach any C-family language takes.
Today, I stumbled on a pretty idiom that surprised me. If you want to filter a slice in place without allocating, use two slices with the same backing array:
s := []T{
// the input
}
s2 := s
s = s[:0]
for _, v := range s2 {
if shouldKeep(v) {
s = append(s, v)
}
}
Here's a specific example of removing duplicate strings:
s := []string{"a", "a", "b", "c", "c"}
s2 := s
s = s[:0]
var last string
for _, v := range s2 {
if len(s) == 0 || v != last {
last = v
s = append(s, v)
}
}
If you need to keep both slices, simply replace s = s[:0] with s = nil or s = make([]T, 0, len(s)), depending on whether you want append() to allocate for you.
There are a couple of nice ways to filter a slice without allocations or new dependencies. Found in the Go wiki on Github:
Filter (in place)
n := 0
for _, x := range a {
if keep(x) {
a[n] = x
n++
}
}
a = a[:n]
And another, more readable, way:
Filtering without allocating
This trick uses the fact that a slice shares the same backing array
and capacity as the original, so the storage is reused for the
filtered slice. Of course, the original contents are modified.
b := a[:0]
for _, x := range a {
if f(x) {
b = append(b, x)
}
}
For elements which must be garbage collected, the following code can
be included afterwards:
for i := len(b); i < len(a); i++ {
a[i] = nil // or the zero value of T
}
One thing I'm not sure about is whether the first method needs clearing (setting to nil) the items in slice a after index n, like they do in the second method.
EDIT: the second way is basically what MicahStetson described in his answer. In my code I use a function similar to the following, which is probably as good as it gets in terms on performance and readability:
func filterSlice(slice []*T, keep func(*T) bool) []*T {
newSlice := slice[:0]
for _, item := range slice {
if keep(item) {
newSlice = append(newSlice, item)
}
}
// make sure discarded items can be garbage collected
for i := len(newSlice); i < len(slice); i++ {
slice[i] = nil
}
return newSlice
}
Note that if items in your slice are not pointers and don't contain pointers you can skip the second for loop.
There isn't an idiomatic way you can achieve the same expected result in Go in one single line as in Ruby, but with a helper function you can obtain the same expressiveness as in Ruby.
You can call this helper function as:
Filter(strs, func(v string) bool {
return strings.HasPrefix(v, "foo_") // return foo_testfor
}))
Here is the whole code:
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
}))
}
And the running example: Playground
you can use the loop as you did and wrap it to a utils function for reuse.
For multi-datatype support, copy-paste will be a choice. Another choice is writing a generating tool.
And final option if you want to use lib, you can take a look on https://github.com/ledongthuc/goterators#filter that I created to reuse aggregate & transform functions.
It requires the Go 1.18 to use that support generic + dynamic type you want to use with.
filteredItems, err := Filter(list, func(item int) bool {
return item % 2 == 0
})
filteredItems, err := Filter(list, func(item string) bool {
return item.Contains("ValidWord")
})
filteredItems, err := Filter(list, func(item MyStruct) bool {
return item.Valid()
})
It also supports Reduce in case you want to optimize the way you select.
Hope it's useful with you!
"Select Elements from Array" is also commonly called a filter function. There's no such thing in go. There are also no other "Collection Functions" such as map or reduce. For the most idiomatic way to get the desired result, I find https://gobyexample.com/collection-functions a good reference:
[...] in Go it’s common to provide collection functions if and when they are specifically needed for your program and data types.
They provide an implementation example of the filter function for strings:
func Filter(vs []string, f func(string) bool) []string {
vsf := make([]string, 0)
for _, v := range vs {
if f(v) {
vsf = append(vsf, v)
}
}
return vsf
}
However, they also say, that it's often ok to just inline the function:
Note that in some cases it may be clearest to just inline the
collection-manipulating code directly, instead of creating and calling
a helper function.
In general, golang tries to only introduce orthogonal concepts, meaning that when you can solve a problem one way, there shouldn't be too many more ways to solve it. This adds simplicity to the language by only having a few core concepts, such that not every developer uses a different subset of the language.
Take a look at this library: github.com/thoas/go-funk
It provides an implementation of a lot of life-saving idioms in Go (including filtering of elements in array for instance).
r := funk.Filter([]int{1, 2, 3, 4}, func(x int) bool {
return x%2 == 0
}
Here is an elegant example of both Fold and Filter that uses recursion to accomplish filtering. FoldRight is also generally useful. It is not stack safe but could be made so with trampolining. Once Golang has generics it can be entirely generalized for any 2 types:
func FoldRightStrings(as, z []string, f func(string, []string) []string) []string {
if len(as) > 1 {//Slice has a head and a tail.
h, t := as[0], as[1:len(as)]
return f(h, FoldRightStrings(t, z, f))
} else if len(as) == 1 {//Slice has a head and an empty tail.
h := as[0]
return f(h, FoldRightStrings([]string{}, z, f))
}
return z
}
func FilterStrings(as []string, p func(string) bool) []string {
var g = func(h string, accum []string) []string {
if p(h) {
return append(accum, h)
} else {
return accum
}
}
return FoldRightStrings(as, []string{}, g)
}
Here is an example of its usage to filter out all the strings with length < 8
var p = func(s string) bool {
if len(s) < 8 {
return true
} else {
return false
}
}
FilterStrings([]string{"asd","asdfas","asdfasfsa","asdfasdfsadfsadfad"}, p)
I`m developing this library: https://github.com/jose78/go-collection. PLease try this example to filter elements:
package main
import (
"fmt"
col "github.com/jose78/go-collection/collections"
)
type user struct {
name string
age int
id int
}
func main() {
newMap := generateMapTest()
if resultMap, err := newMap.FilterAll(filterEmptyName); err != nil {
fmt.Printf("error")
} else {
fmt.Printf("Result: %v\n", resultMap)
result := resultMap.ListValues()
fmt.Printf("Result: %v\n", result)
fmt.Printf("Result: %v\n", result.Reverse())
fmt.Printf("Result: %v\n", result.JoinAsString(" <---> "))
fmt.Printf("Result: %v\n", result.Reverse().JoinAsString(" <---> "))
result.Foreach(simpleLoop)
err := result.Foreach(simpleLoopWithError)
if err != nil {
fmt.Println(err)
}
}
}
func filterEmptyName(key interface{}, value interface{}) bool {
user := value.(user)
return user.name != "empty"
}
func generateMapTest() (container col.MapType) {
container = col.MapType{}
container[1] = user{"Alvaro", 6, 1}
container[2] = user{"Sofia", 3, 2}
container[3] = user{"empty", 0, -1}
return container
}
var simpleLoop col.FnForeachList = func(mapper interface{}, index int) {
fmt.Printf("%d.- item:%v\n", index, mapper)
}
var simpleLoopWithError col.FnForeachList = func(mapper interface{}, index int) {
if index > 0 {
panic(fmt.Sprintf("Error produced with index == %d\n", index))
}
fmt.Printf("%d.- item:%v\n", index, mapper)
}
Result of execution:
Result: map[1:{Alvaro 6 1} 2:{Sofia 3 2}]
Result: [{Sofia 3 2} {Alvaro 6 1}]
Result: [{Alvaro 6 1} {Sofia 3 2}]
Result: {Sofia 3 2} <---> {Alvaro 6 1}
Result: {Alvaro 6 1} <---> {Sofia 3 2}
0.- item:{Sofia 3 2}
1.- item:{Alvaro 6 1}
0.- item:{Sofia 3 2}
Recovered in f Error produced with index == 1
ERROR: Error produced with index == 1
Error produced with index == 1
The DOC currently are located in wiki section of the project. You can try it in this link. I hope you like it...
REgaRDS...
I have two values:
[3:6]
I was trying to play something around in Golang but I can't manage to find a good method to create an array according to those values.
This what I would like to achieve:
[3,4,5,6]
You can make use of the for ... range construct to make it more compact and maybe even faster:
lo, hi := 3, 6
s := make([]int, hi-lo+1)
for i := range s {
s[i] = i + lo
}
As a matter of curiosity, the loop can be implemented without a loop variable, but it will be slower, and the code longer. By decrementing hi:
for ; hi >= lo; hi-- {
s[hi-len(s)+1] = hi
}
Or incrementing lo:
for ; lo <= hi; lo++ {
s[len(s)-1-hi+lo] = lo
}
I was looking for a similar answer and #icza's answer was a great start. I ended up creating a helper function based on icza's answer:
func createNumbers(lo int, hi int) []int {
s := make([]int, hi-lo+1)
for i:= range s {
s[i] = i + lo
}
return s
}
I tried to translate the following Python code to Go
import random
list = [i for i in range(1, 25)]
random.shuffle(list)
print(list)
but found my Go version lengthy and awkward because there is no shuffle function and I had to implement interfaces and convert types.
What would be an idiomatic Go version of my code?
dystroy's answer is perfectly reasonable, but it's also possible to shuffle without allocating any additional slices.
for i := range slice {
j := rand.Intn(i + 1)
slice[i], slice[j] = slice[j], slice[i]
}
See this Wikipedia article for more details on the algorithm. rand.Perm actually uses this algorithm internally as well.
As your list is just the integers from 1 to 25, you can use Perm :
list := rand.Perm(25)
for i, _ := range list {
list[i]++
}
Note that using a permutation given by rand.Perm is an effective way to shuffle any array.
dest := make([]int, len(src))
perm := rand.Perm(len(src))
for i, v := range perm {
dest[v] = src[i]
}
Since 1.10 Go includes an official Fisher-Yates shuffle function.
Documentation: pkg/math/rand/#Shuffle
math/rand: add Shuffle
Shuffle uses the Fisher-Yates algorithm.
Since this is new API, it affords us the opportunity
to use a much faster Int31n implementation that mostly avoids division.
As a result, BenchmarkPerm30ViaShuffle is
about 30% faster than BenchmarkPerm30,
despite requiring a separate initialization loop
and using function calls to swap elements.
See also the original CL 51891
First, as commented by shelll:
Do not forget to seed the random, or you will always get the same order.
For example rand.Seed(time.Now().UnixNano())
Example:
words := strings.Fields("ink runs from the corners of my mouth")
rand.Shuffle(len(words), func(i, j int) {
words[i], words[j] = words[j], words[i]
})
fmt.Println(words)
Answer by Evan Shaw has a minor bug. If we iterate through the slice from lowest index to highest, to get a uniformly (pseudo) random shuffle, according to the same article, we must choose a random integer from interval [i,n) as opposed to [0,n+1).
That implementation will do what you need for larger inputs, but for smaller slices, it will perform a non-uniform shuffle.
To utilize rand.Intn(), we can do:
for i := len(slice) - 1; i > 0; i-- {
j := rand.Intn(i + 1)
slice[i], slice[j] = slice[j], slice[i]
}
following the same algorithm from Wikipedia article.
Maybe you can also use the following function:
func main() {
slice := []int{10, 12, 14, 16, 18, 20}
Shuffle(slice)
fmt.Println(slice)
}
func Shuffle(slice []int) {
r := rand.New(rand.NewSource(time.Now().Unix()))
for n := len(slice); n > 0; n-- {
randIndex := r.Intn(n)
slice[n-1], slice[randIndex] = slice[randIndex], slice[n-1]
}
}
When using the math/rand package, do not forget to set a source
// Random numbers are generated by a Source. Top-level functions, such as
// Float64 and Int, use a default shared Source that produces a deterministic
// sequence of values each time a program is run. Use the Seed function to
// initialize the default Source if different behavior is required for each run.
So I wrote a Shuffle function that takes this into consideration:
import (
"math/rand"
)
func Shuffle(array []interface{}, source rand.Source) {
random := rand.New(source)
for i := len(array) - 1; i > 0; i-- {
j := random.Intn(i + 1)
array[i], array[j] = array[j], array[i]
}
}
And to use it:
source := rand.NewSource(time.Now().UnixNano())
array := []interface{}{"a", "b", "c"}
Shuffle(array, source) // [c b a]
If you would like to use it, you can find it here https://github.com/shomali11/util
Raed's approach is very inflexible because of []interface{} as input. Here is more convenient version for go>=1.8:
func Shuffle(slice interface{}) {
rv := reflect.ValueOf(slice)
swap := reflect.Swapper(slice)
length := rv.Len()
for i := length - 1; i > 0; i-- {
j := rand.Intn(i + 1)
swap(i, j)
}
}
Example usage:
rand.Seed(time.Now().UnixNano()) // do it once during app initialization
s := []int{1, 2, 3, 4, 5}
Shuffle(s)
fmt.Println(s) // Example output: [4 3 2 1 5]
And also, don't forget that a little copying is better than a little dependency
Use Shuffle() from the math/rand library.
Here's an example:
package main
import (
"fmt"
"math/rand"
"strings"
)
func main() {
words := strings.Fields("ink runs from the corners of my mouth")
rand.Shuffle(len(words), func(i, j int) {
words[i], words[j] = words[j], words[i]
})
fmt.Println(words)
}
Since it comes from the math/rand library it needs to be seeded. See here for more details.