How to reverse a dictionary in Julia?
Asked Answered
R

7

12

If I have a dictionary such as

my_dict = Dict(
    "A" => "one",
    "B" => "two",
    "C" => "three"
  )

What's the best way to reverse the key/value mappings?

Rabkin answered 19/11, 2016 at 14:43 Comment(1)
Do you have to worry about two different keys mapping to the same value, or not?Bigod
A
23

One way would be to use a comprehension to build the new dictionary by iterating over the key/value pairs, swapping them along the way:

julia> Dict(value => key for (key, value) in my_dict)
Dict{String,String} with 3 entries:
  "two"   => "B"
  "one"   => "A"
  "three" => "C"

When swapping keys and values, you may want to keep in mind that if my_dict has duplicate values (say "A"), then the new dictionary may have fewer keys. Also, the value located by key "A" in the new dictionary may not be the one you expect (Julia's dictionaries do not store their contents in any easily-determined order).

Along answered 19/11, 2016 at 15:16 Comment(0)
B
15

Assuming that you don't have to worry about repeated values colliding as keys, you could use map with reverse:

julia> my_dict = Dict("A" => "one", "B" => "two", "C" => "three")
Dict{String,String} with 3 entries:
  "B" => "two"
  "A" => "one"
  "C" => "three"

julia> map(reverse, my_dict)
Dict{String,String} with 3 entries:
  "two"   => "B"
  "one"   => "A"
  "three" => "C"
Bigod answered 19/11, 2016 at 15:36 Comment(4)
That's the best answer!Trainee
@DSM: How can you do this for Julia v1.x?Citrin
I think this does not work in Julia Version 1.1.1. I get an error "map is not defined on dictionaries".Humorist
Use collect on my_dict first for Julia >= v1.xRoan
B
5

made this a while back for dictionaries that may have clashing values

function invert_dict(dict, warning::Bool = false)
    vals = collect(values(dict))
    dict_length = length(unique(vals))

    if dict_length < length(dict)
        if warning
            warn("Keys/Vals are not one-to-one")
        end 

        linked_list = Array[]

        for i in vals 
            push!(linked_list,[])
        end 

        new_dict = Dict(zip(vals, linked_list))

        for (key,val) in dict 
            push!(new_dict[val],key)
        end
    else
        key = collect(keys(dict))

        counter = 0
        for (k,v) in dict 
            counter += 1
            vals[counter] = v
            key[counter] = k
        end
        new_dict = Dict(zip(vals, key))
    end 

    return new_dict
end

using this if if a key becomes duplicate, you'll have a list with all values, so no data will be lost, i.e

julia> a = [1,2,3]
julia> b = ["a", "b", "b"]

julia> Dict(zip(a,b))
Dict{Int64,String} with 3 entries:
  2 => "b"
  3 => "b"
  1 => "a"

julia> invert_dict(ans)
Dict{String,Array} with 2 entries:
  "b" => Any[2,3]
  "a" => Any[1]
Broncobuster answered 19/11, 2016 at 17:36 Comment(0)
L
5

In Julia 1.x (assuming a bijection between keys and values):

julia> D = Dict("A" => "one", "B" => "two", "C" => "three")
Dict{String,String} with 3 entries:
  "B" => "two"
  "A" => "one"
  "C" => "three"

julia> invD = Dict(D[k] => k for k in keys(D))
Dict{String,String} with 3 entries:
  "two"   => "B"
  "one"   => "A"
  "three" => "C"

Otherwise:

julia> D = Dict("A" => "one", "B" => "three", "C" => "three")
Dict{String,String} with 3 entries:
  "B" => "three"
  "A" => "one"
  "C" => "three"

julia> invD = Dict{String,Vector{String}}()
Dict{String,Array{String,1}} with 0 entries

julia> for k in keys(D)
         if D[k] in keys(invD)
           push!(invD[D[k]],k)
         else
           invD[D[k]] = [k]
         end
       end

julia> invD
Dict{String,Array{String,1}} with 2 entries:
  "one"   => ["A"]
  "three" => ["B", "C"]
Liturgy answered 22/8, 2019 at 7:13 Comment(0)
M
3

Yet another good idea is what is done in this post: https://discourse.julialang.org/t/is-something-like-reversed-dict-findall-x-x-house-cc-is-to-slow/16443

Particularly, I liked a lot the solution, due to @ExpandingMan:

dict = Dict(rand(Int, 10^5) .=> rand(Int, 10^5))
rdict = Dict(values(dict) .=> keys(dict))

or the one from @bennedich

dict = Dict(rand(Int, 10^5) .=> rand(Int, 10^5))
rdict = Dict(v => k for (k,v) in dict)
Masjid answered 18/6, 2020 at 21:4 Comment(0)
A
0

DSM answer adapted with bluesmoon comment becomes (checked in Julia 1.9):

julia> my_dict = Dict("A" => "one", "B" => "two", "C" => "three")
Dict{String, String} with 3 entries:
  "B" => "two"
  "A" => "one"
  "C" => "three"
julia> Dict(map(reverse, collect(my_dict)))
Dict{String,String} with 3 entries:
  "two"   => "B"
  "one"   => "A"
  "three" => "C"

Notice that map(reverse, collect(my_dict)) has type Vector{Pair{String, String}}.

Auriculate answered 14/12, 2023 at 1:8 Comment(0)
K
0
using BenchmarkTools
dic = Dict(zip(rand(10000),rand(10000)))
@benchmark Dict(map(reverse,collect(dic))) #403 us
@benchmark Dict(dic[i]=> i for i in keys(dic)) #2.84 ms
@benchmark Dict(reverse(i) for i in dic) #361 us
@benchmark Dict(value => key for (key, value) in dic) #351 us
@benchmark Dict(values(dic) .=> keys(dic)) #426 us
Kofu answered 3/1, 2024 at 13:20 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.