Skip to content
This is my space, where experience meets the will to start over. This is my space, where experience meets the will to start over.

The first step is knowing where you want to go.

  • Home
  • Coding Hub
    • Software & Project
      • Small Biz Ops – S.B.O.
        • SmallBizOps – Day 10/90
      • CRM/ERP
      • MyTracker
      • My Budget
    • Form Zero to “WoW”
      • JavaScript from Zero (Completed)
        • 2. Remove and Edit List Items
        • 3. Separate HTML and JavaScript, Use addEventListener and Conditional Logic
        • 4. Add Dynamic CSS Classes
        • 5. Save & Restore Your List with localStorage
        • 6 – Turn Your App into a Full To-Do List
      • Python from Zero (Completed)
        • 2. Lists & Loops
        • 3. Conditional Menus
        • 4. Edit & Remove Tasks (with closing: Python vs PHP and Large Data)
        • 5 – Save to File: Make Your Tasks Survive Restarts
        • 6 — Pythin from zero – Final Project Polishing: Numbering, Formatting, and Preparing for CSV
      • Rust – From Zero to “WoW” (Completed)
        • 1 – Setup and Project Structure in Rust
        • 2 – User input: validation and error handling
        • 3 – Rust from Zero to “WoW – BMI Calculation and Conditional Logic
        • 4 –Rust – Clear, Formatted Output
        • 5 – Rust – Final Thoughts: Precision as a Form of Respect
      • Go from Zero to “WoW” (Completed)
        • 1 – Why Go Is Perfect for a Personal Expense Tracker
        • 2 – Logging Expenses and Console Input
        • 3 – Go from Zero to “WoW” – Smart Filtering & Display Logic
        • 4 – Go – Saving Data to Local Files
        • 5 – Go – Final Project – Expense Tracker in Go
      • C++ from Zero to “WoW” (Completed)
        • 1 – Why C++ for file organization?
        • 2 – C++ – File Type Detection and Classification
        • 3 – C++ – Creating & Managing Subfolders
        • 4 – C++ – Safe File Movement and User Feedback
        • 5 – C++ – Order as Mental Clarity
      • Ubuntu – From Zero to “WoW” (Completed)
        • 2 – Ubuntu – The Desktop Environment and Essential Commands
        • 3 – Ubuntu – Managing Files, Folders, and Permissions
        • 4 – Ubuntu – Installing and Updating Software with APT and Snap
        • 5 – Ubuntu – Customizing the Desktop Environment
        • 6 – Ubuntu – Network and Device Configuration
        • 7 – Ubuntu – User Management & System Security — “The Cathedral of Permissions”
        • 8 – Ubuntu – The Talking Machine: Terminal & Bash Scripting
        • 9 – Ubuntu – Ubuntu as a Server or Development Environment
        • 10 – Ubuntu – Backup, Maintenance & Troubleshooting
    • Git Hub Repository
      • Small Biz Ops – S.B.O.
      • Mini ERP – PHP & MySQL
      • CleverCRM (Java, Spring Boot)
      • FraudWatch (Python, FastAPI + scikit-learn)
      • OnboardIQ – Smart Onboarding Portal (Flask + SQLite Demo)
    • ArchPilot
      • 1-Users & Roles, End-to-End (Architecture, Database, and Cross-Framework Code)
      • 2 – Client Registry (CRM) Across Frameworks
      • 3 – Project & Budget Tracker (ERP)
      • 4 – Approval Workflow Engine Multi-step routing, status tracking, escalation paths
      • 5 – Audit Trail & Versioning
    • Small Biz Ops – S.B.O.
  • Vivere in USA
  • P4Y
  • Testi poetici
    • 1 – Sospeso
    • 2 – Il bicchiere di vetro quieto
    • 3 – Quando l’amore inciampa
    • 4 – Ma chi siete davvero?
    • 5 – Above the Thread of Day
    • 6 – The Truth That Doesn’t Exist
    • 7 – All of You, I Miss
    • 8 – The Captain and the Ocean
    • 9 – Between Light and Mist
    • 10 – Il peso delle scelte
  • Contact
  • Admin
This is my space, where experience meets the will to start over.
This is my space, where experience meets the will to start over.

The first step is knowing where you want to go.

Coding – Step 12.3 – Go from Zero to “WoW” – Smart Filtering & Display Logic

