I want to remove a key-value pair from a dictionary.
I am creating a new dictionary for now:
julia> dict = Dict(1 => "one", 2 => "two")
Dict{Int64,String} with 2 entries:
2 => "two"
1 => "one"
julia> dict = Dict(k => v for (k, v) in dict if k != 2)
Dict{Int64,String} with 1 entry:
1 => "one"
But I want to update the existing dictionary instead. How can I do this in Julia?
delete! will remove a key-value pair from a dictionary if the key exists, and have no effect if the key does not exist. It returns a reference to the dictionary:
julia> dict = Dict(1 => "one", 2 => "two")
Dict{Int64,String} with 2 entries:
2 => "two"
1 => "one"
julia> delete!(dict, 1)
Dict{Int64,String} with 1 entry:
2 => "two"
Use pop! if you need to use the value associated with the key. However, it will error if the key does not exist:
julia> dict = Dict(1 => "one", 2 => "two");
julia> value = pop!(dict, 2)
"two"
julia> dict
Dict{Int64,String} with 1 entry:
1 => "one"
julia> value = pop!(dict, 2)
ERROR: KeyError: key 2 not found
You can avoid throwing an error with the three argument version of pop!. The third argument is a default value to return in case the key does not exist:
julia> dict = Dict(1 => "one", 2 => "two");
julia> value_or_default = pop!(dict, 2, nothing)
"two"
julia> dict
Dict{Int64,String} with 1 entry:
1 => "one"
julia> value_or_default = pop!(dict, 2, nothing)
Use filter! to bulk remove key-value pairs according to some predicate function:
julia> dict = Dict(1 => "one", 2 => "two", 3 => "three", 4 => "four");
julia> filter!(p -> iseven(p.first), dict)
Dict{Int64,String} with 2 entries:
4 => "four"
2 => "two"
Related
I am looking for solution to remove duplicated value in vector which is in dictionary format at Julia.
Here is my dictionary :
x = Dict{AbstractString,Array{Integer,1}}("A" => [1,2,3], "B" => [3,4,5], "C" => [5,6,7])
and below is expected output :
Dict{AbstractString, Vector{Integer}} with 3 entries:
"A" => [1, 2]
"B" => [4]
"C" => [6, 7]
This is a relatively short way to do it (I have not optimized it for full speed to keep the solution short):
julia> using StatsBase
julia> x = Dict{AbstractString,Array{Integer,1}}("A" => [1,2,3], "B" => [3,4,5], "C" => [5,6,7])
Dict{AbstractString, Vector{Integer}} with 3 entries:
"B" => [3, 4, 5]
"A" => [1, 2, 3]
"C" => [5, 6, 7]
julia> dups = [k for (k, v) in countmap(Iterators.flatten(values(x))) if v > 1]
2-element Vector{Int64}:
5
3
julia> foreach(v -> setdiff!(v, dups), values(x))
julia> x
Dict{AbstractString, Vector{Integer}} with 3 entries:
"B" => [4]
"A" => [1, 2]
"C" => [6, 7]
If anything is not clear in the code please comment.
This solution updates your dictionary x in-place as I assumed this is what you wanted.
Or, pedestrian-style
allvalues(x::AbstractDict) = reduce(vcat, collect(values(x)))
function finddups(x::AbstractArray)
dups = Int[]
filter(item -> item in dups ? true : begin
push!(dups, item)
false end, x)
end
x = Dict{AbstractString,Array{Integer,1}}("A" => [1,2,3], "B" => [3,4,5], "C" => [5,6,7])
dups = x |> allvalues |> finddups
foreach(v -> setdiff!(v, dups), values(x))
x
I have some dictionary that already exists. I want to add a new key-value pair, but I do not want to create a new dictionary from scratch.
How can I add a new key-value pair to an existing dictionary in Julia?
Julia uses the common dict[key] = value syntax for setting a key-value pair:
julia> dict = Dict(1 => "one")
Dict{Int64,String} with 1 entry:
1 => "one"
julia> dict[2] = "two"
"two"
julia> dict
Dict{Int64,String} with 2 entries:
2 => "two"
1 => "one"
The same syntax will override a key-value pair if the key already exists:
julia> dict
Dict{Int64,String} with 2 entries:
2 => "two"
1 => "one"
julia> dict[1] = "foo"
"foo"
julia> dict
Dict{Int64,String} with 2 entries:
2 => "two"
1 => "foo"
dict[key] = value is syntax for a setindex! call. Although not as common, you can call setindex! directly:
julia> dict = Dict(1 => "one")
Dict{Int64,String} with 1 entry:
1 => "one"
julia> setindex!(dict, "two", 2)
Dict{Int64,String} with 2 entries:
2 => "two"
1 => "one"
I want to key into a dictionary, but Julia throws an exception if the key does not exist. To avoid the exception, I first have to check if they key exists in the dictionary.
I'm using this custom function for now:
function has_some_key(dict, key)
for (k, v) in dict
if k == key
return true
end
end
return false
end
Is there a better way to determine if a dictionary has a mapping for a given key?
haskey will check whether some collection has a mapping for a given key:
julia> d
Dict{Int64,String} with 2 entries:
2 => "two"
1 => "one"
julia> haskey(d, 1)
true
julia> haskey(d, 3)
false
Another way, which might be viable depending on your use case, is to use get to provide a default value in case the key is not present:
julia> d = Dict(1 => "one", 2 => "two")
Dict{Int64,String} with 2 entries:
2 => "two"
1 => "one"
julia> get(d, 1, "zero")
"one"
julia> get(d, 3, "zero")
"zero"
There's also get!, which will store the default value for the queried key as well.
I have two separate dictionaries defined. I am wondering if there is an elegant, single expression way that I can merge these two dictionaries into one?
Yes, using the merge() function. Here's a quick example:
julia> a = Dict("One" => 1.0, "Two" => 2.0)
Dict{String,Float64} with 2 entries:
"One" => 1.0
"Two" => 2.0
julia> b = Dict("Three" => 3, "Four" => 4)
Dict{String,Int64} with 2 entries:
"Three" => 3
"Four" => 4
julia> c = merge(a, b)
Dict{String,Float64} with 4 entries:
"One" => 1.0
"Two" => 2.0
"Three" => 3.0
"Four" => 4.0
See the Julia docs here for more examples and functionality related to merge(). The merge function creates a new, separate dictionary and returns it. There is also a mutating merge! function which modifies its first argument:
julia> merge!(a, b)
Dict{String,Float64} with 4 entries:
"One" => 1.0
"Two" => 2.0
"Three" => 3.0
"Four" => 4.0
julia> a
Dict{String,Float64} with 4 entries:
"One" => 1.0
"Two" => 2.0
"Three" => 3.0
"Four" => 4.0
Note that merge! returns the merged array which is the original dictionary a modified.
I want to unpack parameters that are stored in a dictionary. They should be available inside the local scope of a function afterwards. The name should be the same as the key which is a symbol.
macro unpack_dict()
code = :()
for (k,v) in dict
ex = :($k = $v)
code = quote
$code
$ex
end
end
return esc(code)
end
function assign_parameters(dict::Dict{Symbol, T}) where T<:Any
#unpack_dict
return a + b - c
end
dict = Dict(:a => 1,
:b => 5,
:c => 6)
assign_parameters(dict)
However, this code throws:
LoadError: UndefVarError: dict not defined
If I define the dictionary before the macro it works because the dictionary is defined.
Does someone has an idea how to solve this? Using eval() works but is evaluated in the global scope what I want to avoid.
If you want to unpack them then the best method is to simply unpack them directly:
function actual_fun(d)
a = d[:a]
b = d[:b]
c = d[:c]
a+b+c
end
This will be type stable, relatively fast and readable.
You could, for instance, do something like this (I present you two options to avoid direct assignment to a, b, and c variables):
called_fun(d) = helper(;d...)
helper(;kw...) = actual_fun(;values(kw)...)
actual_fun(;a,b,c, kw...) = a+b+c
function called_fun2(d::Dict{T,S}) where {T,S}
actual_fun(;NamedTuple{Tuple(keys(d)), NTuple{length(d), S}}(values(d))...)
end
and now you can write something like:
julia> d = Dict(:a=>1, :b=>2, :c=>3, :d=>4)
Dict{Symbol,Int64} with 4 entries:
:a => 1
:b => 2
:d => 4
:c => 3
julia> called_fun(d)
6
julia> called_fun2(d)
6
But I would not recommend it - it is not type stable and not very readable.
AFACT other possibilities will have similar shortcomings as during compile time Julia knows only types of variables not their values.
EDIT: You can do something like this:
function unpack_dict(dict)
ex = :()
for (k,v) in dict
ex = :($ex; $k = $v)
end
return :(myfun() = ($ex; a+b+c))
end
runner(d) = eval(unpack_dict(d))
and then run:
julia> d = Dict(:a=>1, :b=>2, :c=>3, :d=>4)
Dict{Symbol,Int64} with 4 entries:
:a => 1
:b => 2
:d => 4
:c => 3
julia> runner(d)
myfun (generic function with 1 method)
julia> myfun()
6
but again - I feel this is a bit messy.
In Julia 1.7, you can simply unpack named tuples into the local scope, and you can easily "spread" a dict into a named tuple.
julia> dict = Dict(:a => 1, :b => 5, :c => 6)
Dict{Symbol, Int64} with 3 entries:
:a => 1
:b => 5
:c => 6
julia> (; a, b, c) = (; sort(dict)...)
(a = 1, b = 5, c = 6)
julia> a, b, c
(1, 5, 6)
(The dict keys are sorted so that the named tuple produced is type-stable; if the keys were produced in arbitrary order then this would result in a named tuple with fields in arbitrary order as well.)