I want to parse a string xxxxx:yyyyy:zzz.aaa.bbb.cc:dd:ee:ff to a struct in Go, how can I do it with multiple delimiter ':' and '.'.
Edit:
I want to split the string "xxxxx:yyyyy:zzz.aaa.bbb.cc:dd" into below struct type
type Target struct {
Service string
Type string
Domain string
Plan string
Host string
Region string
Other string
}
So that
Service = xxxxx
Type = yyyyy
Domain = zzzz
Plan = aaa
Host = bbb
Region = cc
Other = dd
You may use
strings.FieldsFunc(input, Split)
Try it on The Go Playground:
package main
import (
"fmt"
"strings"
)
func main() {
input := `xxxxx:yyyyy:zzz.aaa.bbb.cc:dd:ee:ff`
a := strings.FieldsFunc(input, Split)
t := Target{a[0], a[1], a[2], a[3], a[4], a[5], a[6]}
fmt.Println(t) // {xxxxx yyyyy zzz aaa bbb cc dd}
}
func Split(r rune) bool {
return r == ':' || r == '.'
}
type Target struct {
Service string
Type string
Domain string
Plan string
Host string
Region string
Other string
}
output:
{xxxxx yyyyy zzz aaa bbb cc dd}
You can use regex for splitting your string
import "regexp"
func splitWord(word string) []string {
array := regexp.MustCompile("[\\:\\,\\.\\s]+").Split(word, -1)
return array
}
You can use this function, which can split a string by multiple runes:
import "fmt"
import "strings"
func SplitAny(s string, seps string) []string {
splitter := func(r rune) bool {
return strings.ContainsRune(seps, r)
}
return strings.FieldsFunc(s, splitter)
}
func main() {
words := SplitAny("xxxxx:yyyyy:zzz.aaa.bbb.cc:dd:ee:ff", ".:")
fmt.Println(strings.Join(words, " "))
}
Output:
xxxxx yyyyy zzz aaa bbb cc dd ee ff
Or even with one line of code:
words := strings.FieldsFunc(s, func(r rune) bool { return strings.ContainsRune(" .:", r) })
Here is a generic function that will take a string as a set of runes to split on.
func Splitter(s string, splits string) []string {
m := make(map[rune]int)
for _, r := range splits {
m[r] = 1
}
splitter := func(r rune) bool {
return m[r] == 1
}
return strings.FieldsFunc(s, splitter)
}
func TestSplit() {
words := Splitter("orange apple-banana", " -")
}
Alright. This isn't a very elegant solution but it should at least get you started and works for the specific example you've given. In reality you'd probably want to add some error handling or generalize the logic a bit to work with a broader set of inputs.
type Target struct {
Service string
Type string
Domain string
Plan string
Host string
Region string
Other string
}
func main() {
input := `xxxxx:yyyyy:zzz.aaa.bbb.cc:dd:ee:ff`
t := Target{}
tokens := strings.Split(input, ":")
t.Service = tokens[0]
t.Type = tokens[1]
subtokens := strings.Split(tokens[2], ".")
t.Domain = subtokens[0]
t.Plan = subtokens[1]
t.Host = subtokens[2]
t.Region = subtokens[3]
t.Other = tokens[3]
fmt.Printf("%v", t)
}
Working example here;
https://play.golang.org/p/57ZyOfdbvo
Related
I am new to the language GO and working on an assignment where i should write a code that return the word frequencies of the text. However I know that the words 'Hello', 'HELLO' and 'hello' are all counted as 'hello', so I need to convert all strings to lower case.
I know that I should use strings.ToLower(), however I dont know where I should Included that in the class. Can someone please help me?
package main
import (
"fmt"
"io/ioutil"
"log"
"strings"
"time"
)
const DataFile = "loremipsum.txt"
// Return the word frequencies of the text argument.
func WordCount(text string) map[string]int {
fregs := make(map[string]int)
words := strings.Fields(text)
for _, word := range words {
fregs[word] += 1
}
return fregs
}
// Benchmark how long it takes to count word frequencies in text numRuns times.
//
// Return the total time elapsed.
func benchmark(text string, numRuns int) int64 {
start := time.Now()
for i := 0; i < numRuns; i++ {
WordCount(text)
}
runtimeMillis := time.Since(start).Nanoseconds() / 1e6
return runtimeMillis
}
// Print the results of a benchmark
func printResults(runtimeMillis int64, numRuns int) {
fmt.Printf("amount of runs: %d\n", numRuns)
fmt.Printf("total time: %d ms\n", runtimeMillis)
average := float64(runtimeMillis) / float64(numRuns)
fmt.Printf("average time/run: %.2f ms\n", average)
}
func main() {
// read in DataFile as a string called data
data, err:= ioutil.ReadFile("loremipsum.txt")
if err != nil {
log.Fatal(err)
}
// Convert []byte to string and print to screen
text := string(data)
fmt.Println(text)
fmt.Printf("%#v",WordCount(string(data)))
numRuns := 100
runtimeMillis := benchmark(string(data), numRuns)
printResults(runtimeMillis, numRuns)
}
You should convert words to lowercase when you are using them as map key
for _, word := range words {
fregs[strings.ToLower(word)] += 1
}
I get [a:822 a.:110 I want all a in the same. How do i a change the code so that a and a. is the same? – hello123
You need to carefully define a word. For example, a string of consecutive letters and numbers converted to lowercase.
func WordCount(s string) map[string]int {
wordFunc := func(r rune) bool {
return !unicode.IsLetter(r) && !unicode.IsNumber(r)
}
counts := make(map[string]int)
for _, word := range strings.FieldsFunc(s, wordFunc) {
counts[strings.ToLower(word)]++
}
return counts
}
to remove all non-word characters you could use a regular expression:
package main
import (
"bufio"
"fmt"
"log"
"regexp"
"strings"
)
func main() {
str1 := "This is some text! I want to count each word. Is it cool?"
re, err := regexp.Compile(`[^\w]`)
if err != nil {
log.Fatal(err)
}
str1 = re.ReplaceAllString(str1, " ")
scanner := bufio.NewScanner(strings.NewReader(str1))
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println(strings.ToLower(scanner.Text()))
}
}
See strings.EqualFold.
Here is an example.
I was trying to write a stringReverse function which reverses the given string:
func reverseString(s string) []rune
well, i did succeed but the problem is there are spaces after reversing it.
Input: "Hello, 世界"
Output: [界 世 , o l l e H]
code of function :
func reverseString(s string) []rune {
// sir: string in rune
sir := []rune(s)
for i, _ := range sir {
sir[i], sir[len(sir)-i-1] = sir[len(sir)-i-1], sir[i]
if i >= len(sir)-i-1 {
return sir
}
}
return sir
}
i did not do any error implementation since i am new to go.
Thanks for your help.
To reverse strings,you can declare an empty string and then start appending the characters from the end, one by one.Here is a sample code for the same logic:
package main
import "fmt"
func reverse(str string) (result string) {
for _, v := range str {
result = string(v) + result
}
return
}
func main() {
runeStr := "Hello, 世界"
fmt.Println(runeStr)
runeRev := reverse(runeStr)
fmt.Println(runeRev)
}
Output:
Hello, 世界
界世 ,olleH
try to
func reverseString(s string) []rune {
// sir: string in rune
sir := []rune(s)
for i, _ := range sir {
sir[i], sir[len(sir)-i-1] = sir[len(sir)-i-1], sir[i]
if i+1 >= len(sir)/2 {
return sir
}
}
return sir
}
i have a string in golang :
"hi hi hi ho ho hello"
I would like to remove duplicates word to keep only one to obtain this :
"hi ho hello"
There are multiple way to achive this. One is this:
import "strings"
func Dedup(input string) string {
unique := []string{}
words := strings.Split(input, " ")
for _, word := range words {
// If we alredy have this word, skip.
if contains(unique, word) {
continue
}
unique = append(unique, word)
}
return strings.Join(unique, " ")
}
func contains(strs []string, str string) bool {
for _, s := range strs {
if s == str {
return true
}
}
return false
}
package main
import "fmt"
func removeDuplicates(arr []string) []string {
words_string := map[string]bool{}
for i:= range arr {
words_string[arr[i]] = true
}
desired_output := []string{} // Keep all keys from the map into a slice.
for j, _ := range words_string {
desired_output = append(desired_output, j)
}
return desired_output
}
func main() {
arr := []string{"hi", "hi", "hi", "ho", "ho", "hello"}
fmt.Println(arr)
desired_output := removeDuplicates(arr) // Remove the duplicates
fmt.Println(desired_output)
}
type mcat struct {
ID int
}
type cat struct {
Name string
M mcat
}
func getValue(path string, mcat cat){
//throuth struct path get the value
}
func main(){
mycat := cat{"cat", mcat{1}}
id := getvalue("/M/ID", mycat)
}
Can I do this by reflecting to get a value based on the field name?
You may do what you want with the Value.FieldByName() function. Just range over the parts of the path which may be splitted using strings.Split().
Here's an example:
func getValue(i interface{}, path string) interface{} {
v := reflect.ValueOf(i)
for _, field := range strings.Split(path[1:], "/") {
v = v.FieldByName(field)
}
return v.Interface()
}
func main() {
mycat := cat{"cat", mcat{1}}
id := getValue(mycat, "/M/ID")
fmt.Println(id)
}
It outputs (try it on the Go Playground):
1
Some things to note:
The above solution works for all struct types, not just with cat. Checks if the passed value is a struct or the field exists is omitted.
I cut of the leading / of the path with a slice expression: path[1:] so we don't have to deal with an empty field name inside the loop.
The above getValue() returns the result as an interface{}. If you need the ID as an int, you may use type assertion like this:
var intID int
intID = id.(int)
Also note that it may be nicer / more useful to use a variadic parameter for the path:
func getValue(i interface{}, path ...string) interface{} {
v := reflect.ValueOf(i)
for _, field := range path {
v = v.FieldByName(field)
}
return v.Interface()
}
func main() {
mycat := cat{"cat", mcat{1}}
id := getValue(mycat, "M", "ID")
fmt.Println(id)
}
Output is the same. Try this one on the Go Playground.
With this code, is there a better way to loop through all the users and create a new string containing all their Nick values?
package main
import "fmt"
type User struct {
Nick string
}
func main() {
var users [2]User
users[0] = User{ Nick: "Radar" }
users[1] = User{ Nick: "NotRadar" }
names := ":"
for _, u := range users {
names += u.Nick + " "
}
fmt.Println(names)
}
For example,
package main
import (
"bytes"
"fmt"
)
type User struct {
Nick string
}
func main() {
var users [2]User
users[0] = User{Nick: "Radar"}
users[1] = User{Nick: "NotRadar"}
var buf bytes.Buffer
buf.WriteByte(':')
for _, u := range users {
buf.WriteString(u.Nick)
buf.WriteByte(' ')
}
names := buf.String()
fmt.Println(names)
}
This avoids a lot of allocations due to the concatenation of strings.
You could also write:
package main
import (
"fmt"
)
type User struct {
Nick string
}
func main() {
var users [2]User
users[0] = User{Nick: "Radar"}
users[1] = User{Nick: "NotRadar"}
var buf []byte
buf = append(buf, ':')
for _, u := range users {
buf = append(buf, u.Nick...)
buf = append(buf, ' ')
}
names := string(buf)
fmt.Println(names)
}
It really looks like you want a strings.Join here. You probably want to avoid that tight loop of repeated string concatenations in the original code; I'm fairly certain that Go doesn't implement a rope-like data structure for its primitive strings.
package main
import (
"fmt"
"strings"
)
type User struct {
Nick string
}
func main() {
var users [2]User
users[0] = User{Nick: "Radar"}
users[1] = User{Nick: "NotRadar"}
userNames := []string{}
for _, u := range users {
userNames = append(userNames, u.Nick)
}
names := ":" + strings.Join(userNames, " ")
fmt.Println(names)
}
Unfortunately, I do not know of a more elegant way to write that code.
Go does have a String.Join method so if you made a helper that converted your array of users to a slice of strings ([]string) then you could pass that to String.Join.
I think that Go's static typing and lack of templates makes it hard to write a general purpose map function like Ruby has.
This is what I was talking about in the comments of dyoo's post. Effectively a rewrite of join to prevent having to iterate over the list an extra time and allocate an extra slice.
func Usernames(users []User) string {
if len(users) == 0 {
return ""
}
if len(users) == 1 {
return users[0].Name
}
sep := " "
n := len(users)-1 // From len(sep) * len(a)-1, sep is always len 1 unlike in Join
for i := 0; i < len(users); i++ {
n += len(users[i].Name)
}
names := make([]byte,n)
namesp := copy(names, users[0].Name)
for _,u := range users[1:] {
namesp += copy(names[namesp:], sep)
namesp += copy(names[namesp:], u.Name)
}
return string(names)
}
For reference, strings.go with the strings.Join source:
http://golang.org/src/pkg/strings/strings.go
See line 356