Printing vector of vectors with :compact output

Following the docs example, we get matrices to be printed with the “compact” form of the elements:

julia> [Polar(3, 4.0) Polar(4.0,5.3)]
1×2 Matrix{Polar{Float64}}:
 3.0ℯ4.0im  4.0ℯ5.3im

and also with a dict with vector values, which is good because the vectors are “horizontal”:

julia> Dict(1 => [Polar(3, 4.0), Polar(4.0,5.3)])
Dict{Int64, Vector{Polar{Float64}}} with 1 entry:
  1 => [3.0ℯ4.0im, 4.0ℯ5.3im]

However, a vector of vectors uses the extended form:

julia> [[Polar(3, 4.0), Polar(4.0,5.3)]]
1-element Vector{Vector{Polar{Float64}}}:
 [3.0 * exp(4.0im), 4.0 * exp(5.3im)]

besides also being an “horizontal” printing of the vector.

Is there a reasonably simple solution to that?

One idea, following the same docs example:

function println_vec(io::IO, v::Vector{Polar{Float64}})
    iob = IOBuffer()
    show(IOContext(iob, :compact=>true), v)
    str = String(take!(iob))
    ('[' in str) && (str = '[' * last(split(str,'[')))
    println(io, str)
end

function Base.show(io::IO, ::MIME"text/plain", vv::Vector{Vector{Polar{Float64}}})
    println(io, "$(length(vv))-element Vector{Vector{Polar{Float64}}}:")
    foreach(v -> println_vec(io, v), vv)
end

vv = [[Polar(3.0, 4.0), Polar(4.0, 5.3)], [Polar(-1.0, 1.0), Polar(0.0, -2)]]

# Result:
2-element Vector{Vector{Polar{Float64}}}:
[3.0ℯ4.0im, 4.0ℯ5.3im]
[-1.0ℯ1.0im, 0.0ℯ-2.0im]
1 Like

Where in the manual did you find this? (or how else did you find it?)

Not really from the manual, but from the scattered tips that the masters reveal here in Discourse or SO and that I try to put together. For part of that code snippet see here.

1 Like

Now, for example, if following the same example I want to print a vector of matrices, the problem occurs again:

julia> [Polar(3, 4.0) Polar(4.0,5.3)] # matrix is printed in compact form
1×2 Matrix{Polar{Float64}}:
 3.0ℯ4.0im  4.0ℯ5.3im

julia>  [ [Polar(3, 4.0) Polar(4.0,5.3)] ] # but inside a vector it expands
1-element Vector{Matrix{Polar{Float64}}}:
 [3.0 * exp(4.0im) 4.0 * exp(5.3im)]

I understand that the outer vector sets the context of the IO and that affects the printing of the inner matrix. Is the solution really to implement, now, a custom vector-of-matrices printing routine? That does not seem to end up with a consistent behavior.

(specifically, it seems that the form of the printing should be define by the immediately upper-level container, but that of course would create issues for customization).

Is there a better way?

I don’t think defining methods like Base.show(io::IO, ::MIME"text/plain", vv::Vector{Vector{Polar{Float64}}}) is a good idea; Base already defines show for Vector, so this is piracy IIUC, and breaks composability as you pointed out.

I think probably the fix is for Base to set compact to true when printing elements. It seems to do so conditionally currently (julia/base/arrayshow.jl at 7ef92c8876a82fc0a649a1d964a78bbf0782a704 · JuliaLang/julia · GitHub), maybe that condition can be extended?

1 Like

If the piracy pointed out by Eric is acceptable, we could extend the methods for vectors and matrices using type unions:

function println_vec(io::IO, u::Union{Vector{T}, Matrix{T}}) where T
    iob = IOBuffer()
    show(IOContext(iob, :compact=>true), u)
    str = String(take!(iob))
    ('[' in str) && (str = '[' * last(split(str,'[')))
    println(io, str)
end

function Base.show(io::IO, ::MIME"text/plain", vv::Union{Vector{Vector{T}}, Vector{Matrix{T}}}) where T
    if eltype(vv) == Vector{T}
        println(io, "$(length(vv))-element Vector{Vector{$T}}:")
    elseif eltype(vv) == Matrix{T}
        println(io, "$(length(vm))-element Vector{Matrix{$T}}:")
    end
    foreach(v -> println_vec(io, v), vv)
end

# Example:
vm = [ [Polar(3, 4.0) Polar(4.0, 5.3); Polar(-1, 0.0) Polar(2.0, -3.1)]]

1-element Vector{Matrix{Polar{Float64}}}:
[3.0ℯ4.0im 4.0ℯ5.3im; -1.0ℯ0.0im 2.0ℯ-3.1im]

No, that’s not piracy if one own the element type. But the issue is the consistency when printing nested containers.