Map lookups

Pig Latin
Pig Latin in Go
// Package piglatin is a small library for translating a sentence into Pig Latin.
package piglatin

import (
	"strings"
)

var vowels = map[byte]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true}
var specials = map[string]bool{"xr": true, "yt": true}
var vowels_y = map[byte]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}

// Sentence translates a sentence into Pig Latin.
func Sentence(phrase string) string {
	piggyfied := strings.Builder{}

	for _, word := range strings.Split(phrase, " ") {
		if piggyfied.Len() != 0 {
			piggyfied.WriteByte(' ')
		}

		if vowels[word[0]] || specials[word[:2]] {
			piggyfied.WriteString(word + "ay")
			continue
		}

		for pos := 1; pos < len(word); pos++ {
			letter := word[pos]
			if vowels_y[letter] {
				if letter == 'u' && word[pos-1] == 'q' {
					pos++
				}
				piggyfied.WriteString(word[pos:] + word[:pos] + "ay")
				break
			}
		}
	}
	return piggyfied.String()
}

The approach imports the strings package so it can use strings.Builder as an efficient way to build the output string.

This approach starts be defining several maps for looking up vowels and special letter combinations. The maps have boolean values so that a key can be looked up simply as

if vowels[word[0]]

instead of

if _, ok := vowels[word[0]]; ok {

Doing so also allows having multiple checks on the same line, since there can not be multiple if with short statements on the same line.

After instantiating a strings.Builder the sentence is Split with a space separator to iterate the words. Unless it is the first word, a space is written to the Builder to separate the word from the previous word.

The word is checked for starting with a vowel or if the beginning slice of two characters is in the map of "specials". If so, the word is written to the Builder along with ay at the end. Then the iteration is continued to the next word. If not, each byte in the word is iterated from index 1 up to the length of the word, until finding a vowel (that now includes y).

The ways to iterate characters are by Unicode runes, or by each letter being a string, or by each letter being a byte. The runes are from range on a string, the strings from Split(), and the bytes from indexing into the string. Another way to iterate runes is to convert the string to a rune slice and range on it. The difference between ranging on a rune slice vs ranging on a string is that the index returned from a string is the position of the next rune in bytes, not which rune it is. For example, if the first unicode character is two bytes, then the second unicode character index will be 2 when ranging on a string and 1 when ranging on a rune slice. As of the time of this writing we can iterate bytes, since all of the characters are single-byte ASCII characters.

If the found vowel is u, the previous consonant is checked for being q and the position is adjusted accordingly. A slice is then taken from the position until the end of the word, plus a slice taken from the beginning of the word and stopped before the position, plus ay, and added to the Builder.

After the iteraton of words is finished, the Builder's String method is called to return the accumulated string from the function.

11th Sep 2024 · Found it useful?