'Idiomatic Replacement for map/reduce/filter/etc
I have a list of things in a go program. I want to loop over them, and perform some operation on/with a subset of those things. Is there more elegant/idiomatic code for doing this than the following?
for key, value := range listOfThings {
if(!value.Enabled) {
continue;
}
doTheThing(key, value)
}
The large context -- I'm coming from languages where map/reduce/filter/etc are popular patterns for this sort of thing, but word on the internet is that those sorts of higher level abstractions aren't really a go-ish thing to do.
Is there something more elegant than guard/continue clauses in my range blocks for this sort of code?
Solution 1:[1]
Simple apply/filter/reduce package.
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.
Rob Pike
Follow Rob's advice. Use for loops.
Solution 2:[2]
If you're still interested, the go-funk package will enable you to do map/reduce/filter etc.
This library is at the time of answering regularly updated. The previous library mentioned hasn't been updated in 4 years.
Solution 3:[3]
March 2022 update:
You might be interested with the lo package that implements generics, which came out 15 March 2022 in stable go (1.18). Much like Javascript's Lodash, they are good functional/declarative helpers
Here, str is automatically inferred to be string because of generics, very cool & powerful
str, ok := lo.Find([]string{"foobar"}, func(i string) bool {
return i == "b"
})
Solution 4:[4]
With Go 1.18, type parameters (generics) enable type-safe functional structures, and some libraries like gostream are taking advantage of it. For example:
results := stream.Generate(rand.Int).
Map(func(n int) int {
return n%6 + 1
}).
Limit(5).
ToSlice()
fmt.Printf("results: %v\n", results)
Output:
results: [3 5 2 1 3]
Solution 5:[5]
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 at https://github.com/ledongthuc/goterators that I created to reuse aggregate & transform functions.
They are required go1.18 to use that support generic + dynamic type you want to use with.
// Reduce
total := Reduce(list, 0, func(previous int, current int, index int, list []int) int {
return previous + current
})
mappedItems := Map(testSource, func(item int64) float64 {
return float64(item)
})
filteredItems, err := Filter(list, func(item string) bool {
return item.Contains("ValidWord")
})
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | peterSO |
| Solution 2 | |
| Solution 3 | Valian Masdani |
| Solution 4 | Mario |
| Solution 5 |

