stringgoformatcommand-line-interfacetui

How to print a 2-columns table in Golang?


Kind of stuck with this. My idea is having a function that prints a two-columns table. The first one is for keys, and it has a fixed width. The second one is for values, which may be very long strings, and its width depends on the current width of the terminal.

An example of what I'd like to have:

Key1                                  Value1Value1Value1Value1
Key2                                  Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2
                                      Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2
                                      Value2Value2Value2Value2Value2Value2

So far, the best I achieved is to have a fixed width for the first columns, using lipgloss.

func PrintMetadata(metadata map[string]string, color string) {
    style := lipgloss.NewStyle().Width(32).Foreground(lipgloss.Color(color))
    for k, v := range metadata {
        fmt.Println(style.Render(k) + v)
    }
}

The result of which is something like:

Key1                                  Value1Value1Value1Value1
Key2                                  Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2

So, how can I format a string in the way I want? I can use both standard and external libraries, so any suggestions are welcome.


Solution

  • I made a function for this. This function has two parameters first for the map variable for columning, second is for how many characters do you want to fill to per line. It simply change key's value contents with spacing into new variable then prints that key, value. But if you have works with unmodified value you can use unmodified variable.

    package main
    
    import (
        "fmt"
        "strings"
        "sort"
    )
    
    func main() {
        a := map[string]string{
        "Key1": strings.Repeat("Value1", 50), 
        "Key2": strings.Repeat("Value2", 50), 
        "Key3": strings.Repeat("Value3", 50),
        }
        
        columner(a, 30)
    }
    
    func columner(m map[string]string, charAmount int) {
    
        var keys []string
        
        var keyLens []int
        
        // to avoid index panics and gathering keys for later usage
        for key, value := range m {
            if charAmount > len(value) || charAmount < 1{
                value += strings.Repeat(" ", charAmount-len(value))
                m[key] = value
            }
            keys = append(keys, key)
            keyLens = append(keyLens, len(key))
        }
    
        sort.Ints(keyLens)
        
        for i := 0; i < len(keys); i++ {
            
            // for storing updated value of key
            var value2 string
            
            value := m[keys[i]]
            // will used while extracting substring of key's value as first index
            firstI := 0
            
            // last index for extract substring from key's value. the len of substring will be same as charAmount
            charAmount2 := charAmount
            
            // will be used to advance next substring of key's value
            advance := charAmount2
            
            // spaces between between key and value 
            // key       value
            spacing := strings.Repeat(" ", 20 + (keyLens[0] - len(keys[i])))
            
            // var for adjusting spaces of gap between key and value of next line
            // key        value
            //          value
            // to
            // key        value
            //            value
            spacingU := spacing + strings.Repeat(" ", len(keys[i]) + 1)
            
            // this loop will be run as long as there is no substring left which exceed next line
            for j := 0; j < len(value); j += advance {
                
                // adjusting spaces of gap between key and value of next line
                if j > 0 {
                    spacing = spacingU
                }
                
                // add space between key and value, then extract substring, then add spaces to the next line of the
                // next substring of key's value
                value2 += spacing + value[firstI:charAmount2] + "\n"
                
                // finish loop when there is no substring that can be exceed to next line
                if ((len(value) - charAmount2) < advance) || ((len(value) - charAmount2) == advance) {
                    break
                }
        
                // changing first index to start index of next substring of key's value
                firstI = charAmount2
                
                // advancing to next substring of key's value
                charAmount2 += advance
            }   
            
            // add last remaining substring of key's value to variable which will be show as formatted.
            value2 += spacing + value[charAmount2:]
    
            // show formatted key and value
            fmt.Println(keys[i], value2, "\n")
            
        }
    }
    

    This is a example output:

    Key1                     Value1Value1Value1Value1Value1
                             Value1Value1Value1Value1Value1
                             Value1Value1Value1Value1Value1
                             Value1Value1Value1Value1Value1
                             Value1Value1Value1Value1Value1
                             Value1Value1Value1Value1Value1
                             Value1Value1Value1Value1Value1
                             Value1Value1Value1Value1Value1
                             Value1Value1Value1Value1Value1
                             Value1Value1Value1Value1Value1 
    
    Key2                     Value2Value2Value2Value2Value2
                             Value2Value2Value2Value2Value2
                             Value2Value2Value2Value2Value2
                             Value2Value2Value2Value2Value2
                             Value2Value2Value2Value2Value2
                             Value2Value2Value2Value2Value2
                             Value2Value2Value2Value2Value2
                             Value2Value2Value2Value2Value2
                             Value2Value2Value2Value2Value2
                             Value2Value2Value2Value2Value2 
    
    Key3                     Value3Value3Value3Value3Value3
                             Value3Value3Value3Value3Value3
                             Value3Value3Value3Value3Value3
                             Value3Value3Value3Value3Value3
                             Value3Value3Value3Value3Value3
                             Value3Value3Value3Value3Value3
                             Value3Value3Value3Value3Value3
                             Value3Value3Value3Value3Value3
                             Value3Value3Value3Value3Value3
                             Value3Value3Value3Value3Value3 
    

    But please notice this, order of key and values may be different on each execution because map type is unordered when printed in for loop with key, value pair.