An efficient way to compare strings in Go

Avatar of Rakesh Mothukuri

Rakesh Mothukuri

| Reading Time : 3 minutes

Avatar of Rakesh Mothukuri Avatar of Rakesh Mothukuri Avatar of Rakesh Mothukuri Avatar of Rakesh Mothukuri

Introduction

One of the most overlooked operations is comparing two strings and this might not cause system overhead but definitely, it is a good practice to write an efficient code where possible. These small dividends definitely pay at the end when the application is under real stress in production.

Note: I am amateur in Go if you are an experienced professional in Go and find anything wrong here don’t be annoyed. If you could point the mistake I will correct those accordingly.

One of the often used string comparisons is

strings.ToLower(stringA) == strings.ToLower(stringB)

but this way of comparing takes a lot of time per operation and the reason is, In Go, a string is an immutable sequence of runes. rune in Go is an integer value represents code point

The more detailed and sensible explanation is given by Rob Pike in blog: https://blog.golang.org/strings Its a very nice bedtime read which gives a recap of basics of strings in programming languages.

Here ToLower a standard function in strings package which when used traverse through the entire string converts it to a lower case and returns a new string cause strings in Go is immutable which means strings.ToLower allocates new memory spaces for these two strings.

sample code:

func CompareStringsInCrudeWay(stringA, stringB string) (bool, error) {
	if strings.ToLower(stringA) == strings.ToLower(stringB) {
		return true, nil
	} else {
		return false, nil
	}
}

Benchmark:

Input:

  stringA = "rakesh123456789mothukuri123456789"

  stringB = "ramesh123456789mothukuri123456789"

BenchmarkCompare-8       8304722               151 ns/op               0 B/op          0 allocs/op

If we notice in earlier benchmark figures this operation takes 151 nanoseconds per operation. But is this too much we need to talk about? No, but if there is a simple change to way strings are compared and we could get this down to 30 nanoseconds why not?

Optimization

To optimize we could find a way instead traverse through runes of each string, compare each character in the string, if rune doesn’t match we would convert rune to lowercase and compare again and if they still don’t match we break the loop and break the loop and return strings are not same. This process will save operation time spent comparing string.

Pseudocode

func Compare(stringA, stringB string) bool {
	for i := 0; i < len(stringA); i++ {
		if stringA[i] == stringB[i] {
			continue
		}
		if unicode.ToLower(stringA[i]) != unicode.ToLower(stringB[i]) {
			return false
		}
	}
	return true
}

But this has more logic here to remember and implement every time we compare string only if there is an easy way to do. Go strings package has a method which performs in this fashion which is strings.EqualFold

sample code using EqualFold

func CompareStringsInEfficientWay(stringA, stringB string) (bool, error) {
   if strings.EqualFold(stringA, stringB) {
      return true, nil
   } else {
      return false, nil
   }
}

Benchmark:

Input:
  stringA = "rakesh123456789mothukuri123456789"
  stringB = "ramesh123456789mothukuri123456789"
BenchmarkCompare-8      76842292                17.1 ns/op             0 B/op          0 allocs/op

Now, it took 17 nanoseconds per operation, These small wins over a period of time will definitely payback, not only they pay back it also helps promote an efficient way of writing code.

This is the end of the blog if you think there is a more efficient way of comparing string in Go please drop me a message I will attribute you and amend the content.