Go: Efficiently find a number in a Slice
In Go, slices are a powerful and flexible way to handle collections of data. One common task when working with slices is to determine whether a specific element exists within the slice. In this blog post, we will explore a simple yet effective way to accomplish this using the FindNumber function. We will break down the function step by step and understand its usage and practical applications. But before that lets write a test for a function to find a number in a Slice. Go has a built-in testing package called testing that you can use to write and run your tests. Below is an example of how to write test cases for the FindNumber function.
mkdir findnumber
go mod init github.com/abhishekamralkar/numInList
touch findnumber_test.go
By convention, Go test files are named with a _test.go suffix. Here’s the structure for test file:
package findnumber_test
import (
"testing"
findnumber "github.com/abhishekamralkar/numInList"
)
package `findnumber_test`: Naming the package with a _test suffix keeps the test code separate
Imports:
- testing: Go’s built-in package that provides the tools we need to write and run tests.
- findnumber: Importing the package containing our FindNumber function using an alias.
Struct:
type testCase struct {
name string
input []int
search int
expected bool
}
testCase
struct holds the data for each test case, including:
- name: A descriptive name for the test case.
- input: The slice of integers to search through.
- search: The number to search for in the slice.
- expected: The expected result of the function (true if the number should be found, false otherwise).
TestCase:
func TestFindNumber(t *testing.T) {
testcases := []testCase{
{"Number exists in slice", []int{1, 2, 3, 4, 5}, 3, true},
{"Number does not exist in slice", []int{1, 2, 3, 4, 5}, 6, false},
{"Empty slice", []int{}, 1, false},
{"Single element slice - match", []int{1}, 1, true},
{"Single element slice - no match", []int{2}, 1, false},
{"Multiple identical elements", []int{3, 3, 3, 3}, 3, true},
{"Large slice", make([]int, 100000), 99999, false},
}
We defined a slice of testCase structs, each representing a different scenario to test the FindNumber function:
Common scenarios:
- Check if a number exists or doesn’t exist in a small slice.
- Test with an empty slice.
- Test with a single-element slice, both where the element matches and doesn’t match the search number.
- Test with a slice that contains multiple identical elements.
Edge case:
- Test with a very large slice of 100,000 elements to check how the function performs with large input sizes.
Testcase Runner:
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
result := findnumber.FindNumber(tc.input, tc.search)
if result != tc.expected {
t.Errorf("FindNumber(%v, %d) = %v; want %v", tc.input, tc.search, result, tc.expected)
}
})
}
- for _, tc := range testcases: This loop iterates over each test case defined in the testcases slice.
- t.Run(tc.name, func(t *testing.T) {…}): t.Run is used to run each test case as a subtest, making it easier to track which specific case is being tested and its outcome.
- result := findnumber.FindNumber(tc.input, tc.search): The FindNumber function is called with the input slice and search number from the current test case.
- if result != tc.expected {…}: This condition checks if the function’s result matches the expected output. If not, an error is reported using t.Errorf, which provides detailed information about what went wrong.
Error Reporting:
t.Errorf("FindNumber(%v, %d) = %v; want %v", tc.input, tc.search, result, tc.expected)
If the function’s output (result) doesn’t match the expected output (tc.expected), t.Errorf is called to report the mismatch.
Run Test:
Test should fail
go test
github.com/abhishekamralkar/numInList: no non-test Go files in /Users/abhishekamralkar/Code/go/programs/numInList
FAIL github.com/abhishekamralkar/numInList [build failed]
Function:
package findnumber
func FindNumber(l []int, n int) bool {
for _, i := range l {
if i == n {
return true
}
}
return false
}
Lets understand the findnumber
function
Package Declaration:
package findnumber
This line defines the package name as findnumber. In Go, every file must belong to a package. The package name here is chosen to logically group related functions, making it easier to manage code and reuse the FindNumber function in other parts of the application.
Function Signature:
func FindNumber(l []int, n int) bool
- func: This keyword declares a function.
- FindNumber: The name of the function. It’s descriptive, indicating the function’s purpose: to find a number in a slice.
- l []int: This is the first parameter, representing the slice of integers that the function will search through. In Go, slices are dynamically-sized arrays.
- n int: The second parameter is the integer that the function is looking for within the slice.
- bool: The return type of the function is bool, meaning it will return either true if the number is found, or false if it is not.
Iterating Over the Slice:
for _, i := range l {
• for _, i := range l: This loop iterates over each element i in the slice l.
• range: In Go, range is used to iterate over elements in a variety of data structures like slices, arrays, maps, etc.
- _, i: The loop returns two values on each iteration: the index and the value. Here, the index is ignored (_), and only the value i is used.
Checking for the Number:
if i == n {
return true
}
• if i == n: This condition checks if the current element i in the slice is equal to the number n that we are searching for.
- return true: If the condition is true (i.e., the number is found), the function immediately returns true, indicating that the number exists in the slice.
Returning false if the Number is Not Found:
return false
Check the full code here.