2
\$\begingroup\$

This is my custom function to join strings from an array into one string. We can provide normal separator and the last one. JoinStringsIntoArray(", ", " and ", words...)

func JoinStringsIntoArray(sep string, lastSep string, words ...string) string { lastElementIndex := len(words) - 1 var joinedString string for index, word := range words[0:lastElementIndex] { if index == lastElementIndex-1 { sep = lastSep } joinedString += word + sep } joinedString += words[lastElementIndex] return joinedString } 

Test for this function

func TestStringsJoinsWithCommas(t *testing.T) { var words []string words = append(words, "one", "two", "three") expectedResult := "one, two and three" result := JoinStringsIntoArray(", ", " and ", words...) if expectedResult != result { t.Errorf("Strings joiner is broken, it should be '%v', we got '%v'", expectedResult, result) } } 

What do you think about this, can this solution be improved? The function works fine but I'm not so sure it's written well.

\$\endgroup\$

    1 Answer 1

    1
    \$\begingroup\$

    The Go standard library strings package has a Join function:

    func Join(elems []string, sep string) string 

    Your function is a simple extension of strings.Join:

    func JoinLast(elems []string, sep, lastSep string) string 

    However, for no obvious reason, you chose a different function parameter signature:

    func JoinLast(sep string, lastSep string, words ...string) string 

    We need a comment to explain why this isn't an arbitrary or idiosyncratic decision.


    Your testing is inadequate. Check boundary conditions. Your function fails on an empty words list.

    words = []string{} 

    You have not provided any testing benchmarks. For example,

    BenchmarkJoin-4 5534918 207.9 ns/op 104 B/op 5 allocs/op 
    func JoinStringsIntoArray(sep string, lastSep string, words ...string) string { lastElementIndex := len(words) - 1 var joinedString string for index, word := range words[0:lastElementIndex] { if index == lastElementIndex-1 { sep = lastSep } joinedString += word + sep } joinedString += words[lastElementIndex] return joinedString } func BenchmarkJoin(b *testing.B) { words := []string{"one", "two", "three", "four", "five"} b.ReportAllocs() b.ResetTimer() for N := 0; N < b.N; N++ { JoinStringsIntoArray(", ", " and ", words...) } } 

    You can do better. For example, minimize allocation,

    BenchmarkJoin-4 16612879 70.95 ns/op 32 B/op 1 allocs/op 
    func JoinLast(elems []string, sep, lastSep string) string { var join strings.Builder joinCap := 0 for i := 0; i < len(elems); i++ { joinCap += len(elems[i]) } if len(elems) > 1 { joinCap += len(lastSep) if len(elems) > 2 { joinCap += (len(elems) - 2) * len(sep) } } join.Grow(joinCap) last := len(elems) - 1 - 1 for i, elem := range elems { if i >= last { if i == last { sep = lastSep } else { sep = "" } } join.WriteString(elem) join.WriteString(sep) } return join.String() } func BenchmarkJoin(b *testing.B) { words := []string{"one", "two", "three", "four", "five"} b.ReportAllocs() b.ResetTimer() for N := 0; N < b.N; N++ { JoinLast(words, ", ", " and ") } } 
    \$\endgroup\$

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.