-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
148 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package keyboard | ||
|
||
import ( | ||
"math" | ||
) | ||
|
||
var ( | ||
keyDistances map[string]Coordinates | ||
keyboardLayouts = map[string][]string{ | ||
"qwerty-us": { | ||
"`1234567890-=", | ||
" qwertyuiop[]\\", | ||
" asdfghjkl;'", | ||
" zxcvbnm,./", | ||
}, | ||
} | ||
) | ||
|
||
type Coordinates struct { | ||
X float64 | ||
Y float64 | ||
} | ||
|
||
func init() { | ||
keyDistances = generateKeyDistance(keyboardLayouts["qwerty-us"]) | ||
} | ||
|
||
func GetBestMatch(input string, list []string) (string, float64) { | ||
var scores = make([]float64, len(list)) | ||
for listOffset, ref := range list { | ||
for i := 0; i < len(input); i++ { | ||
if i >= len(ref) { | ||
// @todo missing characters, we should add a penalty | ||
scores[listOffset] += 1 | ||
continue | ||
} | ||
|
||
left, right := string(input[i]), string(ref[i]) | ||
scores[listOffset] += getDistance(keyDistances[left], keyDistances[right]) | ||
} | ||
} | ||
|
||
var bestScore = math.Inf(1) | ||
var offset int | ||
for listOffset, score := range scores { | ||
if score < bestScore { | ||
bestScore = score | ||
offset = listOffset | ||
} | ||
} | ||
|
||
return list[offset], scores[offset] | ||
} | ||
|
||
func getDistance(a, b Coordinates) float64 { | ||
return math.Sqrt( | ||
math.Pow(b.X-a.X, 2) + math.Pow(b.Y-a.Y, 2), | ||
) | ||
} | ||
|
||
func generateKeyDistance(rows []string) map[string]Coordinates { | ||
var keyMap = make(map[string]Coordinates, len(rows)) | ||
|
||
for rowIndex, row := range rows { | ||
for column := 0; column < len(row); column++ { | ||
character := string(row[column]) | ||
keyMap[character] = Coordinates{ | ||
X: float64(column), | ||
Y: float64(rowIndex), | ||
} | ||
} | ||
} | ||
|
||
return keyMap | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package keyboard | ||
|
||
import ( | ||
"math" | ||
"testing" | ||
) | ||
|
||
const ( | ||
floatTolerance = 0.01 | ||
) | ||
|
||
func TestGetBestMatch(t *testing.T) { | ||
testData := []struct { | ||
Input string | ||
List []string | ||
Expect string | ||
}{ | ||
{Input: "bee4", List: []string{"beer", "beef"}, Expect: "beer"}, | ||
{Input: "bee5", List: []string{"beef", "beer"}, Expect: "beer"}, | ||
{Input: "bee5", List: []string{"beef", "beer", "beast"}, Expect: "beer"}, | ||
{Input: "bee5", List: []string{"beef", "beer", "ben"}, Expect: "beer"}, | ||
} | ||
|
||
for _, td := range testData { | ||
result, distance := GetBestMatch(td.Input, td.List) | ||
if td.Expect != result { | ||
t.Errorf("Expected '%s' to match '%s', instead I got '%s' with distance %f, %+v", | ||
td.Input, td.Expect, result, distance, td) | ||
} | ||
} | ||
} | ||
|
||
func TestGetDistance(t *testing.T) { | ||
testData := []struct { | ||
A Coordinates | ||
B Coordinates | ||
Distance float64 | ||
}{ | ||
{A: Coordinates{X: 0, Y: 0}, B: Coordinates{X: 0, Y: 100}, Distance: 100}, | ||
{A: Coordinates{X: 0, Y: 0}, B: Coordinates{X: 100, Y: 0}, Distance: 100}, | ||
{A: Coordinates{X: 1, Y: 2}, B: Coordinates{X: 1, Y: 2}, Distance: 0}, | ||
{A: Coordinates{X: 10, Y: 20}, B: Coordinates{X: 20, Y: 10}, Distance: 14.14}, | ||
} | ||
|
||
for _, td := range testData { | ||
d := getDistance(td.A, td.B) | ||
|
||
if math.Abs(d-td.Distance) > floatTolerance { | ||
t.Errorf("Expected the distance to be %f, instead I got %f\n%v", td.Distance, d, td) | ||
} | ||
} | ||
|
||
} | ||
|
||
func TestGenerateKeyDistance(t *testing.T) { | ||
table := generateKeyDistance([]string{ | ||
"abc", // 00, 01, 02 | ||
"def", // 10, 11, 12 | ||
"ghi", // 20, 21, 22 | ||
}) | ||
|
||
if table["a"].X != 0 || table["a"].Y != 0 { | ||
t.Errorf("Expected the coords to be at 0,0 %+v", table["a"]) | ||
} | ||
|
||
if table["e"].X != 1 || table["e"].Y != 1 { | ||
t.Errorf("Expected the coords to be at 1,1 %+v", table["i"]) | ||
} | ||
|
||
if table["i"].X != 2 || table["i"].Y != 2 { | ||
t.Errorf("Expected the coords to be at 2,2 %+v", table["i"]) | ||
} | ||
} |