Lesson 1
Go Fundamentals: Slices and Strings
Introduction

Welcome to this course on Revisiting Go Basics, part of the Fundamental Coding Interview Preparation in Go course path!

Before delving into Go programming for interview preparation, let's explore some foundational concepts in Go. We'll focus on Go's slices and examine how strings are handled. These tools are crucial for organizing multiple elements, such as numbers or letters, within a single structure.

Understanding Go's Slices

Go's slices are dynamically-sized, flexible views into the elements of an array. Unlike arrays, slices are dynamic and allow you to change their contents, providing significant flexibility. Let’s see how to create and modify slices:

Go
1package main 2 3import "fmt" 4 5func main() { 6 // Defining a slice 7 mySlice := []int{1, 2, 3, 4} 8 9 // Printing the first (0-indexed) element of the slice 10 fmt.Println(mySlice[0]) 11 12 // Changing the first element of the slice 13 mySlice[0] = 100 14 15 // Now, mySlice contains {100, 2, 3, 4} 16 fmt.Println(mySlice) 17}

In this simple snippet:

  • We define mySlice as a slice of integers, containing the elements 1, 2, 3, 4.
  • We access the first element via indexing using mySlice[0]
  • We update the first element by assigning a new value mySlice[0] = 100
Diving Into Slices

Slices in Go serve as a powerful abstraction over arrays. They allow you to efficiently manage collections of data, providing several built-in functions for manipulation. These operations include appending elements, slicing, and removing elements.

append() is a function that adds a new element to the end of a slice, effectively increasing its size. To remove elements, you can employ slicing techniques; for example, mySlice[1:] selects all elements of mySlice starting from index 1, effectively removing the element at index 0.

For many other operations, such as finding an element in a slice, the idiomatic way in Go to carry out such operations is to simply employ for loops. The example below illustrates these operations:

Go
1package main 2 3import "fmt" 4 5 6func main() { 7 // Creating a slice 8 fruits := []string{"apple", "banana", "cherry"} 9 10 // Adding a new element using append 11 fruits = append(fruits, "date") 12 13 // Inserting an element by manual slicing 14 fruits = append(fruits[:1], append([]string{"bilberry"}, fruits[1:]...)...) 15 16 // Finding the position of 'banana' and removing it 17 for i, fruit := range fruits { 18 if fruit == "banana" { 19 fruits = append(fruits[:i], fruits[i+1:]...) 20 break 21 } 22 } 23 24 // Accessing elements using indexing 25 firstFruit := fruits[0] // "apple" 26 lastFruit := fruits[len(fruits)-1] // "date" 27 28 // Now, fruits equals {"apple", "bilberry", "cherry", "date"} 29 fmt.Println(fruits) 30 fmt.Println(firstFruit, lastFruit) 31}

Here, append() adds "date" to the end of the fruits slice. Manual slicing is utilized to insert "bilberry", and a loop helps locate and remove "banana". The slicing operation for inserting "bilberry" involves breaking the slice and reconstructing it with the new element. The three dots (...) notation in fruits[1:]... is called the "variadic argument" expansion. It allows you to pass elements of fruits[1:] as separate arguments to the append() function, effectively appending each element individually.

Understanding Strings

In Go, strings are sequences of bytes, typically representing UTF-8 encoded text. Strings are immutable, which means once created, their contents cannot be changed. However, you can manipulate them with package functions like strings.ToLower, strings.ToUpper, strings.Split, strings.Trim, or strings.Replace. We'll discuss some of these functions in more detail in a later unit, but for now let's see a couple of basic operations in action:

Go
1package main 2 3import ( 4 "fmt" 5 "strings" 6) 7 8func main() { 9 // Creating a string 10 greeting := "HeLLo, WoRlD!" 11 12 // Accessing characters using indexing 13 firstChar := greeting[0] // 'H' 14 lastChar := greeting[len(greeting)-1] // '!' 15 16 // Lowercasing the entire string 17 lowerGreeting := strings.ToLower(greeting) 18 // greeting becomes "hello, world!" 19 20 // Convert the string back to uppercase 21 upperGreeting := strings.ToUpper(greeting) 22 // greeting becomes "HELLO, WORLD!" 23 24 fmt.Printf("%c %c\n", firstChar, lastChar) 25 fmt.Println(lowerGreeting) 26 fmt.Println(upperGreeting) 27}

In this example, we use indexing to access the first and last characters in a string. To print them as characters, we utilize fmt.Printf with the %c verb to convert their byte values to characters. Additionally, we showcase strings.ToLower and strings.ToUpper functions, which convert the string to lowercase and uppercase, respectively.

Indexing and Common Operations

In Go, both slices and strings use zero-based indexing for element access. This forms the basis for various useful operations, such as slicing, concatenation, and finding elements.

  1. Slicing: This operation allows you to extract a portion of a slice or string using straightforward syntax: slice[start:end]. Note that, similarly to many other languages, the start index is inclusive but the end index is not.

  2. Concatenation: Use append() for slices and the + operator for strings to join elements.

  3. Finding elements: Search for elements in a slice using loops, or use the strings package for string-specific operations.

Go
1package main 2 3import ( 4 "fmt" 5 "sort" 6 "strings" 7) 8 9func main() { 10 // Define a slice and a string 11 mySlice := []int{1, 2, 3, 4, 5} 12 greeting := "Hello" 13 14 // Slicing: get elements within a range 15 slicePart := mySlice[2:4] // Extract elements from index 2 to 3 16 sliceString := greeting[1:3] // Retrieves 'el' 17 18 // Concatenation: join slices and strings 19 concatenateSlice := append(mySlice, 6, 7, 8) 20 concatenateString := greeting + ", world!" 21 22 // Finding and counting occurrences 23 count := 0 24 for _, v := range mySlice { 25 if v == 2 { 26 count++ 27 } 28 } 29 countL := strings.Count(greeting, "l") 30 31 // Sorting items in a slice 32 sort.Sort(sort.Reverse(sort.IntSlice(mySlice))) // Sorts in descending order 33 34 fmt.Println(slicePart) 35 fmt.Println(sliceString) 36 fmt.Println(concatenateSlice) 37 fmt.Println(concatenateString) 38 fmt.Println(count, countL) 39 fmt.Println(mySlice) 40}

Here, append() concatenates slices, and strings.Count finds occurrences of a substring in a string. The sort package is also used to sort elements in mySlice in descending order.

Lesson Summary and Practice

Excellent work! You've now explored the basic structure of Go's slices and strings, learning how to create, access, and manipulate these data structures using various operations.

Next, solidify your understanding through hands-on practice with Go-specific exercises. Grasping these concepts and frequent practice will equip you to tackle more complex problem-solving tasks effortlessly. Happy coding!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.