function filterarrays!(d)
for arr in [d["a"], d["b"]]
deleteat!(arr, 2)
end
end
d = Dict("a" => [1, 2, 3], "b" => [1., 2., 3.])
display(d)
filterarrays!(d)
display(d)
With output:
Dict{String, Vector} with 2 entries:
"b" => [1.0, 2.0, 3.0]
"a" => [1, 2, 3]
Dict{String, Vector} with 2 entries:
"b" => [1.0, 3.0]
"a" => [1, 2, 3]
In the case of the b key of dictionary d, the middle array element is removed as expected. For the values of the a key, no elements are removed. I’m not sure if the reason for the difference is related to this topic, but in the example above the two arrays have different types and the output for the Float64 array (b) is as I would expect.
Can anyone explain why the loop behavior is different here?
Note that you could alternatively use a Tuple, which also saves you an allocation:
function filterarrays!(d)
for arr in (d["a"], d["b"]) # <-- tuple here
deleteat!(arr, 2)
end
end
d = Dict("a" => [1, 2, 3], "b" => [1., 2., 3.])
filterarrays!(d)
display(d)
# Dict{String, Vector} with 2 entries:
# "b" => [1.0, 3.0]
# "a" => [1, 3]
This seems like a bug to me. Is the statement [d["b"], d["a"]] making a copy of d["a"] with elements promoted to Float64 and leaving d["b"] as a reference to to the original array?
Arrays can also be directly constructed with square braces; the syntax [A, B, C, ...] creates a one-dimensional array (i.e., a vector) containing the comma-separated arguments as its elements. The element type (eltype) of the resulting array is automatically determined by the types of the arguments inside the braces. If all the arguments are the same type, then that is its eltype. If they all have a common promotion type then they get converted to that type using convert and that type is the array’s eltype. Otherwise, a heterogeneous array that can hold anything — a Vector{Any} — is constructed; this includes the literal [] where no arguments are given. Array literal can be typed with the syntax T[A, B, C, ...] where T is a type.
(emphasis mine),
help?> convert
search: convert Base.cconvert const collect
convert(T, x)
(...)
If T is a collection type and x a collection, the result of convert(T, x) may alias all or part of x.
julia> x = Int[1, 2, 3];
julia> y = convert(Vector{Int}, x);
julia> y === x
true
I think the conversion to Float64 is okay (and documented like you mention), but it does not seem correct that a copy is made for d["a"] with eltype Float64 while a reference is maintained to the d["b"]. Maybe it is correct behaviour.
I agree it can lead to unintuitive behaviour as in this thread, but it is the logical consequence of the requirements that
You want to promote to a common type (ultimately via convert) in array literals
You want convert(typeof(x), x) === x.
You might disagree with these requirements (though 2 seems pretty essential), but they are certainly intentional design choices. So yeah, I’d say this is correct behaviour.