I would like to implement pick function that would extract the given key from the collection objects.
let people = @[(name: "John"), (name: "Sarah")]
echo people.pick(name)
It works if the key is the object field, but doesn't work if it's a function. Is there a way to make it work for function too?
template pick*[T](list: seq[T], field: untyped): untyped =
var result: seq[T.`field`] = @[]
for v in `list`:
result.add v.`field`
result
let people = @[(name: "John"), (name: "Sarah")]
echo people.pick(name)
proc some_fn(o: tuple[name: string]): string = o.name
echo people.pick(some_fn)
Your last usage of pick is basically the same as a call to map. There are a couple of ways to achieve this, the easiest but slightly hacky way is to do this (playground:
#import sequtils
template pick*[T](list: seq[T], field: untyped): untyped =
when compiles(`list`.map(`field`)):
`list`.map(`field`)
else:
var result: seq[T.`field`] = @[]
for v in `list`:
result.add v.`field`
result
let people = @[(name: "John"), (name: "Sarah")]
echo people.pick(name)
proc some_fn(o: tuple[name: string]): string = o.name
echo people.pick(some_fn)
This will however fail with a rather unproductive error message if sequtils isn't imported.
Maybe not the cleanest, but you can do this:
template pick*[T](list: seq[T], field: untyped): untyped =
var res: seq[typeof(list[0].field)] = @[]
for v in `list`:
res.add v.`field`
res
let people = @[(name: "John"), (name: "Sarah")]
echo people.pick(name)
proc some_fn(o: tuple[name: string]): string = o.name
echo people.pick(some_fn)
Your last usage of pick is basically the same as a call to map
I didn't knew map can do that :). Although it has its own limitation, it works with function but not with field.
I found a way, by using sugar macro
import sugar, sequtils
template pick*[T](list: seq[T], field: untyped): untyped =
list.map((v) => v.`field`)
let people = @[(name: "John"), (name: "Sarah")]
echo people.pick(name)
proc some_fn(o: tuple[name: string]): string = o.name
echo people.pick(some_fn)