Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 1x 1x 1x 1x 3x 9x 9x 9x 9x 9x 9x 9x 9x 9x 3x 1x 1x 1x 1x 3x 3x 1x 1x 12x 12x 12x 12x 12x 12x 12x 12x 12x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | type CellFormatter = (str: string, rowIndex: number, colIndex: number) => any
 
export interface AlignedTextTableOptions {
    alignRight?: boolean
    maxCharactersPerColumn?: number
    maxCharactersPerLine?: number
}
 
// Output a pretty table for consles
export const toAlignedTextTable = (
    headerSlugs: string[],
    rows: any[],
    options: AlignedTextTableOptions = {}
): string => {
    const {
        alignRight = true,
        maxCharactersPerColumn = 20,
        maxCharactersPerLine = 80,
    } = options
 
    // Set initial column widths
    const colWidths = headerSlugs.map((slug) =>
        slug.length > maxCharactersPerColumn
            ? maxCharactersPerColumn
            : slug.length
    )
 
    // Expand column widths if needed
    rows.forEach((row) => {
        headerSlugs.forEach((slug, index) => {
            const cellValue = row[slug]
            if (!cellValue) return
            const length = cellValue.toString().length
            if (length > colWidths[index])
                colWidths[index] =
                    length > maxCharactersPerColumn
                        ? maxCharactersPerColumn
                        : length
        })
    })
 
    // Drop columns if they exceed the max line width
    let runningWidth = 0
    const finalHeaderSlugs = headerSlugs.filter((slug, index) => {
        runningWidth = runningWidth + colWidths[index]
        if (runningWidth <= maxCharactersPerLine) return true
        return false
    })
 
    const cellFn = (cellText = "", row: number, col: number) => {
        const width = colWidths[col]
        // Strip newlines in fixedWidth output
        const cellValue = cellText?.toString().replace(/\n/g, "\\n") || ""
        const cellLength = cellValue.length
        if (cellLength > width) return cellValue.substr(0, width - 3) + "..."
 
        const padding = " ".repeat(width - cellLength)
        return alignRight ? padding + cellValue : cellValue + padding
    }
    return (
        (finalHeaderSlugs.length !== headerSlugs.length
            ? `Showing ${finalHeaderSlugs.length} of ${headerSlugs.length} columns\n`
            : "") + toDelimited(" ", finalHeaderSlugs, rows, cellFn)
    )
}
 
export const toMarkdownTable = (
    slugs: string[],
    rows: any[],
    formatFn?: CellFormatter
): string =>
    [
        slugs,
        slugs.map(() => "-"),
        ...rows.map((row) => slugs.map((slug) => row[slug])),
    ]
        .map((row, rowIndex) => {
            const formattedValues = row.map((val, colIndex) =>
                formatFn ? formatFn(val, rowIndex, colIndex) : val
            )
            return `|${formattedValues.join("|")}|`
        })
        .join("\n")
 
export const toDelimited = (
    delimiter: string,
    columnSlugs: string[],
    rows: any[],
    cellFn?: CellFormatter,
    rowDelimiter = "\n"
): string => {
    const skipHeaderRow = 1
    const header = columnSlugs.map((columnName, index) =>
        cellFn ? cellFn(columnName, 0, index) : columnName
    )
    const formattedRows = rows.map((row, rowNumber) =>
        columnSlugs.map((slug, columnIndex) =>
            cellFn
                ? cellFn(row[slug], rowNumber + skipHeaderRow, columnIndex)
                : row[slug]
        )
    )
 
    return (
        header.join(delimiter) +
        rowDelimiter +
        formattedRows.map((row) => row.join(delimiter)).join(rowDelimiter)
    )
}
  |