Skip to content

Struct Inference

Learn how goTableView can automatically detect columns from your Go structs, saving you time and reducing errors.

What You'll Learn

  • Automatic column detection from structs
  • Using struct tags to customize columns
  • Working with different data types
  • Handling maps and complex data

The Problem

Manually building tables from structs is tedious:

go
type Employee struct {
    Name       string
    Department string
    Age        int
    Salary     float64
}

employees := []Employee{
    {"Alice", "Engineering", 30, 75000.00},
    {"Bob", "Sales", 25, 60000.00},
}

// Manual approach - repetitive and error-prone ❌
table := gotableview.New("Employees")
table.Columns("Name", "Department", "Age", "Salary")
for _, emp := range employees {
    table.Row(
        emp.Name,
        emp.Department,
        fmt.Sprintf("%d", emp.Age),
        fmt.Sprintf("%.2f", emp.Salary),
    )
}
table.Show()

The Solution: FromStructs()

Let goTableView do the work for you:

go
type Employee struct {
    Name       string
    Department string
    Age        int
    Salary     float64
}

employees := []Employee{
    {"Alice", "Engineering", 30, 75000.00},
    {"Bob", "Sales", 25, 60000.00},
}

// Automatic approach - clean and simple ✅
gotableview.FromStructs("Employees", employees).Show()
goTableView basic usage

How It Works

FromStructs() uses Go's reflection to:

  1. Detect all exported struct fields
  2. Use field names as column headers
  3. Automatically format values based on their types

Basic Struct Inference

Simple Example

go
package main

import "github.com/mansoldof/goTableView"

type Product struct {
    ID    int
    Name  string
    Price float64
    Stock int
}

func main() {
    products := []Product{
        {1, "Laptop", 999.99, 15},
        {2, "Mouse", 24.99, 50},
        {3, "Keyboard", 74.99, 30},
    }
    
    gotableview.FromStructs("Product Inventory", products).Show()
}

Result:

  • Columns: ID, Name, Price, Stock
  • Price automatically formatted to 2 decimal places
  • No manual conversion needed!

Automatic Type Formatting

goTableView handles common types intelligently:

go
type Demo struct {
    Text      string
    Integer   int
    Decimal   float64
    IsActive  bool
    Timestamp time.Time
}

demo := []Demo{
    {"Hello", 42, 3.14159, true, time.Now()},
    {"World", 100, 2.71828, false, time.Now().Add(-24 * time.Hour)},
}

gotableview.FromStructs("Type Demo", demo).Show()

Type Formatting:

  • string → As-is
  • int, int8, int16, int32, int64 → Integer format
  • uint, uint8, uint16, uint32, uint64 → Unsigned integer
  • float32, float64 → Two decimal places (e.g., "3.14")
  • bool → "Yes" or "No"
  • time.Time → "2006-01-02 15:04:05"
  • nil pointers → Empty string

Customizing with Struct Tags

Use the gotableview tag to control column headers:

Basic Tag Usage

go
type Product struct {
    ID          int     `gotableview:"Product ID"`
    Name        string  `gotableview:"Product Name"`
    Price       float64 `gotableview:"Price ($)"`
    InStock     bool    `gotableview:"Available"`
    Description string  `gotableview:"Details"`
}

products := []Product{
    {1, "Laptop", 999.99, true, "High-performance laptop"},
    {2, "Mouse", 24.99, true, "Wireless mouse"},
}

gotableview.FromStructs("Products", products).Show()

Result:

  • Column headers: Product ID, Product Name, Price ($), Available, Details
  • Much more user-friendly than default field names!
goTableView basic usage

Skipping Fields

Use gotableview:"-" to hide fields:

go
type User struct {
    ID       int    `gotableview:"User ID"`
    Name     string `gotableview:"Full Name"`
    Email    string `gotableview:"Email Address"`
    Password string `gotableview:"-"`  // Hidden from table
    APIKey   string `gotableview:"-"`  // Hidden from table
    Role     string `gotableview:"Role"`
}

users := []User{
    {1, "Alice Johnson", "alice@example.com", "secret123", "key-xyz", "Admin"},
    {2, "Bob Smith", "bob@example.com", "pass456", "key-abc", "User"},
}

gotableview.FromStructs("Users", users).Show()

Result:

  • Columns shown: User ID, Full Name, Email Address, Role
  • Password and APIKey are not displayed

Security Note

Even though sensitive fields are hidden from the table, they're still in memory. Don't use this as a security measure - properly handle sensitive data in your application.

Working with Pointers

goTableView gracefully handles pointer fields:

go
type Employee struct {
    Name    string
    Age     *int     // Pointer - may be nil
    Salary  *float64 // Pointer - may be nil
    Active  bool
}

age1 := 30
salary1 := 75000.00

employees := []Employee{
    {"Alice", &age1, &salary1, true},
    {"Bob", nil, nil, false},  // nil pointers display as empty
}

gotableview.FromStructs("Employees", employees).Show()

Result:

  • Alice: Shows age "30" and salary "75000.00"
  • Bob: Shows empty strings for age and salary (nil values)

Nested Structs

Currently, nested structs are displayed using their string representation:

go
type Address struct {
    City  string
    State string
}

type Person struct {
    Name    string
    Age     int
    Address Address  // Nested struct
}

people := []Person{
    {"Alice", 30, Address{"New York", "NY"}},
    {"Bob", 25, Address{"Los Angeles", "CA"}},
}

gotableview.FromStructs("People", people).Show()

Result:

  • Address column shows: {New York NY}

Better Nested Struct Handling

