Array printing of custom objects

I defined the following object and show() methods:

# Custom Set

struct CustomSet{T}
    dict::Dict{T, Nothing}
end

CustomSet(xs) = CustomSet(Dict(zip(xs, fill(nothing, length(xs)))))

# default show used by print
function Base.show(io::IO, cs::CustomSet)
    print(io, "CustomSet($(collect(keys(cs.dict))))")
end

# default show used by display() on the REPL
function Base.show(io::IO, mime::MIME"text/plain", cs::CustomSet)
    print(io, "$(length(cs.dict))-element $(typeof(cs)):\n")
    for i ∈ keys(cs.dict) println(i) end
end

The behavior is mostly as I would like it to be:

julia> s = CustomSet([1,2,3,4])
4-element CustomSet{Int64}:
4
2
3
1


julia> print(s)
CustomSet([4, 2, 3, 1])
julia> (s, s)
(CustomSet([4, 2, 3, 1]), CustomSet([4, 2, 3, 1]))

Except when printing an array containing a CustomSet type:

julia> [s]
1-element Vector{CustomSet{Int64}}:
 4
2
3
1
CustomSet([4, 2, 3, 1])

julia> [s, s]
2-element Vector{CustomSet{Int64}}:
 4
2
3
1
CustomSet([4, 2, 3, 1])
 4
2
3
1
CustomSet([4, 2, 3, 1])

I would like for this to print more like a Set object:

julia> t = Set([1,2,3,4])
Set{Int64} with 4 elements:
  4
  2
  3
  1

julia> [t, t]
2-element Vector{Set{Int64}}:
 Set([4, 2, 3, 1])
 Set([4, 2, 3, 1])

What is the best way to achieve this? Also, is this the typical pattern for defining show() methods for custom objects, or is there a better way?

One small bug is you need the println in the loop to print to io:

    for i ∈ keys(cs.dict) println(io, i) end

This will fix most of your issues.

You might also want to check if the IOContext has already printed type info so you can avoid having type info for each element of an array.

The spacing is also a little better with something like:

function Base.show(io::IO, ::MIME"text/plain", cs::CustomSet)
    print(io, "$(length(cs.dict))-element $(typeof(cs)):\n ")
    print(io, join(keys(cs.dict), "\n "))
end

The other IOContext elements are also useful for displaying very large sets in a REPL-friendly way.

3 Likes