Accessing strings contained in slice - go

I am working on some coding exercises to better understand Go. A given exercise instructs me to create a program that will accept user input as follows:
The first line specifies how many strings will be provided as input on separate lines
The subsequent N lines will each be single strings
I am to output the characters corresponding to even and odd indices of each string separated by a space, and each string on it's separate line.
Example Input:
2
foo_bar
fizz_buzz
Should Output:
fobr o_a
fz_uz izbz
But in my program accessing a slice of strings returns an empty string:
package main
import (
"fmt"
)
func main() {
// read an integer describing how many strings will be input
var num_strings int
fmt.Scan(&num_strings)
// create a slice of strings to hold provided strings
strings := make([]string, num_strings)
// add provided strings to slice
for i := 0; i < num_strings; i++ {
var temp string
fmt.Scan(&temp)
strings = append(strings, temp)
}
// check that strings have been appended
fmt.Println("Strings:", strings)
// check that strings can be accessed
for i := 0; i < num_strings; i++ {
fmt.Println(i, strings[i]) // only i prints, not strings[i]
}
// loop over all strings
for i := 0; i < num_strings; i++ {
// if string index is even print the char
for index, val := range strings[i] {
if index%2 == 0 {
fmt.Print(val)
}
}
fmt.Print(" ")
// if string index is odd print the char
for index, val := range strings[i] {
if index%2 != 0 {
fmt.Print(val)
}
}
// newline for next string
fmt.Print("\n")
}
}
2
foo_bar
fizz_buzz
Strings: [ foo_bar fizz_buzz]
0
1

Because when you make your strings slice, you're creating a slice with both a capacity and length of n. So when you append to it, you're increasing the length of the slice:
Change this bit of code:
// create a slice of strings to hold provided strings
strings := make([]string, num_strings)
// add provided strings to slice
for i := 0; i < num_strings; i++ {
var temp string
fmt.Scan(&temp)
strings = append(strings, temp)
}
to either:
// create a slice of strings to hold provided strings
strings := []{}
// add provided strings to slice
for i := 0; i < num_strings; i++ {
var temp string
fmt.Scan(&temp)
strings = append(strings, temp)
}
Or
// create a slice of strings to hold provided strings
strings := make([]string, num_strings)
// add provided strings to slice
for i := 0; i < num_strings; i++ {
var temp string
fmt.Scan(&temp)
strings[i] = temp
}
And you should be good.

Related

Reading from a slice of unknown length in Golang

