I'm working on a text wrapping function. I want it to break a long line of text into string slices of a maximum length of characters. I've got it mostly working. However, sometimes the words are placed out of order.
This happens when there is a long word followed by a short word. I believe the program sees the longer word will not fit on the line so it skips that word and adds in the next word that will fit.
As this is text, the words must stay in the correct order. How can I force the loop to only add words in the correct order?
Actual Output:
[]string{" Go back out of the hotel entrance and your is", " room on lower ground a private street", " entrance."}
Expected Output:
[]string{" Go back out of the hotel entrance and your", " room is on lower ground a private street", " entrance."}
This is what I have so far.
Link: https://play.golang.org/p/YsCWoM9hQJV
package main
import (
"fmt"
"strings"
)
func main() {
directions := "Go back out of the hotel entrance and your room is on the lower ground a private street entrance."
ws := strings.Split(directions, " ")
neededSlices := strings.Count(directions, "") / 48
if strings.Count(directions, "")%48 != 0 {
neededSlices++
}
ls := make([]string, neededSlices, neededSlices)
keys := make(map[string]bool)
for i := 0; i < len(ls); i++ {
for _, v := range ws {
if _, ok := keys[v]; !ok {
if strings.Count(ls[i], "")+strings.Count(v, "") <= 48 {
ls[i] = ls[i] + " " + v
keys[v] = true
}
}
}
}
fmt.Printf("%#v", ls)
}
I think this is simple implementation of what you need
package main
import (
"fmt"
"strings"
)
func main() {
directions := "Go back out of the hotel entrance and your room is on the lower ground a private street entrance."
ws := strings.Split(directions, " ")
sl := []string{""}
i := 0
for _,word := range ws {
if (len(sl[i]) + len(word) + 1) >=48 {
i++
sl = append(sl, "")
}
sl[i] += " " + word
}
fmt.Printf("%#v", sl)
}
Link: https://play.golang.org/p/7R2TS6lv4Tm
The first problem I notice is your usage of a map. A map can only contain a key once. Due to this, your code will only contain each word once in one of the output slices.
The second problem is that you iterate over the whole ws array again for each iteration of the ls slice. I guess you tried to work around this issue with the map?
The solution would be to iterate only once over ws and assign the words to the index in ls.
Also note that strings.Count returns the number of characters (unicode points) in the string plus 1.
Your code btw also adds a space at the beginning of each string in the slice. I am not sure if this is intended (your expected output matches this). In my example solution I deviate from that so my output does not 100% match your stated expected output but I think it gives a more expected result.
package main
import (
"fmt"
"strings"
)
func main() {
directions := "Go back out of the hotel entrance and your hotel room is on the lower ground a private street entrance."
ws := strings.Split(directions, " ")
ls := []string{}
l := 0 // current line length
i := -1
for _, v := range ws {
lv := strings.Count(v, "")-1
if l == 0 || l+lv+1 > 48 {
i++
ls = append(ls, v)
l = lv
continue
}
ls[i] += " " + v
l += lv+1
}
fmt.Printf("%#v", ls)
}
Go Playground: https://play.golang.org/p/HhdX8RudiXn
Related
The task is to add zeros to string elements of 2d slice. So the stdin is [["7" "3" "1"]["2" "9"]] and I need to add zeros from the last element of each slice to the first one. For each step the counter of zeros is incremented by +1. Therefore, stdout is expected to be [["700", "30", "1"]["20", "9"]].
I have tried to do such an algorithm but can't get expected answer. Here is my code:
package main
import (
"fmt"
"strings"
)
func addZero(strs [][]string) [][]string {
zero := "0"
counter := 0
for i := range strs {
for j := range strs[i] {
strs[i][j] += strings.Repeat(zero, counter)
}
counter++
}
return strs
}
func main() {
fmt.Println(addZero([][]string{{"7", "3", "1"}, {"2", "9"}}))// here the result is [[7 3 1] [20 90]]
}
How to change my code to get an expected answer ?
Counting zeros has to reset in each line, so move that code inside the first loop.
Also range goes from index 0, and you want increasing zeroes from the end of lines, so counter has to start from len(strs[i])-1, and you have to decrement it:
func addZero(strs [][]string) [][]string {
for i := range strs {
zero := "0"
counter := len(strs[i]) - 1
for j := range strs[i] {
strs[i][j] += strings.Repeat(zero, counter)
counter--
}
}
return strs
}
With these changes output will be (try it on the Go Playground):
[[700 30 1] [20 9]]
Note that if you would process lines from the end, the suffix to append (the zeros) would increase. So you could ditch strings.Repeat() by keeping and "extending" the previous suffix:
func addZero(strs [][]string) [][]string {
for _, line := range strs {
zeros := ""
for i := len(line) - 1; i >= 0; i-- {
line[i] += zeros
zeros += "0"
}
}
return strs
}
This outputs the same. Try it on the Go Playground.
Also note that strings can be sliced, and the result shares the memory with the sliced string. So it's fast and does not create garbage! You could build a single, long zeros string holding just zeros, and you could slice this long string to have as many zeros as you need to append. This solution avoids any unnecessary string allocations and concatenations:
var zeros = strings.Repeat("0", 1000) // Use the maximum length here
func addZero(strs [][]string) [][]string {
for _, line := range strs {
count := len(line) - 1
for i := range line {
line[i] += zeros[:count-i]
}
}
return strs
}
This again outputs the same, try it on the Go Playground.
Let say I have input and output string where the output will be the frequency of each elements in the string and the char itself
input := "programming"
output := "p2ro2ga2min"
How can I print it based on index after I found the freq of distinct character
This is my code
func countFreq(s string) {
sMap := make(map[string]int)
for _, v := range s {
sMap[string(v)]++
}
for i, v := range sMap {
fmt.Printf("%v%v", i, v)
}
// Don't know what to do next
}
The output of code is
output: n1p1r2o1g2a1m2i1
#icza's answer is great. Here's an alternative that I thought of before but only just got around to writing.
It uses a string to keep track of the rune order but you could use a string builder if speed is important.
func countFreq(s string) {
sMap := make(map[rune]int)
sOut := ""
for _, c := range s {
sMap[c]++
if sMap[c] == 1 {
sOut += string(c)
}
}
for _, c := range sOut {
if sMap[c] > 1 {
fmt.Print(sMap[c])
}
fmt.Printf("%c", c)
}
}
You're counting runes, so use a map of map[rune]int, so you can omit the conversions back to string.
Maps are unordered, so if you want the output in the same order as the input, you can't (shouldn't) iterate over the map.
Once you counted the letters, range over the input letters again, and get the frequency from the map, and remove it. If the count is greater than 1, also print the number.
func countFreq(s string) {
sMap := make(map[rune]int)
for _, v := range s {
sMap[v]++
}
for _, v := range s {
count := sMap[v]
if count == 0 {
continue // Char already printed and removed
}
delete(sMap, v)
if count > 1 {
fmt.Print(count)
}
fmt.Print(string(v))
}
}
Testing it:
for _, s := range []string{"programming", "ab", "aba", "aabcdeac"} {
fmt.Println("In:", s)
fmt.Print("Out: ")
countFreq(s)
fmt.Println()
}
This will output (try it on the Go Playground):
In: programming
Out: p2ro2ga2min
In: ab
Out: ab
In: aba
Out: 2ab
In: aabcdeac
Out: 3ab2cde
Trying to find the longest word using Go from a sentence.
At the moment I am using this method:
func longestWord(s string) string {
newArr := strings.Split(s, " ")
l := len(newArr[0])
long := newArr[0]
var result string
// fmt.Println(long)
for _, lenString := range newArr {
if len(lenString) > l {
// ll := len(lenString)
// l := len(lenString)
d := &l
p := &long
c := &result
*d = len(lenString)
*p = lenString
*c = lenString
// fmt.Println(lenString)
} else {
c := &result
*c = newArr[0]
}
}
return result
}
func main() {
args := "Monday Tuesday Friday Sunday Wednesday"
fmt.Println(longestWord(args))
}
But I'm not sure that this is the best method to achieve that. Is there any other elegant way to do that? I know that there is one more method by using sort, but I would prefer more using the way with iteration between words.
"Best" solution
We can even write it more compact than the other answers by taking advantage of the following:
using tuple assignments
initializing the best and its length with the zero values ("" and 0) and omitting the check for 0 words as the for range handles that properly
no need to store words as a local variable as it is only used in the loop
We lose nothing from readability:
func longestWord(s string) string {
best, length := "", 0
for _, word := range strings.Split(s, " ") {
if len(word) > length {
best, length = word, len(word)
}
}
return best
}
Testing it:
fmt.Printf("%q\n", longestWord(""))
args := "Monday Tuesday Friday Sunday Wednesday"
fmt.Printf("%q\n", longestWord(args))
Output (try it on the Go Playground):
""
"Wednesday"
Most compact solution
Note that storing the length of the best is optional and is purely for optimization purposes, since if we have best, its length is always len(best).
Taking advantage of this, and that we can use named result parameters (and that all variables are initialized to the zero value of their types unless an initial value is provided–which for string is ""), we can even write it more compact, again losing nothing from readability:
func longestWord(s string) (best string) {
for _, word := range strings.Split(s, " ") {
if len(word) > len(best) {
best = word
}
}
return
}
Testing and output is the same, try it on the Go Playground. Again, in most cases this is probably slightly slower compared to when we stored the length too.
That totally works! You could make it a bit shorter, while also using longer variable names that explain a bit more about your intention.
func longestWord(s string) string {
words := strings.Split(s, " ")
if len(words) == 0 {
return ""
}
best := words[0]
best_length := 0
for _, word := range words {
if len(word) > best_length {
best = word
best_length = len(word)
}
}
return best
}
You could change this to track a pointer instead of the word itself if you like.
I would do it like this:
func longestWord(s string) string {
newArr := strings.Split(s, " ")
longestWord := ""
longestLength := 0
// loop through the array
for _, word := range newArr {
// save length of word in the actual iteration
length := len(word)
// if length is larger, than longest
if length > longestLength {
// save the new longest word
longestWord = word
longestLength = length
}
}
// return the longest word
return longestWord
}
Implementation can be found on the go playground
I know that for counting the occurrence of one substring I can use "strings.Count(, )". What if I want to count the number of occurrences of substring1 OR substring2? Is there a more elegant way than writing another new line with strings.count()?
Use a regular expression:
https://play.golang.org/p/xMsHIYKtkQ
aORb := regexp.MustCompile("A|B")
matches := aORb.FindAllStringIndex("A B C B A", -1)
fmt.Println(len(matches))
Another way to do substring matching is with the suffixarray package. Here is an example of matching multiple patterns:
package main
import (
"fmt"
"index/suffixarray"
"regexp"
)
func main() {
r := regexp.MustCompile("an")
index := suffixarray.New([]byte("banana"))
results := index.FindAllIndex(r, -1)
fmt.Println(len(results))
}
You can also match a single substring with the Lookup function.
If you want to count the number of matches in a large string, without allocating space for all the indices just to get the length and then throwing them away, you can use Regexp.FindStringIndex in a loop to match against successive substrings:
func countMatches(s string, re *regexp.Regexp) int {
total := 0
for start := 0; start < len(s); {
remaining := s[start:] // slicing the string is cheap
loc := re.FindStringIndex(remaining)
if loc == nil {
break
}
// loc[0] is the start index of the match,
// loc[1] is the end index (exclusive)
start += loc[1]
total++
}
return total
}
func main() {
s := "abracadabra"
fmt.Println(countMatches(s, regexp.MustCompile(`a|b`)))
}
runnable example at Go Playground
I have a mask that contains a binary counting of cpu_ids (0xA00000800000 for 3 CPUs) which I want to convert into a string of comma separated cpu_ids: "0,2,24".
I did the following Go implementation (I am a Go starter). Is it the best way to do it? Especially the handling of byte buffers seems to be inefficient!
package main
import (
"fmt"
"os"
"os/exec"
)
func main(){
cpuMap := "0xA00000800000"
cpuIds = getCpuIds(cpuMap)
fmt.Println(cpuIds)
}
func getCpuIds(cpuMap string) string {
// getting the cpu ids
cpu_ids_i, _ := strconv.ParseInt(cpuMap, 0, 64) // int from string
cpu_ids_b := strconv.FormatInt(cpu_ids_i, 2) // binary as string
var buff bytes.Buffer
for i, runeValue := range cpu_ids_b {
// take care! go returns code points and not the string
if runeValue == '1' {
//fmt.Println(bitString, i)
buff.WriteString(fmt.Sprintf("%d", i))
}
if (i+1 < len(cpu_ids_b)) && (runeValue == '1') {
//fmt.Println(bitString)
buff.WriteString(string(","))
}
}
cpuIds := buff.String()
// remove last comma
cpuIds = cpuIds[:len(cpuIds)-1]
//fmt.Println(cpuIds)
return cpuIds
}
Returns:
"0,2,24"
What you're doing is essentially outputting the indices of the "1"'s in the binary representation from left-to-right, and starting index counting from the left (unusal).
You can achieve the same using bitmasks and bitwise operators, without converting it to a binary string. And I would return a slice of indices instead of its formatted string, easier to work with.
To test if the lowest (rightmost) bit is 1, you can do it like x&0x01 == 1, and to shift a whole number bitwise to the right: x >>= 1. After a shift, the rightmost bit "disappears", and the previously 2nd bit becomes the 1st, so you can test again with the same logic. You may loop until the number is greater than 0 (which means it sill has 1-bits).
See this question for more examples of bitwise operations: Difference between some operators "|", "^", "&", "&^". Golang
Of course if we test the rightmost bit and shift right, we get the bits (indices) in reverse order (compared to what you want), and the indices are counted from right, so we have to correct this before returning the result.
So the solution looks like this:
func getCpuIds(cpuMap string) (r []int) {
ci, err := strconv.ParseInt(cpuMap, 0, 64)
if err != nil {
panic(err)
}
count := 0
for ; ci > 0; count, ci = count+1, ci>>1 {
if ci&0x01 == 1 {
r = append(r, count)
}
}
// Indices are from the right, correct it:
for i, v := range r {
r[i] = count - v - 1
}
// Result is in reverse order:
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return
}
Output (try it on the Go Playground):
[0 2 24]
If for some reason you need the result as a comma separated string, this is how you can obtain that:
buf := &bytes.Buffer{}
for i, v := range cpuIds {
if i > 0 {
buf.WriteString(",")
}
buf.WriteString(strconv.Itoa(v))
}
cpuIdsStr := buf.String()
fmt.Println(cpuIdsStr)
Output (try it on the Go Playground):
0,2,24