cojua8
December 11, 2020, 1:48pm
1
Hi,
I have an array of strings, which are replaced by ones or missings
julia> my_array = ["a", "b"]
2-element Array{String,1}:
"a"
"b"
julia> my_replaced_array = replace(my_array, "a" => true, "b" => missing)
2-element Array{Any,1}:
true
missing
but as you can see, the element type is now Any, instead of the desired Union{Missing, Bool}.
However, if I call an operation:
julia> .!my_replaced_array
2-element Array{Union{Missing, Bool},1}:
false
missing
The return type is the expected.
My question is if there is a way to get the right type of the elements of the replaced array from the elements of the array itself
One option is to convert after the replacement:
julia> my_replaced_array = Vector{Union{Bool,Missing}}(replace(my_array, "a" => true, "b" => missing))
2-element Array{Union{Missing, Bool},1}:
true
missing
I’m not sure it is the most efficient approach, though.
cojua8
December 11, 2020, 3:03pm
3
I’m looking for something more robust, which can work with any types.
But yeah, I think that is the solution for real cases, no need to develop a general case solution
Skoffer
December 11, 2020, 3:10pm
4
You can try this trick
identity.(replace(my_array, "a" => true, "b" => missing))
4 Likes
Feels a bit clunky but seems to work:
julia> function replace2(x, pairs...)
types = [pairs[j][2] for j = 1 : length(pairs)]
T = eltype(types);
Vector{T}(replace(x, pairs...));
end
replace2 (generic function with 1 method)
julia> replace2(my_array, "a" => true, "b" => missing)
2-element Array{Union{Missing, Bool},1}:
true
missing
1 Like
julia> collect([true,missing])
2-element Vector{Union{Missing, Bool}}:
true
missing
how about this?
Note that [true, missing]
is already of that element type:
julia> [true,missing]
2-element Array{Union{Missing, Bool},1}:
true
missing
and that collect
doesn’t actually do the type narrowing:
julia> collect(Any[true,missing])
2-element Array{Any,1}:
true
missing
The identity.(...)
“trick” is probably the most concise.
7 Likes
This has come up before, though I can’t find the exact threads. It might be worth having something in Base
which takes an arbitrary array and converts it into the type that one would get from [a, b, c]
julia> t = Any[1, 2, 3]
3-element Array{Any,1}:
1
2
3
julia> collect(t)
3-element Array{Any,1}:
1
2
3
julia> identity.(t)
3-element Array{Int64,1}:
1
2
3
julia> t = Any[1, 2, 3, 5.6]
4-element Array{Any,1}:
1
2
3
5.6
julia> identity.(t)
4-element Array{Real,1}:
1
2
3
5.6
However, identity
does not seem to work in all cases. For example:
julia> identity.([true, 1])
2-element Array{Int64,1}:
1
1
Note that the [
syntax already does type promotion there, ie
julia> [true, 1]
2-element Array{Int64,1}:
1
1
so identity.(...)
is a no-op. But if you are a bit more careful about inputs, you will see that
julia> identity.(Any[1, true])
2-element Array{Integer,1}:
1
true
works fine.
3 Likes
I guess what “works” means is ambiguous here. I expected the return type to be Vector{Union{Bool,Int64}}
instead of Vector{Int}
. But, yes, that’s a good point.
But, if you read carefully, you will see that you got Vector{Integer}
, not Vector{Int}
. Integer
is a supertype for both Int
and Bool
.
5 Likes
Right - so it depends on whether the expected return type should be a Union of concrete types or their common supertype. Or is there a reason why the answer should always be the latter?
The solution is to do a pass-through of the data first using mapreduce(typeof, promote_typejoin, x)
and then fill in an array with that type, I think.
I don’t think that there is a solution that is generally the preferred one — it depends on various circumstances. Eg if I know there are a “few” concrete types, I would prefer Union
, but if there are many, I would hope that the implementation gives up sooner.