'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?

go


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.

Github - Go Funk

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

https://github.com/samber/lo

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.

enter image description here

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