Posted on 7 Ottobre 20257 Ottobre 2025 By Francesco

Friendly heads-up: This lesson focuses on clean code and composable logic. The domain (products/inventory) is just a vehicle to learn practical Go patterns.


What You’ll Learn

By the end, you’ll be able to:

  • Model simple records with struct and build immutable-style transformations.
  • Write predicate functions and compose them with And / Or / Not helpers.
  • Implement filtering, sorting, limit/offset (pagination) and search.
  • Render a neat ASCII table with dynamic column widths.
  • Add unit tests and benchmarks for critical paths.

What We’ll Build

A command-line program that reads a small dataset of products, then applies user-provided filters:

  • --min-price, --max-price
  • --category
  • --q (substring search on name/description)
  • --sort (e.g., price, name, -price for descending)
  • --limit, --offset

Example:

go run ./cmd/catalog \
  --min-price 10 --max-price 40 \
  --category "snacks" \
  --q "protein" \
  --sort -price \
  --limit 5

Project Structure

smartfilter/
├─ cmd/catalog/main.go      # CLI: parse flags, call core logic, print table
├─ internal/catalog/
│  ├─ data.go               # sample data & loaders
│  ├─ filter.go             # predicates, composition, Filter function
│  ├─ sort.go               # sorters and parseSort
│  ├─ paginate.go           # limit/offset helpers
│  ├─ render.go             # table render utilities
│  └─ catalog_test.go       # unit tests & benchmarks
└─ go.mod

Data Model

package catalog

type Product struct {
    ID          int
    Name        string
    Category    string
    Price       float64
    InStock     bool
    Description string
}

func Seed() []Product {
    return []Product{
        {1, "Protein Bar", "snacks", 19.90, true,  "High-protein bar, 20g protein"},
        {2, "Trail Mix",   "snacks", 12.00, true,  "Almonds, raisins, dark chocolate"},
        {3, "Olive Oil",   "pantry", 39.50, true,  "Extra virgin, cold pressed"},
        {4, "Quinoa",      "grains", 7.80,  false, "Organic white quinoa"},
        {5, "Oatmeal",     "grains", 5.40,  true,  "Steel-cut oats"},
        {6, "Protein Shake","drinks", 29.00, true,  "Ready-to-drink, vanilla"},
        {7, "Green Tea",   "drinks", 8.50,  true,  "Sencha blend"},
    }
}

Predicate Basics

package catalog

type Pred func(Product) bool

func And(ps ...Pred) Pred {
    return func(p Product) bool {
        for _, pr := range ps {
            if !pr(p) { return false }
        }
        return true
    }
}

func Or(ps ...Pred) Pred {
    return func(p Product) bool {
        for _, pr := range ps {
            if pr(p) { return true }
        }
        return false
    }
}

func Not(p Pred) Pred {
    return func(x Product) bool { return !p(x) }
}

// Leaf predicates
func PriceMin(v float64) Pred { return func(p Product) bool { return p.Price >= v } }
func PriceMax(v float64) Pred { return func(p Product) bool { return p.Price <= v } }
func CategoryIs(c string) Pred { return func(p Product) bool { return p.Category == c } }
func InStockOnly() Pred { return func(p Product) bool { return p.InStock } }
func Query(q string) Pred {
    ql := strings.ToLower(q)
    return func(p Product) bool {
        return strings.Contains(strings.ToLower(p.Name), ql) ||
               strings.Contains(strings.ToLower(p.Description), ql)
    }
}

Filter Function

func Filter(xs []Product, p Pred) []Product {
    if p == nil { return append([]Product(nil), xs...) }
    out := make([]Product, 0, len(xs))
    for _, it := range xs {
        if p(it) { out = append(out, it) }
    }
    return out
}

Sorting

package catalog

import "slices"

func SortBy(xs []Product, key string) {
    desc := false
    k := key
    if strings.HasPrefix(key, "-") { desc, k = true, key[1:] }

    less := func(i, j int) bool {
        a, b := xs[i], xs[j]
        switch k {
        case "price":
            if desc { return a.Price > b.Price }
            return a.Price < b.Price
        case "name":
            if desc { return a.Name > b.Name }
            return a.Name < b.Name
        case "category":
            if desc { return a.Category > b.Category }
            return a.Category < b.Category
        default:
            if desc { return a.ID > b.ID }
            return a.ID < b.ID
        }
    }
    slices.SortFunc(xs, func(a, b Product) int {
        if less(0, 1) { // not used directly; we’ll inline comparator below
            return 0
        }
        return 0
    })
}

