# Optional elements in vector literal

is there a nice way to optionally include an element in a vector literal construction? something like `foo = [1, 2, @maybe x 3, 4, 5]` where only if `x==true` is 3 included.

answering myself, i suppose `insert!` is good enough: `foo=[1,2,4,5]; insert!(foo, 3, 3)`

This may depend on what you mean by βliteralβ. `insert!` is not really a literal syntax because a temporary array is getting constructed and then manipulated. For a true literal, we would want to achieve the result at construction.

``````julia> x = true
true

julia> foo = [y for (i,y) in pairs(1:5) if i!=3 || x]
5-element Vector{Int64}:
1
2
3
4
5

julia> x = false
false

julia> foo = [y for (i,y) in pairs(1:5) if i!=3 || x]
4-element Vector{Int64}:
1
2
4
5

julia> f(x) = [y for (i,y) in pairs(1:5) if i!=3 || x]
f (generic function with 2 methods)

julia> @code_lowered f(true)
CodeInfo(
1 β       #33 = %new(Main.:(var"#33#35"))
β   %2  = #33
β   %3  = Main.:(var"#34#36")
β   %4  = Core.typeof(x)
β   %5  = Core.apply_type(%3, %4)
β         #34 = %new(%5, x)
β   %7  = #34
β   %8  = 1:5
β   %9  = Main.pairs(%8)
β   %10 = Base.Filter(%7, %9)
β   %11 = Base.Generator(%2, %10)
β   %12 = Base.collect(%11)
βββ       return %12
)
``````

The array comprehension syntax gets lowered to several layers of lazy iterators before being collected. Only at collection is the array allocated and formed. Notably see `Iterators.filter` which creates a `Iterators.Filter`.

This also works:

``````using Base.Iterators

g(x) = collect(flatten([[1,2],x ? [3] : Int[],[4,5]]))
``````

and

``````julia> g(false) == [1,2,4,5]
true

julia> g(true) == [1,2,3,4,5]
true
``````

With this method, in case `x` is false, the unincluded element is not even materialized or computed (especially important for complex elements).

1 Like

Using `Iterators.flatten` is nice. Do note that a type assertion might be good for the return type since the method is currently type unstable. The array comprehension is type stable.

``````julia> f(x) = [y for (i,y) in pairs(1:5) if i!=3 || x]
f (generic function with 2 methods)

julia> @code_warntype f(true)
MethodInstance for f(::Bool)
from f(x) in Main at REPL[114]:1
Arguments
#self#::Core.Const(f)
x::Bool
Locals
#50::var"#50#52"{Bool}
#49::var"#49#51"
Body::Vector{Int64}
1 β       (#49 = %new(Main.:(var"#49#51")))
β   %2  = #49::Core.Const(var"#49#51"())
β   %3  = Main.:(var"#50#52")::Core.Const(var"#50#52")
β   %4  = Core.typeof(x)::Core.Const(Bool)
β   %5  = Core.apply_type(%3, %4)::Core.Const(var"#50#52"{Bool})
β         (#50 = %new(%5, x))
β   %7  = #50::var"#50#52"{Bool}
β   %8  = (1:5)::Core.Const(1:5)
β   %9  = Main.pairs(%8)::Core.Const(Base.Pairs(1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5))
β   %10 = Base.Filter(%7, %9)::Core.PartialStruct(Base.Iterators.Filter{var"#50#52"{Bool}, Base.Pairs{Int64, Int64, LinearIndices{1, Tuple{Base.OneTo{Int64}}}, UnitRange{Int64}}}, Any[var"#50#52"{Bool}, Core.Const(Base.Pairs(1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5))])
β   %11 = Base.Generator(%2, %10)::Core.PartialStruct(Base.Generator{Base.Iterators.Filter{var"#50#52"{Bool}, Base.Pairs{Int64, Int64, LinearIndices{1, Tuple{Base.OneTo{Int64}}}, UnitRange{Int64}}}, var"#49#51"}, Any[Core.Const(var"#49#51"()), Core.PartialStruct(Base.Iterators.Filter{var"#50#52"{Bool}, Base.Pairs{Int64, Int64, LinearIndices{1, Tuple{Base.OneTo{Int64}}}, UnitRange{Int64}}}, Any[var"#50#52"{Bool}, Core.Const(Base.Pairs(1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5))])])
β   %12 = Base.collect(%11)::Vector{Int64}
βββ       return %12

julia> g(x) = collect(flatten(( (1,2), x ? (3,) : (), (4,5) )))
g (generic function with 1 method)

julia> @code_warntype g(true)
MethodInstance for g(::Bool)
from g(x) in Main at REPL[116]:1
Arguments
#self#::Core.Const(g)
x::Bool
Locals
@_3::Union{Tuple{}, Tuple{Int64}}
Body::Any
1 β %1  = Core.tuple(1, 2)::Core.Const((1, 2))
βββ       goto #3 if not x
2 β       (@_3 = Core.tuple(3))
βββ       goto #4
3 β       (@_3 = ())
4 β %6  = @_3::Union{Tuple{}, Tuple{Int64}}
β   %7  = Core.tuple(4, 5)::Core.Const((4, 5))
β   %8  = Core.tuple(%1, %6, %7)::Core.PartialStruct(Tuple{Tuple{Int64, Int64}, Union{Tuple{}, Tuple{Int64}}, Tuple{Int64, Int64}}, Any[Core.Const((1, 2)), Union{Tuple{}, Tuple{Int64}}, Core.Const((4, 5))])
β   %9  = Main.flatten(%8)::Base.Iterators.Flatten
β   %10 = Main.collect(%9)::Any
βββ       return %10
``````

Yes, youβre right, I thought the `Int[]` in the definition would be enough for inference, but adding `Vector{Int}` is enough:

``````g(x) = collect(flatten(Vector{Int}[[1,2],x ? [3] : Int[],[4,5]]))
``````

and:

``````julia> @code_warntype g(false)
...
β   %11 = Main.collect(%10)::Vector{Int64}
βββ       return %11
``````

Actually, the problem was with the tuples, which made `flatten` unstable. Perhaps a good compromise is:

``````g(x) = collect(flatten(( (1,2), x ? [3] : Int[], (4,5) )))
``````

which is type stable.