- 2016/08/31

Sore tadi itu ceritanya, liat dua pertanyaan untuk developer di sebuah perusahaan, simpel sih, cuma membalikkan string dan cari karakter yang tidak berulang dari string. Pertanyaannya seperti ini :

  1. Write a function that takes in a string and reverse it. (Do not use existing string reverse function).

    For instance, if the input string is “selamat pagi”, the function should return “igap tamales”

  2. Write a function that takes in a string and returns the first character in the string that appears exactly once.

    For instance, if the input string “abcxyzcba”, the function should return “x”

Iseng ahh, mainan Go. :-D

MEMBALIKKAN STRING

Cara pertama yang paling naif adalah mengambil panjang byte dari string, lalu mulai dari byte yang paling akhir ditambahkan ke byte list :

func StringReverse(str string) string {
    byteLen := len(str)
    result := []byte{}
    for byteLen != 0 {
        byteLen--
        result = append(result, str[byteLen])
    }

    return string(result)
}

// selamat pagi ---> igap tamales
// BRANTAKAN : selamat る pagi 見る --> ��㋦� igap ��� tamales 

Meski fungsi diatas sudah memenuhi kriteria untuk pertanyaan no. 1 (karena contohnya cuma selamat pagi), dia masih punya kekurangan, yaitu jika string mengandung karakter unicode, hasilnya bakal berantakan, makanya saya sebut cara paling naif. Jadi, saya refactor fungsi itu biar support karakter unicode juga, menjadi seperti ini :

// import "unicode/utf8"

func StringReverse(str string) string {
    strLen := utf8.RuneCountInString(str) // panjang string
    byteLen := len(str) // panjang byte
    result := make([]rune, strLen)
    for startIdx, width, resultIdx := 0, 0, (strLen - 1); startIdx < byteLen; startIdx += width {
        runeValue, byteWidth := utf8.DecodeRuneInString(str[startIdx:])
        result[resultIdx] = runeValue
        width = byteWidth
        resultIdx--
    }

    return string(result)
}

// selamat pagi ---> igap tamales
// selamat る pagi 見る ---> る見 igap る tamales

Karakter unicode itu, bisa mempunyai panjang byte yg berbeda dengan karakter alfabet biasa, di fungsi StringReverse yang kedua, saya memakai type rune dan membedakan antara panjang byte dan panjang string. Untuk panjang string, menggunakan fungsi built-in utf8.RuneCountInString() bawaan Go, sedangkan panjang byte cuma menggunakan len() biasa.

Fungsi StringReverse yang kedua sepertinya gak idiomatic go code 1, hahaha. Stupid, but it works.

AMBIL KARAKTER PERTAMA YANG TIDAK BERULANG

Untuk mengambil karakter pertama yang tidak berulang, saya membuat temporary slice yang berisi counter dari setiap karakter input, kemudian input di-iterasi lagi dan jika di dalam iterasi tersebut menemukan counter dengan jumlah 1, langsung kembalikan hasilnya.

// import "unicode/utf8"

func FirstNonRepeat(str string) string {
    counter := make(map[string]int, utf8.RuneCountInString(str))
    for _, r := range str {
        s := string(r) // cast to string
        counter[s]++
    }

    for _, r := range str {
        s := string(r) // cast to string
        if counter[s] == 1 {
            return s
        }
    }

    return ""
}

// map[b:2 x:1 z:1 a:2 c:2 y:1]
// abcxyzcba --> x

Dari mainan diatas, for + range di Go ini dipake buat pengganti foreach (PHP).

A “for” statement with a “range” clause iterates through all entries of an array, slice, string or map, or values received on a channel. For each entry it assigns iteration values to corresponding iteration variables and then executes the block. 2

Jika ada yg lebih idiomatic atau efisien tolong dibagi di kolom komentar ya.