I'm trying to replicate this algorithm for finding duplicates in an array in Golang. Here's the javascript version:
function hasDuplicateValue(array) {
let existingNumbers = [];
for(let i = 0; i < array.length; i++) {
if(existingNumbers[array[i]] === 1) {
return true;
} else {
existingNumbers[array[i]] = 1;
}
}
return false;
}
On line 2, the algorithm creates an empty array of unknown length, and then adds 1 to an index in the array corresponding with each number that it finds (e.g. if it finds the number 3 in the array, it will add a 1 to index 3 in existing numbers.
I'm wondering — how do I replicate this in Golang (since we need to have slots allocated in the slice before reading it). Would I first need to find the max value in the array and then declare the existingNumbers slice to be of that same size?
Or is there a more efficient way of doing this (instead of searching through the array and finding the max value before constructing the slice).
Thanks!
Edit:
I realized that I can't do this with a slice because I can't read from an empty value. However, as #icza suggested, it will work with a map:
func findDuplicates(list []int)(bool) {
temp := make(map[int]int)
for _, elem := range list {
if temp[elem] == 1 {
return true
} else {
temp[elem] = 1
}
}
return false
}
As comments, I would also suggest using a map to keep the state of the duplications, but we can use map[int]struct{} because empty structs are not consumed any memory in Go.
And also I have simplified the code a bit and it is as follows.
func findDuplicates(list []int) bool {
temp := make(map[int]struct{})
for _, elem := range list {
if _, ok := temp[elem]; ok {
return true
}
temp[elem] = struct{}{}
}
return false
}
Full code can be executed here

What does slice[0:0] do in Go?

I recently saw the following code in a Golang markdown parser:
blankLines := make([]lineStat, 0, 128)
isBlank := false
for { // process blocks separated by blank lines
_, lines, ok := reader.SkipBlankLines()
if !ok {
return
}
lineNum, _ := reader.Position()
if lines != 0 {
blankLines = blankLines[0:0]
l := len(pc.OpenedBlocks())
for i := 0; i < l; i++ {
blankLines = append(blankLines, lineStat{lineNum - 1, i, lines != 0})
}
}
I'm confused as to what blankLines = blankLines[0:0] does. Is this a way to prepend to an array?
This slicing [0:0] creates a slice that has the same backing array, but zero length. All it's really doing is "resetting" the len on the slice so that the underlying array can be re-used. It avoids the allocation that may be required if a completely new slice was created for each iteration.

How to combine a slice and array into map

I'm trying to create a morse code map in Go. How do I do this? I can also convert one slice into an array if that makes it any easier.
alphabet := []string{}
for i := 'A'; i <= 'Z'; i++ {
alphabet = append(alphabet, string(i))
}
fmt.Println(alphabet)
morseSlice := [26]string{".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."}
// convert to map that maps letters to morse
var morseDict map[string]string
for i := 0; i > len(morseSlice); i++ {
morseDict[alphabet[i]] = morseSlice[i]
}
fmt.Println(morseDict)
but my map is empty. What am I doing wrong?
Your code will throw an error assignment to entry in nil map. To resolve that, you need to initialise it.
elementMap := make(map[string]string) //Initialize
And your condition should be changed to less than or you can use range
for i, ele := range alphabet {
elementMap[ele] = morseSlice[i]
}
playground

Find the most frequent character in text

I need to implement a package with interface with methods that take text file and performs analysis on it - counts the total amount of characters and finds the most frequent symbol and word. To find the most frequent character I loop through each rune in the text, convert it to string and append it as a key to map. The value is an incremented counter which counts how often this character occurs in the given text. Now I'm stuck a little with the following problem -- I can't figure out how to get the key with the highest value in my map. Here's the code:
package textscanner
import (
"fmt"
"log"
"io/ioutil"
"unicode/utf8"
"strconv"
)
// Initializing my scanner
type Scanner interface {
countChar(text string) int
frequentSym(text string) // Return value is not yet implemented
Scan()
Run()
}
/* method counting characters */
func countChar(sc Scanner, text string) int { ... }
func frequentSym(sc Scanner, text string) {
// Make a map with string key and integer value
symbols := make(map[string] int)
// Iterate through each char in text
for _, sym := range text {
// Convert rune to string
char := strconv.QuoteRune(sym)
// Set this string as a key in map and assign a counter value
count := symbols[char]
if count == symbols[char] {
// increment the value
symbols[char] = count + 1
} else {
symbols[char] = 1
}
}
}
So, basically I need to find a pair with the highest int value and return a string key that corresponds to it, that is the most frequent character in text
Just iterate over the map:
maxK := ""
maxV := 0
for k, v := range symbols {
if v > maxV {
maxV = v
maxK = k
}
}
// maxK is the key with the maximum value.
Expanding on #Ainar-G answer, if there is a possibility that your map could contain multiple keys that occur the same number of times, then #Ainar-G code could return different results every time because Go maps are inherently unordered; in other words, the first key in your map to have a value higher then all previous values becomes the highest key, but you don't always know whether that value will occur first in the map. See this as an example.
In order for the code to be deterministic, you will need to address the case where two keys have the same value. A simple implementation would be to do a string comparison if the value is the same.
maxK := ""
maxV := 0
for k, v := range symbols {
if v > maxV || (v == maxV && k < maxK) {
maxV = v
maxK = k
}
}

How can I easily get a substring in Go while guarding against "slice bounds out of range" error?

Using Go, I want to truncate long strings to an arbitrary length (e.g. for logging).
const maxLen = 100
func main() {
myString := "This string might be longer, so we'll keep all except the first 100 bytes."
fmt.Println(myString[:10]) // Prints the first 10 bytes
fmt.Println(myString[:maxLen]) // panic: runtime error: slice bounds out of range
}
For now, I can solve it with an extra variable and if statement, but that seems very long-winded:
const maxLen = 100
func main() {
myString := "This string might be longer, so we'll keep all except the first 100 bytes."
limit := len(myString)
if limit > maxLen {
limit = maxLen
}
fmt.Println(myString[:limit]) // Prints the first 100 bytes, or the whole string if shorter
}
Is there a shorter/cleaner way?
Use a simple function to hide the implementation details. For example,
package main
import "fmt"
func maxString(s string, max int) string {
if len(s) > max {
r := 0
for i := range s {
r++
if r > max {
return s[:i]
}
}
}
return s
}
func main() {
s := "日本語"
fmt.Println(s)
fmt.Println(maxString(s, 2))
}
Output:
日本語
日本
Assuming you want to keep at most maxLen characters, i.e. what your code says, rather than what your string says.
If you don't need the original myString, you can overwrite it like this:
const maxLen = 100
func main() {
myString := "This string might be longer, so we'll keep the first 100 bytes."
if len(myString) >= maxLen {
myString = myString[:maxLen] // slicing is a constant time operation in go
}
fmt.Println(myString) // Prints the first 100 bytes, or the whole string if shorter
}
This might cut unicode characters in half, leaving some garbage at the end. If you need to handle multi-byte unicode, which you probably do, try this:
func main() {
myString := "日本語"
mid := maxLen
for len(myString) >= mid && utf8.ValidString(myString[:mid]) == false {
mid++ // add another byte from myString until we have a whole multi-byte character
}
if len(myString) > mid {
myString = myString[:mid]
}
fmt.Println(myString) // Prints the first 100 bytes, or the whole string if shorter
}
Or, if you can accept removing up to one character from the output, this version is a bit cleaner
func main() {
myString := "日本語"
for len(myString) >= maxLen || utf8.ValidString(myString) == false {
myString = myString[:len(myString)-1] // remove a byte
}
fmt.Println(myString) // Prints the first 100 bytes, or the whole string if shorter
}

Resources