For better control over nested structs, flatten them or use separate columns:

go
type Person struct {
    Name  string `gotableview:"Name"`
    Age   int    `gotableview:"Age"`
    City  string `gotableview:"City"`
    State string `gotableview:"State"`
}

Working with Maps

For dynamic data structures, use FromMaps():

go
package main

import "github.com/mansoldof/goTableView"

func main() {
    data := []map[string]any{
        {"Name": "Alice", "Age": 30, "City": "NYC"},
        {"Name": "Bob", "Age": 25, "City": "LA"},
        {"Name": "Carol", "City": "Chicago"},  // Missing "Age"
    }
    
    gotableview.FromMaps("People", data).Show()
}

Features:

  • Columns detected from all map keys
  • Missing keys show as empty cells
  • Order of columns may vary
goTableView basic usage

Pagination with Structs

For large datasets, combine structs with pagination:

go
package main

import (
	"time"
	"fmt"
    "github.com/mansoldof/goTableView"
)

func main() {
		type LogEntry struct {
			Timestamp time.Time `gotableview:"Time"`
			Level     string    `gotableview:"Level"`
			Message   string    `gotableview:"Message"`
		}

		// Create 1000 log entries
		logs := make([]LogEntry, 1000)
		for i := range logs {
			logs[i] = LogEntry{
				Timestamp: time.Now().Add(-time.Duration(i) * time.Minute),
				Level:     "INFO",
				Message:   fmt.Sprintf("Log entry #%d", i+1),
			}
		}

		// Show with pagination (50 per page)
		gotableview.FromStructsPaginated("Application Logs", logs, 50).
			WithSearch().  // Add search functionality
			Show()
}
goTableView basic usage

Advanced Examples

Example 1: Database Results

go
package main

import (
    "database/sql"
    "time"
    "github.com/mansoldof/goTableView"
)

type Order struct {
    OrderID   int       `gotableview:"Order #"`
    Customer  string    `gotableview:"Customer Name"`
    Product   string    `gotableview:"Product"`
    Quantity  int       `gotableview:"Qty"`
    Total     float64   `gotableview:"Total ($)"`
    OrderDate time.Time `gotableview:"Order Date"`
}

func main() {
    db, _ := sql.Open("sqlite3", "orders.db")
    defer db.Close()
    
    rows, _ := db.Query("SELECT id, customer, product, quantity, total, order_date FROM orders")
    defer rows.Close()
    
    var orders []Order
    for rows.Next() {
        var o Order
        rows.Scan(&o.OrderID, &o.Customer, &o.Product, &o.Quantity, &o.Total, &o.OrderDate)
        orders = append(orders, o)
    }
    
    gotableview.FromStructs("Recent Orders", orders).Show()
}

Example 2: JSON API Response

go
package main

import (
    "encoding/json"
    "net/http"
    "github.com/mansoldof/goTableView"
)

type User struct {
    ID       int    `json:"id" gotableview:"User ID"`
    Name     string `json:"name" gotableview:"Name"`
    Username string `json:"username" gotableview:"Username"`
    Email    string `json:"email" gotableview:"Email"`
    Phone    string `json:"phone" gotableview:"Phone"`
}

func main() {
    resp, _ := http.Get("https://jsonplaceholder.typicode.com/users")
    defer resp.Body.Close()
    
    var users []User
    json.NewDecoder(resp.Body).Decode(&users)
    
    gotableview.FromStructs("API Users", users).Show()
}

Example 3: File System Scanner

go
package main

import (
    "os"
    "path/filepath"
    "time"
    "github.com/mansoldof/goTableView"
)

type FileInfo struct {
    Name     string    `gotableview:"File Name"`
    Size     int64     `gotableview:"Size (bytes)"`
    Modified time.Time `gotableview:"Last Modified"`
    IsDir    bool      `gotableview:"Directory"`
}

func main() {
    var files []FileInfo
    
    filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return nil
        }
        files = append(files, FileInfo{
            Name:     info.Name(),
            Size:     info.Size(),
            Modified: info.ModTime(),
            IsDir:    info.IsDir(),
        })
        return nil
    })
    
    gotableview.FromStructsPaginated("File System", files, 100).
        WithSearch().
        Show()
}

Best Practices

  1. Use meaningful field names - They become column headers
  2. Add struct tags for clarity - Make headers user-friendly
  3. Hide sensitive fields - Use gotableview:"-" for passwords, keys
  4. Consider pagination - Use FromStructsPaginated() for 100+ items
  5. Export fields - Only exported (capitalized) fields are shown

Common Patterns

Configuration Structs

go
type Config struct {
    ServerHost string `gotableview:"Server"`
    ServerPort int    `gotableview:"Port"`
    DBName     string `gotableview:"Database"`
    DebugMode  bool   `gotableview:"Debug Enabled"`
    Timeout    int    `gotableview:"Timeout (s)"`
}

config := Config{
    ServerHost: "localhost",
    ServerPort: 8080,
    DBName:     "myapp",
    DebugMode:  true,
    Timeout:    30,
}

// Show single config as a slice with one item
gotableview.FromStructs("Configuration", []Config{config}).Show()

Converting Existing Code

Before (manual):

go
for _, item := range items {
    table.Row(
        fmt.Sprintf("%d", item.ID),
        item.Name,
        fmt.Sprintf("%.2f", item.Price),
    )
}

After (automatic):

go
gotableview.FromStructs("Items", items).Show()

What's Next?

Performance Note

Struct reflection has minimal overhead. For datasets under 10,000 items, the performance impact is negligible.

Released under the MIT License.