New go user here.
I have a slice of this struct objects:
type TagRow struct {
Tag1 string
Tag2 string
Tag3 string
}
Which yeilds slices like:
[{a b c} {d e f} {g h}]
I'm wondering how can I convert the resulting slice to a slice of strings like:
["a" "b" "c" "d" "e" "f" "g" "h"]
I tried to iterate over like:
for _, row := range tagRows {
for _, t := range row {
fmt.Println("tag is" , t)
}
}
But I get:
cannot range over row (type TagRow)
So appreciate your help.
For your specific case I would just do it "manually":
rows := []TagRow{
{"a", "b", "c"},
{"d", "e", "f"},
{"g", "h", "i"},
}
var s []string
for _, v := range rows {
s = append(s, v.Tag1, v.Tag2, v.Tag3)
}
fmt.Printf("%q\n", s)
Output:
["a" "b" "c" "d" "e" "f" "g" "h" "i"]
If you want it to dynamically walk through all fields, you may use the reflect package. A helper function which does that:
func GetFields(i interface{}) (res []string) {
v := reflect.ValueOf(i)
for j := 0; j < v.NumField(); j++ {
res = append(res, v.Field(j).String())
}
return
}
Using it:
var s2 []string
for _, v := range rows {
s2 = append(s2, GetFields(v)...)
}
fmt.Printf("%q\n", s2)
Output is the same:
["a" "b" "c" "d" "e" "f" "g" "h" "i"]
Try the examples on the Go Playground.
See similar questions with more complex examples:
Golang, sort struct fields in alphabetical order
How to print struct with String() of fields?
Related
I have a simple gota series and I want to remove duplicate from it.
how it is possible in go?
func main() {
new_series := series.New([]string{"b", "a", "c", "a", "d", "b"}, series.String, "COL.1")
fmt.Println(new_series)
}
[b a c a d b]
expected: [b a c d]
To represent a unique set of elements in go, use a map.
So to create a unique set of strings from a given list, use something like:
func setFromList(list []string) (set []string) {
ks := make(map[string]bool) // map to keep track of repeats
for _, e := range list {
if _, v := ks[e]; !v {
ks[e] = true
set = append(set, e)
}
}
return
}
and to apply this to an existing gota.Series:
func uniqueGotaSeries(s series.Series) series.Series {
return series.New(setFromList(s.Records()), s.Type(), s.Name)
}
Working playground example: https://play.golang.org/p/zqQM-0XxLF5
Output:
Orig: [b a c a d b]
Unique: [b a c d]
Trying to combine multiple slices using variadic,
I'm getting error: cannot initialize 2 variables with 1 value
How do I call this Combine function?
Here's the code:
func Combine(ss ...[]string) []string {
mp := map[string]bool{}
for _, s := range ss {
for _, v := range s {
if v != "" {
if _, ok := mp[v]; !ok {
mp[v] = true
}
}
}
}
combined := []string{}
for v := range mp {
combined = append(combined, v)
}
return combined
}
tests := []struct {
caseName string
s1 []string
s2 []string
want []string
}{
{
caseName: "Test combining 2 slices",
s1: []string{"a", "b", "c", "c", ""},
s2: []string{"a", "b", "z", "z", "", "y"},
want: []string{"a", "b", "c", "y", "z"},
},
}
actual, _ := Combine(test.s1, test.s2)
Your variadic calling parameters format is fine.
The error is due to your function Combine returning one item, not two:
// actual, _ := Combine(test.s1, test.s2) // fails as only one item is returned
actual := Combine(test.s1, test.s2)
I checked an existing answer but it's not similar to my case.
I need to pluck an element at the index and break out of the for loop at runtime based on Compare function.
Issues:
If element to pluck is found at 0 index, index-1 will throw slice bounds of range error and similarly if index+1 is greater than len(elements).
Question: What's the best concise way to achieve the above?
for index, element := range elements {
if element.Compare() == true {
elements = append(elements[:index-1], elements[index+1:]...)
break
}
}
Attempt
for index, element := range elements {
if element.Compare() == true {
if len(elements) > 1 {
elements = append(elements[:index-1], elements[index+1:]...)
} else if len(elements) == 1 {
delete(elements, 0)
}
break
}
}
Attempt 2 Playground any improvements/suggestions?
The idea is to copy the remaining elements from beginning to index and then any elements after.
var elements = []string {"a", "b", "c", "d"}
fmt.Println(elements)
for index, element := range elements {
if element == "c" {
var temp = elements[:index]
for i := index + 1; i<len(elements); i++ {
temp = append(temp, elements[i])
}
elements = temp
break
}
}
fmt.Println(elements)
The high index in a slice expression is exclusive.
This means your example is flawed, and also that no special treatment is required.
The correct slicing expression is:
elements = append(elements[:index], elements[index+1:]...)
If index is the first element (0), then elements[:0] will be an empty slice.
If index is the last element (len-1), then elements[index+1:] will also be an empty slice, as index+1 will be equal to the lenght of the slice. So the solution is simply:
for index, element := range elements {
if element.Compare() {
elements = append(elements[:index], elements[index+1:]...)
break
}
}
To demonstrate it on the Go Playground, let's substitute the Compare() method with a simple index check:
for _, idxToRemove := range []int{0, 2, 4} {
s := []int{0, 1, 2, 3, 4}
for i := range s {
if i == idxToRemove {
s = append(s[:i], s[i+1:]...)
break
}
}
fmt.Println(idxToRemove, ":", s)
}
Output (try it on the Go Playground):
0 : [1 2 3 4]
2 : [0 1 3 4]
4 : [0 1 2 3]
If the slice s is sorted and len(s) is large, find x using a binary search. For example,
package main
import (
"fmt"
"sort"
)
func pluck(s []string, x string) []string {
i := sort.SearchStrings(s, x)
if i >= 0 && i < len(s) && s[i] == x {
s = append(s[:i], s[i+1:]...)
}
return s
}
func main() {
s := []string{"a", "b", "c", "d"}
fmt.Println(s)
s = pluck(s, "b")
fmt.Println(s)
}
Output:
[a b c d]
[a c d]
If the order of slice s does not need to be preserved, switch elements. For example,
package main
import "fmt"
func pluck(s []string, x string) []string {
for i, v := range s {
if v == x {
s[i] = s[len(s)-1]
s = s[:len(s)-1]
break
}
}
return s
}
func main() {
s := []string{"a", "b", "c", "d"}
fmt.Println(s)
s = pluck(s, "b")
fmt.Println(s)
}
Output:
[a b c d]
[a d c]
Otherwise, splice slice s elements. For example,
package main
import "fmt"
func pluck(s []string, x string) []string {
for i, v := range s {
if v == x {
s = append(s[:i], s[i+1:]...)
break
}
}
return s
}
func main() {
s := []string{"a", "b", "c", "d"}
fmt.Println(s)
s = pluck(s, "b")
fmt.Println(s)
}
Output:
[a b c d]
[a c d]
I'm not sure if this is idiomatic, but this works quite well:
package main
import "fmt"
func splice(start, count int, items []string) (ret []string) {
ret = make([]string, len(items)-count)
copy(ret, items[:start])
copy(ret[start:], items[start+count:])
return
}
func main() {
s := []string{"a", "b", "c", "d"}
fmt.Println(s)
s = splice(1, 2, s)
fmt.Println(s)
}
Go Playground: https://play.golang.org/p/UNtdtw77sEQ
Let's say I have two string array.
A = [ "ab", "cd", "ef", "gh"]
B = [ "ef", "gh"]
I want to do C = A^B
where C = ["ab", "cd"]
I'm aware Golang allows XOR byte-wise, but I haven't seen anything for string arrays in the documentation.
How would I go about doing this? Perhaps there is a utility someone has already made for this?
This doesn't seem like something that would go in a standard library in Go, but here's a bit of code that does the trick:
package main
import (
"fmt"
)
func main() {
A := []string{"ab", "cd", "ef", "gh"}
B := []string{"ef", "gh"}
fmt.Println(xor(A,B))
}
func xor(list1, list2 []string) []string {
set1 := make(map[string]bool)
for _, s := range list1 {
set1[s] = true
}
set2 := make(map[string]bool)
for _, s := range list2 {
set2[s] = true
}
var c []string
for _, s := range list1 {
if !set2[s] {
c = append(c, s)
}
}
for _, s := range list2 {
if !set1[s] {
c = append(c, s)
}
}
return c
}
https://play.golang.org/p/SDPhNIQ66E
I have a slice of slices of strings, and want to sort them by their frequency, I tried to follow the byAge example from the docs here http://golang.org/pkg/sort/ but was unable how to pass a list of frequencies to it.
Meaning, the outcome of the example would be:
[[a,b] [a,b,c,d] [a,c,d,e]]
Would the approach be to have "a" be represented by a custom struct with frequency as it's own attribute? That seems to be more in line with the byAge example.
func main() {
transactions := [][]string{{"a", "b"}, {"b", "c", "d", "a"}, {"c", "d", "e", "a"}}
frequencies := map[string]int{
"a": 3,
"b": 2,
"c": 2,
"d": 2,
"e": 1,
}
fmt.Println(transactions, frequencies)
}
In case you need more than the data you want to sort in the sorting process, a common way is
to implement your own struct, yes. In your case this would be something like this (on play):
type SortableTransaction struct {
data []string
frequencies map[string]int
}
data would be the slice with strings and frequencies your specific frequency table.
The following implementation could be used for the Sort interface:
func (s SortableTransaction) Len() int { return len(s.data) }
func (s SortableTransaction) Less(i, j int) bool {
return s.frequencies[s.data[i]] > s.frequencies[s.data[j]]
}
func (s SortableTransaction) Swap(i, j int) {
s.data[j], s.data[i] = s.data[i], s.data[j]
}
If your frequency table is constant, you can declare it at package level of course.
In case you want to sort the outer slice as well, you'd have to sort the inner slices
first and then the outer slices.
For example,
package main
import (
"fmt"
"sort"
)
type NameFrequency struct {
Name string
Frequency int
}
func (nf NameFrequency) String() string {
return fmt.Sprintf("%s: %d", nf.Name, nf.Frequency)
}
type ByFrequency []NameFrequency
func (nf ByFrequency) Len() int { return len(nf) }
func (nf ByFrequency) Swap(i, j int) { nf[i], nf[j] = nf[j], nf[i] }
func (nf ByFrequency) Less(i, j int) bool {
less := nf[i].Frequency > nf[j].Frequency
if nf[i].Frequency == nf[j].Frequency {
less = nf[i].Name < nf[j].Name
}
return less
}
func SortByFrequency(names []string, frequencies map[string]int) []string {
nf := make(ByFrequency, len(names))
for i, name := range names {
nf[i] = NameFrequency{name, frequencies[name]}
}
sort.Sort(ByFrequency(nf))
sortedNames := make([]string, len(names))
for i, nf := range nf {
sortedNames[i] = nf.Name
}
return sortedNames
}
func main() {
transactions := [][]string{{"a", "b"}, {"b", "c", "d", "a"}, {"c", "d", "e", "a"}}
fmt.Println(transactions)
frequencies := map[string]int{
"a": 3,
"b": 2,
"c": 2,
"d": 2,
"e": 1,
}
fmt.Println(frequencies)
sortedTransactions := make([][]string, len(transactions))
for i, transaction := range transactions {
sortedTransactions[i] = SortByFrequency(transaction, frequencies)
}
fmt.Println(sortedTransactions)
}
Output:
[[a b] [b c d a] [c d e a]]
map[a:3 b:2 c:2 d:2 e:1]
[[a b] [a b c d] [a c d e]]