Baratz96
Baratz96

Reputation: 27

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.

Upvotes: 1

Views: 985

Answers (1)

user18572278
user18572278

Reputation:

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.

Upvotes: 1

Related Questions