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
```

```
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.