A cleaner approach is to use slices.SortFunc with a comparator:

func SortBy(xs []Product, key string) {
    desc := false
    k := key
    if strings.HasPrefix(key, "-") { desc, k = true, key[1:] }

    comp := func(a, b Product) int {
        var lt, gt bool
        switch k {
        case "price": lt, gt = a.Price < b.Price, a.Price > b.Price
        case "name": lt, gt = a.Name < b.Name, a.Name > b.Name
        case "category": lt, gt = a.Category < b.Category, a.Category > b.Category
        default:        lt, gt = a.ID < b.ID, a.ID > b.ID
        }
        if desc { lt, gt = gt, lt }
        if lt { return -1 }
        if gt { return 1 }
        return 0
    }
    slices.SortFunc(xs, comp)
}

Pagination

func Paginate(xs []Product, offset, limit int) []Product {
    if offset < 0 { offset = 0 }
    if limit <= 0 { return []Product{} }
    if offset >= len(xs) { return []Product{} }
    end := offset + limit
    if end > len(xs) { end = len(xs) }
    return xs[offset:end]
}

Rendering a Clean Table

func RenderTable(xs []Product) string {
    if len(xs) == 0 { return "(no results)" }

    headers := []string{"ID", "Name", "Category", "Price", "Stock"}
    rows := make([][]string, 0, len(xs))
    for _, p := range xs {
        rows = append(rows, []string{
            fmt.Sprintf("%d", p.ID),
            p.Name,
            p.Category,
            fmt.Sprintf("%.2f", p.Price),
            map[bool]string{true: "Yes", false: "No"}[p.InStock],
        })
    }

    widths := make([]int, len(headers))
    for i, h := range headers { if len(h) > widths[i] { widths[i] = len(h) } }
    for _, r := range rows {
        for i, cell := range r {
            if len(cell) > widths[i] { widths[i] = len(cell) }
        }
    }

    var b strings.Builder
    pad := func(s string, w int) string { return s + strings.Repeat(" ", w-len(s)) }

    // header
    for i, h := range headers {
        if i > 0 { b.WriteString("  ") }
        b.WriteString(pad(h, widths[i]))
    }
    b.WriteString("\n")

    // separator
    for i := range headers {
        if i > 0 { b.WriteString("  ") }
        b.WriteString(strings.Repeat("-", widths[i]))
    }
    b.WriteString("\n")

    // rows
    for _, r := range rows {
        for i, c := range r {
            if i > 0 { b.WriteString("  ") }
            b.WriteString(pad(c, widths[i]))
        }
        b.WriteString("\n")
    }

    return b.String()
}

CLI Wiring (cmd/catalog/main.go)

package main

import (
    "flag"
    "fmt"
    "strings"

    "example.com/smartfilter/internal/catalog"
)

func main() {
    var (
        minPrice = flag.Float64("min-price", 0, "minimum price")
        maxPrice = flag.Float64("max-price", 0, "maximum price (0 = no max)")
        category = flag.String("category", "", "filter by category")
        q        = flag.String("q", "", "substring query on name/description")
        sortKey  = flag.String("sort", "", "sort by field (price,name,category,-price,...) ")
        limit    = flag.Int("limit", 10, "number of rows")
        offset   = flag.Int("offset", 0, "offset for pagination")
        inStock  = flag.Bool("in-stock", false, "only items in stock")
    )
    flag.Parse()

    items := catalog.Seed()

    // Build predicate
    preds := make([]catalog.Pred, 0, 5)
    if *minPrice > 0 { preds = append(preds, catalog.PriceMin(*minPrice)) }
    if *maxPrice > 0 { preds = append(preds, catalog.PriceMax(*maxPrice)) }
    if *category != "" { preds = append(preds, catalog.CategoryIs(strings.ToLower(*category))) }
    if *q != "" { preds = append(preds, catalog.Query(*q)) }
    if *inStock { preds = append(preds, catalog.InStockOnly()) }

    pred := catalog.And(preds...)
    filtered := catalog.Filter(items, pred)

    if *sortKey != "" { catalog.SortBy(filtered, *sortKey) }
    paged := catalog.Paginate(filtered, *offset, *limit)

    fmt.Println(catalog.RenderTable(paged))
}

