Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

soundexdemo.gno

2.40 Kb · 93 lines
 1// Package soundexdemo is a small gnoweb demo of the Soundex phonetic algorithm
 2// provided by the [p/moul/x/daily/soundex](/p/moul/x/daily/soundex/v0) library:
 3// it groups a small name list by phonetic key so the "sounds alike" clusters
 4// are visible.
 5//
 6// It contains no phonetic logic of its own. Stateless, so Render is
 7// deterministic — names are grouped in first-seen order, never by iterating a
 8// map.
 9package soundexdemo
10
11import (
12	"strings"
13
14	"gno.land/p/moul/x/daily/soundex/v0"
15)
16
17// names include several deliberate near-collisions.
18var names = []string{
19	"Robert", "Rupert", "Rubin",
20	"Ashcraft", "Ashcroft",
21	"Tymczak", "Pfister", "Honeyman",
22}
23
24// Render renders the demo for gnoweb.
25//
26//	Render("")       / Render("/") -> the name table + clusters
27//	Render("/<name>")              -> that name's key
28func Render(path string) string {
29	var b strings.Builder
30	b.WriteString("# Soundex\n\n")
31	b.WriteString("Phonetic keys for English names, demoing the ")
32	b.WriteString("[`p/moul/x/daily/soundex`](/p/moul/x/daily/soundex/v0) library.\n\n")
33
34	if q := parseArg(path); q != "" {
35		k := soundex.Encode(q)
36		b.WriteString("## `")
37		b.WriteString(q)
38		b.WriteString("`\n\n")
39		if k == "" {
40			b.WriteString("_No ASCII letters — no key._\n")
41		} else {
42			b.WriteString("key: **")
43			b.WriteString(k)
44			b.WriteString("** (normalized: `")
45			b.WriteString(soundex.Normalize(q))
46			b.WriteString("`)\n")
47		}
48		return b.String()
49	}
50
51	b.WriteString("| name | key |\n|---|---|\n")
52	for _, n := range names {
53		b.WriteString("| ")
54		b.WriteString(n)
55		b.WriteString(" | `")
56		b.WriteString(soundex.Encode(n))
57		b.WriteString("` |\n")
58	}
59
60	// group by key, preserving first-seen order (never iterate a map)
61	b.WriteString("\n## Sounds alike\n\n")
62	keys := []string{}
63	byKey := map[string][]string{}
64	for _, n := range names {
65		k := soundex.Encode(n)
66		if _, seen := byKey[k]; !seen {
67			keys = append(keys, k)
68		}
69		byKey[k] = append(byKey[k], n)
70	}
71	for _, k := range keys {
72		if len(byKey[k]) < 2 {
73			continue
74		}
75		b.WriteString("- **")
76		b.WriteString(k)
77		b.WriteString("**: ")
78		b.WriteString(strings.Join(byKey[k], ", "))
79		b.WriteString("\n")
80	}
81
82	b.WriteString("\n> A *blocking* key for finding candidates, never proof that two names match.\n")
83	return b.String()
84}
85
86func parseArg(path string) string {
87	s := strings.TrimSpace(path)
88	s = strings.TrimPrefix(s, "/")
89	if i := strings.IndexByte(s, '/'); i >= 0 {
90		s = s[:i]
91	}
92	return s
93}