For example:
# Given an enum how do I return a seq of its elements
type
Colors = enum
red
green
blue
var colors: Colors
# Expand Type to its enumerated items
func toSeq[T](enumType: T): seq[T] =
for item in enumType.type:
result.add item
echo colors.toSeq # -> @[red, green, blue]
But how do I change the above so that I don't have to instantiate the enum to give the same result.
echo Colors.toSeq # -> @[red, green, blue]
I'm stumped :-(
Quickly written together in inim:
nim> type A = enum
nim> import std/sequtils
nim> A.items.toSeq()
@[B, C] == type seq[A]
You need to to toSeq an iterator that iterates over the enum. The items iterator is implicitly called when using a for-loop on an enum (and a lot of other types, if not most/all of them), that's why you don't have to explicitly type for x in A.items in your for-loop example.
Thanks... that does it!
In my various iterations I knew about sequtils/toSeq but what I missed was the items
I find it interesting that
import sequtils
type
Colors = enum
red
green
blue
type
Colors* = enum
red
green
blue
let x = Colors.items
generates:
Error: undeclared field: 'items'
found iterators.items(a: string) [iterator declared in /usr/local/Cellar/nim/2.0.0_1/nim/lib/system/iterators.nim(257, 10)]
found iterators.items(s: Slice[items.T]) [iterator declared in /usr/local/Cellar/nim/2.0.0_1/nim/lib/system/iterators.nim(132, 10)]
found iterators.items(a: seq[T]) [iterator declared in /usr/local/Cellar/nim/2.0.0_1/nim/lib/system/iterators.nim(239, 10)]
found iterators.items(a: cstring) [iterator declared in /usr/local/Cellar/nim/2.0.0_1/nim/lib/system/iterators.nim(62, 10)]
found iterators.items(a: array[IX, T]) [iterator declared in /usr/local/Cellar/nim/2.0.0_1/nim/lib/system/iterators.nim(35, 10)]
found iterators.items(a: openArray[T: not char]) [iterator declared in /usr/local/Cellar/nim/2.0.0_1/nim/lib/system/iterators.nim(11, 10)]
found iterators.items(E: typedesc[T: enum and Ordinal]) [iterator declared in /usr/local/Cellar/nim/2.0.0_1/nim/lib/system/iterators.nim(122, 10)]
found iterators.items(a: set[T]) [iterator declared in /usr/local/Cellar/nim/2.0.0_1/nim/lib/system/iterators.nim(53, 10)]
found iterators.items(a: openArray[T: char]) [iterator declared in /usr/local/Cellar/nim/2.0.0_1/nim/lib/system/iterators.nim(18, 10)]
found sequtils.items(xs: iterator (): T{.closure.}) [iterator declared in /usr/local/Cellar/nim/2.0.0_1/nim/lib/pure/collections/sequtils.nim(1135, 10)]
but
let x = Colors.items.toSeq
generates the expected
@[red, green, blue]
I think I dig a little more into the docs/code for items.
BTW:
I now added a little helper in my general nim util module:
proc toSeq[T](enumType: T): seq[T] = enumType.items.toSeq
which allows the following:
echo Colors.toSeq # -> @[red, green, blue]
Thanks again for you help...
Happy to help!
The key difference you're running into here is that items is an iterator!
That is, it is neither a proc, nor a field on an object but its own thing that allows you to iterate over properties or things on a type. items is a generic iterator that exists for most things, though you can of course define your own iterator. Iterators aren't assigned to variables, they can only be used in loops and the like. That's why the attempt at assigning them to a variable causes errors ;-)
Thanks again for clarifying… Everything you say, is supported by reading deeper in the docs… but hard to find the appropriate docs when you don’t know the term to look up :-(.
Documentation that could read minds sure would be nice!
Documentation that could read minds sure would be nice!
We're pretty close to that with the current state of AI LLMs. For example, here's your question answered by GPT-4: https://www.phind.com/search?cache=ftn3ugjb5ke19wzhrilufsol
import sequtils, strutils
type
OneOfException* = object of ValueError
# Case insensittive prefix-match enum validation
proc oneOf*[T](input: string, choices: seq[T]): T =
# Helper to format raised error
template error[T](text1, text2: string, elements:seq[T]) =
raise OneOfException.newException [
text1, " '", input, "'", " - ", text2, ": ", elements.mapIt($it).join ", "
].join
# Check for prefix match (i.e. h match help)
proc minMatch(source, target: string): bool =
source == target[0..<[source.len, target.len].min]
let
toMatch = input.strip.toLowerAscii # Case insensitive
matching = choices.filterIt toMatch.minMatch $it
case matching.len
of 0: error "No match for", "Must be one of", choices
of 1: result = matching[0]
else: error "Ambiguous", "Matches", matching
if isMainModule:
type
Color = enum
red
green
blue
brown
# ...
for text in ["Red", "green", "g", "b", "brown", "BR", "bLu",""]:
try:
let color = text.oneOf Color.toSeq
echo "Converted ", text, " -> ", color, " (", color.int, ")"
except OneOfException as exception:
echo exception.msg
The above produced the following output:
Converted Red -> red (0)
Converted green -> green (1)
Converted g -> green (1)
Ambiguous 'b' - Matches: blue, brown
Converted brown -> brown (3)
Converted BR -> brown (3)
Converted bLu -> blue (2)
Ambiguous '' - Matches: red, green, blue, brown