# Retrieving an instance of an enum using a string

**URL:** https://discourse.julialang.org/t/retrieving-an-instance-of-an-enum-using-a-string/61183
**Category:** General Usage
**Tags:** enum
**Created:** [May 15, 2021, 3:33am UTC](https://discourse.julialang.org/t/retrieving-an-instance-of-an-enum-using-a-string/61183 "2021-05-15T03:33:25Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![lewis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lewis/32/5217_2.png) [@lewis](https://discourse.julialang.org/u/lewis)
#### Post date: [May 15, 2021, 3:33am UTC](https://discourse.julialang.org/t/retrieving-an-instance-of-an-enum-using-a-string/61183/1 "2021-05-15T03:33:25Z")

</div>

This has been posed, sort of, several times in Discourse or stackoverflow.

For this example: `@enum condition nil=5 mild sick severe`

It is easy to get the enum with an integer:

```julia
julia> condition(5)
nil::condition = 5

```

I have a need to convert a string that matches the enum’s appearance to the actual enum: to go from “nil” to `nil::condition = 5`.

Here are 2 versions of `strtoenum`. Which of these would be preferred?

First, using the function Base.Enums.namemap, which is not exported.

```julia
function strtoenum(enumgrp::Type{<:Enum{T}}, str::String) where {T<:Integer}
    dict = Base.Enums.namemap(enumgrp)
    found = -1
    srch = Symbol(str)
    for (k,v) in dict
        if v == srch
            found = k
            break
        end
    end
    if found != -1
        return enumgrp(found)
    end
end

```

```julia
julia> @btime strtoenum(condition, "nil")
  68.191 ns (0 allocations: 0 bytes)
nil::condition = 5

```

Second, using instances, which is documented, rather than namemap:

```julia
function strtoenum2(enumgrp::Type{<:Enum{T}}, str::String) where {T<:Integer}
    srch = Symbol(str)
    found = -1
    for val in instances(enumgrp)
        if srch == Symbol(val)
            found = val
            break
        end
    end
    if found != -1
        return found
    end
end

```

```julia
julia> @btime strtoenum2(condition, "nil")
  71.731 ns (0 allocations: 0 bytes)
nil::condition = 5

```

Break-even in performance; no allocations.

Questions:

1. Neither raises an error if the input string doesn’t match the name of the enum–instead returning nothing. Should a value error be raised? nothing is probably going to cause an error if you try to use the return value for anything.
2. Which is preferred?
3. Is this worth a PR to enum? It’s way slower than `condition(5)`, which has nothing to do. The cost is linear in the number of instances in the enum set.

---

<div class="post-metadata">

### Author: ![lewis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lewis/32/5217_2.png) [@lewis](https://discourse.julialang.org/u/lewis)
#### Post date: [May 15, 2021, 4:54am UTC](https://discourse.julialang.org/t/retrieving-an-instance-of-an-enum-using-a-string/61183/2 "2021-05-15T04:54:43Z")

</div>

Thought this through better. Enums aren’t really meant to change (though they can).

I have them setup in advance. I can build lookup table as a dict in both directions. I only need to do this once. I used symbols below, but it would be just as easy to do with strings (or convert results as needed). Then it’s way faster to just do the lookup for individual enums as needed elsewhere in the code.

Doh!

```julia
julia> inst = instances(condition)
(nil, mild, sick, severe)

julia> syms = Symbol.(inst)
(:nil, :mild, :sick, :severe)

julia> condsym = Dict(zip(inst, syms))
Dict{condition, Symbol} with 4 entries:
  sick => :sick
  mild => :mild
  severe => :severe
  nil => :nil

julia> symcond = Dict(zip(syms, inst))
Dict{Symbol, condition} with 4 entries:
  :sick => sick
  :mild => mild
  :nil => nil
  :severe => severe

```

A single lookup is fast:

```julia
julia> @btime $symcond[:severe]
  5.831 ns (0 allocations: 0 bytes)
severe::condition = 8

```

This is what I’ll do and what anybody else can do…

---

<div class="post-metadata">

### Author: ![FPGro](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fpgro/32/20822_2.png) [@FPGro](https://discourse.julialang.org/u/FPGro)
#### Post date: [May 15, 2021, 6:06am UTC](https://discourse.julialang.org/t/retrieving-an-instance-of-an-enum-using-a-string/61183/3 "2021-05-15T06:06:31Z")

</div>

If you inted to do that very often, you may try to use a `LittleDict` from [OrderedCollections](https://github.com/JuliaCollections/OrderedCollections.jl) which should be a lot faster for just a handful of entries.

---

<div class="post-metadata">

### Author: ![lewis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lewis/32/5217_2.png) [@lewis](https://discourse.julialang.org/u/lewis)
#### Post date: [May 15, 2021, 7:05pm UTC](https://discourse.julialang.org/t/retrieving-an-instance-of-an-enum-using-a-string/61183/4 "2021-05-15T19:05:36Z")

</div>

I’ll give it a shot. Thanks.

---

<div class="post-metadata">

### Author: ![mhinsch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhinsch/32/26676_2.png) [@mhinsch](https://discourse.julialang.org/u/mhinsch)
#### Post date: [July 12, 2021, 6:42pm UTC](https://discourse.julialang.org/t/retrieving-an-instance-of-an-enum-using-a-string/61183/5 "2021-07-12T18:42:27Z")

</div>

Nice. I used `eval(Meta.parse(...))`, but this is prettier. Of course, ideally `parse(condition, "...")` would just work, but oh well, I’ll take your solution.

---

<div class="post-metadata">

### Author: ![o314](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/o314/32/252_2.png) [@o314](https://discourse.julialang.org/u/o314)
#### Post date: [August 17, 2022, 5:27am UTC](https://discourse.julialang.org/t/retrieving-an-instance-of-an-enum-using-a-string/61183/6 "2022-08-17T05:27:51Z")

</div>

May be a more julian way to do it

```julia
Base.tryparse(E::Type{<:Enum}, str::String) =
    let insts = instances(E) ,
        p = findfirst(==(Symbol(str)) ∘ Symbol, insts) ;
        p !== nothing ? insts[p] : nothing
    end

```

```julia
using Test

@enum A AA AB
@test tryparse(A, "AA") == AA
@test tryparse(A, "AB") == AB
@test tryparse(A, "AC") == nothing

```

NOTA:

- as of julia v1.7 both algo seems to be o(n) . namemap is dict-based but we search on value not key  
so no pref from perf there.
- dict are still unordered in base, and the use of iterate with them is still messy. so a pref for instances that is vect-based
- a more proper way may be to add a `valmap = Dict{Symbol, basetype}()` near to namemap @  
[https://github.com/JuliaLang/julia/blob/master/base/Enums.jl](https://github.com/JuliaLang/julia/blob/master/base/Enums.jl)

---

<div class="post-metadata">

### Author: ![o314](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/o314/32/252_2.png) [@o314](https://discourse.julialang.org/u/o314)
#### Post date: [April 8, 2023, 3:19am UTC](https://discourse.julialang.org/t/retrieving-an-instance-of-an-enum-using-a-string/61183/7 "2023-04-08T03:19:50Z")

</div>

```julia
@enum LogLevel begin
    LL_INFO
    LL_DEBUG
end

```

Please also note from

```julia
Base.tryparse(E::Type{<:Enum}, str::String) =
    let insts = instances(E) ,
        p = findfirst(==(Symbol(str)) ∘ Symbol, insts) ;
        p !== nothing ? insts[p] : nothing
    end

using Test
@testset "now" begin
    @test tryparse(LogLevel, "LL_INFO") == LL_INFO
    @test tryparse(LogLevel, "ll_info") == nothing
    @test tryparse(LogLevel, "INFO") == nothing
    @test tryparse(LogLevel, "info") == nothing
end

```

Those possibles evolutions :

```julia
Base.tryparse(E::Type{<:Enum}, str::String; prefix::String="") =
    let eq(x, e) = lowercase(prefix*x) == lowercase(e) ,
        # eq could be statically dispatchable according to some enum surface lang traits
        insts = instances(E) ,
        p = findfirst(insts .|> string) do e; eq(str,e) end;
        p !== nothing ? insts[p] : nothing
    end

@testset "meanwhile" begin
    @test tryparse(LogLevel, "LL_INFO") == LL_INFO
    @test tryparse(LogLevel, "ll_info") == LL_INFO
    @test tryparse(LogLevel, "INFO"; prefix="LL_") == LL_INFO
    @test tryparse(LogLevel, "info"; prefix="LL_") == LL_INFO
end

```

particulaly considering [Solving the drawbacks of @enum](https://discourse.julialang.org/t/solving-the-drawbacks-of-enum/74506)