Tests & Benchmarks (internal/catalog/catalog_test.go)

package catalog

import (
    "testing"
)

func TestFilterAndSort(t *testing.T) {
    xs := Seed()
    out := Filter(xs, And(PriceMin(8), PriceMax(20), CategoryIs("snacks")))
    if len(out) == 0 { t.Fatalf("expected some results") }
    SortBy(out, "-price")
    if out[0].Price < out[len(out)-1].Price {
        t.Fatalf("expected descending price")
    }
}

func TestPaginate(t *testing.T) {
    xs := Seed()
    page := Paginate(xs, 2, 3)
    if len(page) != 3 { t.Fatalf("want 3, got %d", len(page)) }
}

func BenchmarkQuery(b *testing.B) {
    xs := Seed()
    p := And(Query("pro"), PriceMin(10))
    for i := 0; i < b.N; i++ {
        _ = Filter(xs, p)
    }
}

Run:

go test ./...
go test -bench . ./...

Nice-to-Haves

  • CSV loader for real data (encoding/csv).
  • Generics for Filter[T any] and predicates over other types.
  • Fuzzy search with edit distance (e.g., Damerau–Levenshtein) for --q.
  • Color output for stock / category highlights.

Learning Checklist

  • I can model records with struct.
  • I can write and compose predicates.
  • I can apply filter + sort + paginate correctly.
  • I can render aligned tables.
  • I can write tests and basic benchmarks.
Post Views: 1.141

Condividi:

  • Condividi su Facebook (Si apre in una nuova finestra) Facebook
  • Condividi su X (Si apre in una nuova finestra) X
Go Coding CLIcompositionfilteringGogolangpaginationpredicatessortingtable outputunit testing

Navigazione articoli

Previous post
Next post

Francesco

My name is Francesco Boschi, originally from Italy and currently based in the United States. For over twenty years, I’ve worked as a manager and consultant across diverse sectors — from education and cultural institutions to the food industry — developing skills in operational management, strategic consulting, and complex problem-solving. In recent years, I’ve combined this experience with a strong passion for software development, creating custom tools designed to simplify workflows and meet real business needs.

Relocating to the U.S. marks the beginning of a new chapter: a personal and professional decision driven by the desire to be close to my son and to embrace new challenges in a different environment. Today, my goal is to turn my experience into meaningful solutions, blending strategic vision with technical expertise to help people and organizations work more effectively.

I enjoy moving between different worlds, adapting tools and approaches to people and contexts. I bring leadership, flexibility, attention to detail, analytical thinking, and a strong problem-solving mindset — along with a deep curiosity to learn and grow. Above all, I believe in sharing: I’m always eager to offer my experience to support the growth of others.

Related Posts

Coding

I built MyTracker: a minimal time tracker to work better

Posted on 14 Ottobre 202514 Ottobre 2025

MyTracker is a minimal time tracker I built to remove friction: start an activit

Condividi:

  • Condividi su Facebook (Si apre in una nuova finestra) Facebook
  • Condividi su X (Si apre in una nuova finestra) X
Read More
Coding

Coding – Step 2 – 🔐 Access Project Section

Posted on 6 Giugno 202526 Luglio 2025

In this section of the website, I share practical code examples that help highlight the differences between common web development technologies and structures. Each example is crafted to be clear, educational, and replicable, so anyone can understand the logic and experiment with the source code directly. 📌 Starting with a…

Condividi:

  • Condividi su Facebook (Si apre in una nuova finestra) Facebook
  • Condividi su X (Si apre in una nuova finestra) X
Read More
Coding

Coding – Step 14.6 – Ubuntu – Network and Device Configuration

Posted on 27 Ottobre 202527 Ottobre 2025

Ubuntu handles connections and devices smoothly, but knowing how to configure Wi-Fi, printers, Bluetooth, and USB manually gives you full control — even as a beginner.

Condividi:

  • Condividi su Facebook (Si apre in una nuova finestra) Facebook
  • Condividi su X (Si apre in una nuova finestra) X
Read More

Iscriviti alla nostra Newsletter

🤞 Let's keep in touch

We do not send spam! Read our Privacy policy for more information.

Controlla la tua casella di posta o la cartella spam per confermare la tua iscrizione

Cerca nel sito

©2026 This is my space, where experience meets the will to start over. | WordPress Theme by SuperbThemes