Reputation: 21285
Coming from Node.js, I could do something like:
// given an array `list` of objects with a field `fruit`:
fruits = list.map(el => el.fruit) # which will return an array of fruit strings
Any way to do that in an elegant one-liner in Go?
I know I can do it with a range loop, but I am looking for the possibility of a one-liner solution.
Upvotes: 26
Views: 43121
Reputation: 4190
I am not sure what you are referring to the word "Map". In golang map is something like dictionary where you have key and value map[key]value
.
Bellow is a helper function (must go to maps package) which allows to create a map in go:
func Map[T, K comparable, V any](arr []T, f_key func(T) K, f_val func(T) V) map[K]V {
m := make(map[K]V, len(arr))
for i := range arr {
m[f_key(arr[i])] = f_val(arr[i])
}
return m
}
// Usage
var m = Map(books,
func(b Book) string { return b.Id },
func(b Book) Book { return b }
)
Please see playground: https://go.dev/play/p/Bak3gfY0UV0
The map
in nodejs is a function which allows to go through elements and build your own data structure. In this case please see @jub0bs answer https://stackoverflow.com/a/71624929/1143349
Upvotes: -8
Reputation: 66354
Now that Go (1.23+) has standardised iterators, perhaps you'll find the following function useful:
func Map[T, U any](seq iter.Seq[T], f func(T) U) iter.Seq[U] {
return func(yield func(U) bool) {
for a := range seq {
if !yield(f(a)) {
return
}
}
}
}
Example:
names := slices.Values([]string{"Alice", "Bob", "Carol"})
var total int
for n := range Map(names, utf8.RuneCountInString) {
total += n
}
fmt.Println(total) // 13
In Go, arrays used as function parameters are inflexible (because their length is encoded in their type) and costly (because a function operates on copies of its array arguments). I'm assuming you'd like to operate on slices rather than on arrays.
Because methods cannot take additional type arguments, you cannot simply declare a generic Map
method in Go. However, you can define Map
as a generic top-level function:
func Map[T, U any](ts []T, f func(T) U) []U {
us := make([]U, len(ts))
for i := range ts {
us[i] = f(ts[i])
}
return us
}
Then you can write the following code,
names := []string{"Alice", "Bob", "Carol"}
fmt.Println(Map(names, utf8.RuneCountInString))
which prints [5 3 5]
to stdout (try it out in this Playground).
Go 1.18 saw the addition of a golang.org/x/exp/slices
package, which provides many convenient operations on slices, but a Map
function is noticeably absent from it. The omission of that function was the result of a long discussion in the GitHub issue dedicated to the golang.org/x/exp/slices
proposal; concerns included the following:
Map
Russ Cox ultimately elected to drop Map
from the proposal because it's
probably better as part of a more comprehensive streams API somewhere else.
The slices
package (added in Go 1.21) doesn't provide any Map
function either.
Upvotes: 52