# Avoiding double lookup

**URL:** <https://discourse.julialang.org/t/avoiding-double-lookup/78636>\
**Category:** Performance\
**Created:** [March 28, 2022, 8:50pm UTC](https://discourse.julialang.org/t/avoiding-double-lookup/78636 "2022-03-28T20:50:25Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![jar1](https://avatars.discourse-cdn.com/v4/letter/j/c0e974/32.png) [@jar1](https://discourse.julialang.org/u/jar1)\
**Post date:** [March 28, 2022, 8:50pm UTC](https://discourse.julialang.org/t/avoiding-double-lookup/78636/1 "2022-03-28T20:50:25Z")

</div>

Is it possible to cache the location so I don’t need to look it up twice (once in `get` and once in `setindex!`)?

```julia
"Increment the count by 1, starting from 0."
function bump!(d::Dict{K,Int}, k::K) where K
    v = get(d, k, 0)
    setindex!(d, v+1, k)
    d
end

let
    d = Dict{Int,Int}(10 => 1)
    bump!(d, 10)
end

```

---

<div class="post-metadata">

**Author:** ![goerch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerch/32/29122_2.png) [@goerch](https://discourse.julialang.org/u/goerch)\
**Post date:** [March 28, 2022, 8:57pm UTC](https://discourse.julialang.org/t/avoiding-double-lookup/78636/2 "2022-03-28T20:57:36Z")

</div>

There is get! 😉

```julia
  get!(collection, key, default)

  Return the value stored for the given key, or if no mapping for the key is present, store key => default, and return default.

```

and

```julia
  get!(f::Function, collection, key)

  Return the value stored for the given key, or if no mapping for the key is present, store key => f(), and return f().

  This is intended to be called using do block syntax.

```

---

<div class="post-metadata">

**Author:** ![tomerarnon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomerarnon/32/3170_2.png) [@tomerarnon](https://discourse.julialang.org/u/tomerarnon)\
**Post date:** [March 28, 2022, 10:42pm UTC](https://discourse.julialang.org/t/avoiding-double-lookup/78636/4 "2022-03-28T22:42:15Z")

</div>

You can use `Base.ht_keyindex` like `get` does internally:

```julia
function get(h::Dict{K,V}, key, default) where V where K
    index = ht_keyindex(h, key)
    @inbounds return (index < 0) ? default : h.vals[index]::V
end

```

In your case, if `index < 0` you would set `d[k] = 1`

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [March 29, 2022, 12:44am UTC](https://discourse.julialang.org/t/avoiding-double-lookup/78636/5 "2022-03-29T00:44:57Z")

</div>

> [@tomerarnon](#):
>
> You can use `Base.ht_keyindex` like `get` does internally

`ht_keyindex` is not documented and, therefore, not part of the public interface. It should be avoided if possible, as it can disappear between any Julia versions. Does `get!` (the version with a bang) not cover your case?

---

<div class="post-metadata">

**Author:** ![tomerarnon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomerarnon/32/3170_2.png) [@tomerarnon](https://discourse.julialang.org/u/tomerarnon)\
**Post date:** [March 29, 2022, 12:59am UTC](https://discourse.julialang.org/t/avoiding-double-lookup/78636/6 "2022-03-29T00:59:17Z")

</div>

> [@Henrique\_Becker](#):
>
> Does `get!` (the version with a bang) not cover your case?

I think not. @jar1 is asking if you can increment the value of a key in a dictionary with a single lookup. `get!` can set a default value if the key isn’t found, but to increment the value (if it _was_ found) requires a second lookup in the form of `d[k] += 1`

---

<div class="post-metadata">

**Author:** ![tomerarnon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomerarnon/32/3170_2.png) [@tomerarnon](https://discourse.julialang.org/u/tomerarnon)\
**Post date:** [March 29, 2022, 1:04am UTC](https://discourse.julialang.org/t/avoiding-double-lookup/78636/7 "2022-03-29T01:04:30Z")

</div>

I.e.:

```julia
julia> function bump!(d, k)
           index = Base.ht_keyindex(d, k)
           if index < 0
               d[k] = 1
           else
               d.vals[index] += 1
           end
           d
       end
bump! (generic function with 1 method)

julia> function bump2!(d, k)
           d[k] = get(d, k, 0) + 1
           d
       end

julia> @btime bump!(d, 's') setup = (d = Dict('a':'f' .=> 1:6))
  11.624 ns (0 allocations: 0 bytes)
Dict{Char, Int64} with 7 entries:
  'f' => 6
  'a' => 1
  'c' => 3
  'd' => 4
  'e' => 5
  's' => 999
  'b' => 2

julia> @btime bump2!(d, 's') setup = (d = Dict('a':'f' .=> 1:6))
  22.042 ns (0 allocations: 0 bytes)
Dict{Char, Int64} with 7 entries:
  'f' => 6
  'a' => 1
  'c' => 3
  'd' => 4
  'e' => 5
  's' => 996
  'b' => 2

```

---

<div class="post-metadata">

**Author:** ![c42f](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/c42f/32/52842_2.png) [@c42f](https://discourse.julialang.org/u/c42f)\
**Post date:** [March 29, 2022, 1:20am UTC](https://discourse.julialang.org/t/avoiding-double-lookup/78636/8 "2022-03-29T01:20:58Z")

</div>

The interface in [Dictionaries.jl](https://github.com/andyferris/Dictionaries.jl) provides a tokens API as an abstraction for the equivalent of `ht_keyindex`:

```julia
julia> d = Dictionary(["a","b"], [1,2])
2-element Dictionary{String, Int64}
 "a" │ 1
 "b" │ 2

julia> hadtoken, tok = gettoken!(d, "c")
       settokenvalue!(d, tok, hadtoken ? gettokenvalue(d, tok) + 1 : 0)
3-element Dictionary{String, Int64}
 "a" │ 1
 "b" │ 2
 "c" │ 0

julia> hadtoken, tok = gettoken!(d, "c")
       settokenvalue!(d, tok, hadtoken ? gettokenvalue(d, tok) + 1 : 0)
3-element Dictionary{String, Int64}
 "a" │ 1
 "b" │ 2
 "c" │ 1

```

However `Dict` predates Dictionaries.jl so the interfaces aren’t compatible.

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [March 29, 2022, 1:23am UTC](https://discourse.julialang.org/t/avoiding-double-lookup/78636/9 "2022-03-29T01:23:47Z")

</div>

Sincerely, this should be addressed by a PR to support a `get!` which takes a function argument that has the found value as a parameter (or default if there is nothing).

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [March 29, 2022, 1:45am UTC](https://discourse.julialang.org/t/avoiding-double-lookup/78636/10 "2022-03-29T01:45:13Z")

</div>

If the values in your dictionary are mutable, then you can just mutate the value that you get back from `get!`, like this:

```julia
julia> function foo!(d, k)
           v = get!(d, k, Int[])
           push!(v, 0)
       end
foo! (generic function with 1 method)

julia> d = Dict{Int, Vector{Int}}()
Dict{Int64, Vector{Int64}}()

julia> foo!(d, 1)
1-element Vector{Int64}:
 0

julia> foo!(d, 1)
2-element Vector{Int64}:
 0
 0

julia> foo!(d, 2)
1-element Vector{Int64}:
 0

julia> d
Dict{Int64, Vector{Int64}} with 2 entries:
  2 => [0]
  1 => [0, 0]

```

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [March 29, 2022, 1:54am UTC](https://discourse.julialang.org/t/avoiding-double-lookup/78636/11 "2022-03-29T01:54:16Z")

</div>

I pushed for it, lets see if the core developers have some interest: [https://github.com/JuliaLang/julia/issues/13055#issuecomment-1081317599](https://github.com/JuliaLang/julia/issues/13055#issuecomment-1081317599)

It is very easy to implement, so I may end up doing it if they like the idea.

---

<div class="post-metadata">

**Author:** ![Stephen\_Vavasis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stephen_vavasis/32/3389_2.png) [@Stephen\_Vavasis](https://discourse.julialang.org/u/Stephen_Vavasis)\
**Post date:** [March 31, 2022, 12:05am UTC](https://discourse.julialang.org/t/avoiding-double-lookup/78636/12 "2022-03-31T00:05:00Z")

</div>

Note also the related: [Add modify! function for lookup/update/insert/delete in one go by tkf · Pull Request #33758 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/33758)

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [March 31, 2022, 12:18am UTC](https://discourse.julialang.org/t/avoiding-double-lookup/78636/13 "2022-03-31T00:18:58Z")

</div>

Good to know but, sincerely, I really do not like the design that is going there and it overcomplicates for the specific case discussed here, where there is a `default` value (and messing with `nothing` and `Some` is not needed).
