Search Apps Documentation Source Content File Folder Download Copy Actions Download 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}