Removing duplicate values of gota series - go

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]

Related

How to flatten a 2D slice into 1D slice?

I have
arr := [][]int32 {{1,2,3} ,{4,5,6}, {7,8,9}}
and I want
newArr := []int32 {1,2,3,4,5,6,7,8,9}
In JS I can do
arr1d = [].concat(...arr2d);
as one of many simple ways like this
Is there in Go something like this?
Go has strings.Join and bytes.Join, but no generic functionality to join/concat a slice. It's possible that once generics are introduced into the language such functionality will be added to the standard library.
In the meantime, doing this with a loop is clear and concise enough.
var newArr []int32
for _, a := range arr {
newArr = append(newArr, a...)
}
Go 1.18
You can't avoid a for loop, but with generics this is easily extensible to slices of any type:
func Flatten[T any](lists [][]T) []T {
var res []T
for _, list := range lists {
res = append(res, list...)
}
return res
}
Example usage:
func main() {
w := [][]string{{"a", "b", "c"}, {"d", "e", "f"}}
v := Flatten(w)
fmt.Println(v) // [a b c d e f]
d := [][]uint64{{100, 200}, {3000, 4000}}
e := Flatten(d)
fmt.Println(e) // [100 200 3000 4000]
}
Playground: https://go.dev/play/p/X81g7GYFd4n

Code to generate powerset in Golang gives wrong result

Next code in Golang to generate powerset produces wrong result on input {"A", "B", "C", "D", "E"}. I see [A B C E E] as the last generated set.
package main
import (
"fmt"
)
func main() {
for _, s := range PowerSet([]string{"A", "B", "C", "D", "E"}) {
fmt.Println(s)
}
}
func PowerSet(set []string) [][]string {
var powerSet [][]string
powerSet = append(powerSet, make([]string, 0))
for _, element := range set {
var moreSets [][]string
for _, existingSet := range powerSet {
newSet := append(existingSet, element)
moreSets = append(moreSets, newSet)
}
powerSet = append(powerSet, moreSets...)
}
return powerSet
}
How to fix it? How to write it idiomatically in Go?
The problem with your program is not the algorithm itself but this line:
newSet := append(existingSet, element)
You should not append and assign to a different variable.
As the documentation states (emphasis mine), "The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated.".
So, there might be cases where newSet := append(existingSet, element) will actually modify existingSet itself, which would break your logic.
If you change that to instead create a new array and append to that one, it works as you expect it.
newSet := make([]string, 0)
newSet = append(newSet, existingSet...)
newSet = append(newSet, element)
For instance, you can use algorithm like this one: https://stackoverflow.com/a/2779467/3805062.
func PowerSet(original []string) [][]string {
powerSetSize := int(math.Pow(2, float64(len(original))))
result := make([][]string, 0, powerSetSize)
var index int
for index < powerSetSize {
var subSet []string
for j, elem := range original {
if index& (1 << uint(j)) > 0 {
subSet = append(subSet, elem)
}
}
result = append(result, subSet)
index++
}
return result
}
Elaborating on #eugenioy's answer.
Look at this thread. Here is a working example : https://play.golang.org/p/dzoTk1kimf
func copy_and_append_string(slice []string, elem string) []string {
// wrong: return append(slice, elem)
return append(append([]string(nil), slice...), elem)
}
func PowerSet(s []string) [][]string {
if s == nil {
return nil
}
r := [][]string{[]string{}}
for _, es := range s {
var u [][]string
for _, er := range r {
u = append(u, copy_and_append_string(er, es))
}
r = append(r, u...)
}
return r
}

Idiomatic Splice in Go

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

How to XOR two string arrays in Golang?

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

How to convert slice of structs to slice of strings in go?

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?

Resources