Incrementally pretty print a table, one row at a time

luke-kiernan
Tue, 29 Apr 2025 23:36:56 +0000
Performance

I’m comparing some Newton’s method-type iterative solvers. I’d like to pretty print the progress of the iterative method, so I can tell if the search is making good progress. If I didn’t care about performance, I’d just store it all to one big DataFrame, then print it at the end. But I do care about performance. Is there a way to feed PrettyTables.jl data one row at a time, instead of all at once?

I can give it the headers and the max width of each column at the start. If I’m limited to printing strings of max width N and need to handle the rest myself (round/truncate), that would also be acceptable.

I could write my own crude plaintext table printer…but then I’d have to go think about buffers, performance, etc.

Edit: I guess this gets the job done. Have not profiled it, though. Allocating words here is potentially slow.

"""A simple plaintext table writer: always left-aligned.
N is number of columns and W the width of each column."""
struct TableWriter{N, W}
    bf::IOStream
end

function TableWriter{N, W}(bf::IOStream, headers::NTuple{N, String}) where {N, W}
    t = TableWriter{N, W}(bf)
    write_row_separator(t)
    write_row_content(t, headers)
    return t
end

function write_row_separator(t::TableWriter{N, W}) where {N, W}
    write(t.bf, '+')
    for _ in 1:N
        for _ in 1:W
            write(t.bf, '-')
        end
        write(t.bf, '+')
    end
    write(t.bf, '\n')
end

function write_row_content(t::TableWriter{N, W}, words::NTuple{N, String}) where {N, W}
    write(t.bf, '|')
    for word in words
        write(t.bf, word)
        for _ in 1:(W - length(word))
            write(t.bf, ' ')
        end
        write(t.bf, '|')
    end
    write(t.bf, '\n')
end

function write_row(t::TableWriter{N, W}, words::NTuple{N, String}) where {N, W}
    write_row_separator(t)
    write_row_content(t, words)
end

function write_row(t::TableWriter{N, W}, nums::NTuple{N, Float64}) where {N, W}
    f = x -> round(x; sigdigits = 3)
    words = NTuple{N, String}(string.(f.(nums)))
    write_row_separator(t)
    write_row_content(t, words)
end

write_row(t::TableWriter{N, W}, nums::Tuple) where {N, W} = write_row(t, Float64.(nums))

open("test.txt", "w") do filehandle
    headers = NTuple{2,String}(["V_θ", "V_m"])
    t = TableWriter{2, 10}(filehandle, headers)
    write_row(t, (1.5, 2.5))
    write_row(t, (13, 5.0))
end

2 posts - 2 participants