I have found this solution in Python and want to write in Julia something that will yield the same end result.
It inverses the keys and values in a dictionary and consolidate the values in the inverse dictionary into lists that hold what was keys in the original dictionary, that had the same values.
This example says it all, (in Python syntax):
orig_dict # the original dictionary
{‘one’: 10, ‘two’: 20, ‘three’: 20, ‘four’: 10, ‘five’: 30}
inverted_dict # the inverted dictionary
{10: [‘one’, ‘four’], 20: [‘two’, ‘three’], 30: [‘five’]}
And here is the Python code that makes this inversion:
inverted_my_dict = {}
for key, value in my_dict.items():
inverted_my_dict.setdefault(value, list()).append(key)
The iteration line in Julia:
for (k, v) in my_dict
Asking for help here with the next step, the one that does the inverse and consolidation work. It is a one line in Python but it does two things.
How would you write something in Julia that will produce the same result?
Something like:
my_dict = Dict("one" => 10, "two" => 20, "three" => 20, "four" => 10, "five" => 30)
inverted_dict = Dict{valtype(my_dict), Vector{keytype(my_dict)}}()
for (k, v) in my_dict
push!(get!(() -> valtype(inverted_dict)[], inverted_dict, v), k)
end
julia> inverted_dict
Dict{Int64,Array{String,1}} with 3 entries:
30 => ["five"]
10 => ["four", "one"]
20 => ["two", "three"]
perhaps
4 Likes
This works perfectly!
Thank you for the good help.
You are using the following signature for get!
get!(f::Function, collection, key)
...
This is intended to be called using `do` block syntax:
get!(dict, key) do
# default value calculated here
time()
end
Could you please explain the example: isn’t get!
missing a parameter? Why time()
? Could you also give an example of the use of get!
in the do-block?
Edit: Thank you @kristoffer.carlsson for pointing out my oversight and the code is working now.
# I forgot the `( )`. Fixed now
dict = Dict{Any, Any}()
# The doc example
get!(dict, :a) do
time()
end
# The equivalent
get!( ()-> time(), dict, :b)
# If you already have function
function f()
return time()
end
get!(f, dict, :c)
println(duct) # Dict{Any,Any}(:a => 1.593679792519603e9,:b => 1.59367979306765e9,:c => 1.59367992264622e9)
My original post:
AFAIK,
-
do
block in Julia replaces the first parameter (f::Function
) of get!()
. Use do
block if the function is more than a one-liner, for readability.
-
time()
is to give a value to each element in dict
. It can be anything as you see fit.
So it is equivalent to
get!(x-> time(), dict, key)
1 Like
You want to end that with a ()
to create an instance, and not just refer to the type.
2 Likes
Thank you. That was my oversight. The code is working perfectly now.
I opened a PR with your clarification to the docs.
Often I have a one-to-one mapping, and then I define for myself
Base.inv(dict::Dict) = Dict(value => key for (key, value) in dict)
Using SplitApplyCombine’s group
we get some inverted dictionary similar to the solution above, but not as good as type is Any
.
Can it be improved?
using SplitApplyCombine
my_dict = Dict("one" => 10, "two" => 20, "three" => 20, "four" => 10, "five" => 30)
inverted = group(k -> my_dict[k], keys(my_dict))
3-element Dictionaries.Dictionary{Any, Vector{String}}
20 │ ["two", "three"]
10 │ ["four", "one"]
30 │ ["five"]