Related
I want to call a number of function names stored in a slice. The code snippet below works so far but I need to return a value from those functions. Unfortunately I don't get it to work because I don't know to to call those functions and store the return value. Any ideas?
This is the code I'm currently working on:
package main
func A(x int) int {
return x + 1
}
func B(x int) int {
return x + 2
}
func C(x int) int {
return x + 3
}
func main() {
x := 10
type fs func(x int) int
f := []fs{A, B, C}
fns := make([]func(), 3)
for a, _ := range f {
a := a
fns[a] = func() {
f[a](x)
}
}
for _, f := range fns {
f()
}
}
Go Playground
You have call it...
for a, _ := range f {
a := a
fns[a] = func() {
f[a](x) // in this
}
}
here is the playground
let's say that I have generator of fibonachi numbers, and I would like to use enumerate(get_next_fibs(10)) and I would like to have generator of pairs index, number_from_generator, I am struggling to find solution with "named return values"
and it's not how it should be done but it's for purpose of learning specific things about generators
package main
import "fmt"
func get_next_fibs(ii int) func() int {
i := 0
a, b := 0, 1
fc := func() int {
i++
a, b = b, a+b
if ii <= i {
return -1
}
return a
}
return fc
}
func enumerate(iter func() int) func() (index, v int) {
index := 0
fc := func() (index, v int) {
v := iter()
return
index++
}
return fc
}
func main() {
iter := enumerate(get_next_fibs(10))
// iter := get_next_fibs(10)
fmt.Printf("iter = %T\n", iter)
for tuple := iter(); tuple != -1; tuple = iter() {
fmt.Println("tuple:", tuple)
}
}
You have few issues in this code sample:
You can't have index++ after return statement. Use defer if you need to do something after return-ing.
You're missing how variable shadowing works in go. Thus, you're trying to modify a wrong index variable.
Go doesn't have tuples.
...
func enumerate(iter func() int) func() (index, v int) {
counter := 0
return func() (index, v int) {
i := counter
counter++
return i, iter()
}
}
...
func main() {
iter := enumerate(get_next_fibs(10))
fmt.Printf("iter = %T\n", iter)
for i, v := iter(); v != -1; i, v = iter() {
fmt.Printf("i: %d, v: %d\n", i, v)
}
}
Playground link
I'm creating a utility package for my project.
Many of my string slices need a function to
1. remove duplicates
2. remove empty strings
I know 1 way to do this:1. Add a function for each case which accepts a string slice and returns a string slice
func removeEmpty(s []string) []string {
i := 0 // i points to next available pos
for _, v := range s {
if v != "" {
s[i] = v
i++
}
}
return s[:i]
}
func dedup(s []string) []string {
i := 0 // i points to next available pos
mp := map[string]bool{}
for _, v := range s {
if _, ok := mp[v]; !ok {
s[i] = v
mp[v] = true
i++
}
}
return s[:i]
}
when I apply these 2 functions to my slice, I can do:
mySlice := string[]{}
mySlice = dedup(removeEmpty(mySlice))
I want to make it somthing like:
mySlice = mySlice.dedup().removeEmpty()
or
mySlice.dedup().removeEmpty()
Maybe I can add custom method to slice[] ?
I tried writing it
func (s []string) removeEmpty() {
}
I'm getting complie error.
You can't define methods on []string, but you can define your own type based on []string and define methods on that:
type myStringSlice []string
func (s myStringSlice) removeEmpty() myStringSlice {
i := 0 // i points to next available pos
for _, v := range s {
if v != "" {
s[i] = v
i++
}
}
return s[:i]
}
func (s myStringSlice) dedup() myStringSlice {
i := 0 // i points to next available pos
mp := map[string]bool{}
for _, v := range s {
if _, ok := mp[v]; !ok {
s[i] = v
mp[v] = true
i++
}
}
return s[:i]
}
See https://play.golang.org/p/u1z_N3c_wPP.
As mentioned in the documentation:
You can only declare a method with a receiver whose type is defined in the same package as the method. You cannot declare a method with a receiver whose type is defined in another package.
So you have to declare a type on []string and then declare the method on your own type.
Is there any efficient way to get intersection of two slices in Go?
I want to avoid nested for loop like solution
slice1 := []string{"foo", "bar","hello"}
slice2 := []string{"foo", "bar"}
intersection(slice1, slice2)
=> ["foo", "bar"]
order of string does not matter
How do I get the intersection between two arrays as a new array?
Simple Intersection: Compare each element in A to each in B (O(n^2))
Hash Intersection: Put them into a hash table (O(n))
Sorted Intersection: Sort A and do an optimized intersection (O(n*log(n)))
All of which are implemented here
https://github.com/juliangruber/go-intersect
simple, generic and mutiple slices ! (Go 1.18)
Time Complexity : may be linear
func interSection[T constraints.Ordered](pS ...[]T) []T {
hash := make(map[T]*int) // value, counter
result := make([]T, 0)
for _, slice := range pS {
duplicationHash := make(map[T]bool) // duplication checking for individual slice
for _, value := range slice {
if _, isDup := duplicationHash[value]; !isDup { // is not duplicated in slice
if counter := hash[value]; counter != nil { // is found in hash counter map
if *counter++; *counter >= len(pS) { // is found in every slice
result = append(result, value)
}
} else { // not found in hash counter map
i := 1
hash[value] = &i
}
duplicationHash[value] = true
}
}
}
return result
}
func main() {
slice1 := []string{"foo", "bar", "hello"}
slice2 := []string{"foo", "bar"}
fmt.Println(interSection(slice1, slice2))
// [foo bar]
ints1 := []int{1, 2, 3, 9, 8}
ints2 := []int{10, 4, 2, 4, 8, 9} // have duplicated values
ints3 := []int{2, 4, 8, 1}
fmt.Println(interSection(ints1, ints2, ints3))
// [2 8]
}
playground : https://go.dev/play/p/lE79D0kOznZ
It's a best method for intersection two slice. Time complexity is too low.
Time Complexity : O(m+n)
m = length of first slice.
n = length of second slice.
func intersection(s1, s2 []string) (inter []string) {
hash := make(map[string]bool)
for _, e := range s1 {
hash[e] = true
}
for _, e := range s2 {
// If elements present in the hashmap then append intersection list.
if hash[e] {
inter = append(inter, e)
}
}
//Remove dups from slice.
inter = removeDups(inter)
return
}
//Remove dups from slice.
func removeDups(elements []string)(nodups []string) {
encountered := make(map[string]bool)
for _, element := range elements {
if !encountered[element] {
nodups = append(nodups, element)
encountered[element] = true
}
}
return
}
if there exists no blank in your []string, maybe you need this simple code:
func filter(src []string) (res []string) {
for _, s := range src {
newStr := strings.Join(res, " ")
if !strings.Contains(newStr, s) {
res = append(res, s)
}
}
return
}
func intersections(section1, section2 []string) (intersection []string) {
str1 := strings.Join(filter(section1), " ")
for _, s := range filter(section2) {
if strings.Contains(str1, s) {
intersection = append(intersection, s)
}
}
return
}
Try it
https://go.dev/play/p/eGGcyIlZD6y
first := []string{"one", "two", "three", "four"}
second := []string{"two", "four"}
result := intersection(first, second) // or intersection(second, first)
func intersection(first, second []string) []string {
out := []string{}
bucket := map[string]bool{}
for _, i := range first {
for _, j := range second {
if i == j && !bucket[i] {
out = append(out, i)
bucket[i] = true
}
}
}
return out
}
https://github.com/viant/toolbox/blob/a46fd679bbc5d07294b1d1b646aeacd44e2c7d50/collections.go#L869-L920
Another O(m+n) Time Complexity solution that uses a hashmap.
It has two differences compared to the other solutions discussed here.
Passing the target slice as a parameter instead of new slice returned
Faster to use for commonly used types like string/int instead of reflection for all
Yes there are a few different ways to go about it.. Here's an example that can be optimized.
package main
import "fmt"
func intersection(a []string, b []string) (inter []string) {
// interacting on the smallest list first can potentailly be faster...but not by much, worse case is the same
low, high := a, b
if len(a) > len(b) {
low = b
high = a
}
done := false
for i, l := range low {
for j, h := range high {
// get future index values
f1 := i + 1
f2 := j + 1
if l == h {
inter = append(inter, h)
if f1 < len(low) && f2 < len(high) {
// if the future values aren't the same then that's the end of the intersection
if low[f1] != high[f2] {
done = true
}
}
// we don't want to interate on the entire list everytime, so remove the parts we already looped on will make it faster each pass
high = high[:j+copy(high[j:], high[j+1:])]
break
}
}
// nothing in the future so we are done
if done {
break
}
}
return
}
func main() {
slice1 := []string{"foo", "bar", "hello", "bar"}
slice2 := []string{"foo", "bar"}
fmt.Printf("%+v\n", intersection(slice1, slice2))
}
Now the intersection method defined above will only operate on slices of strings, like your example.. You can in theory create a definition that looks like this func intersection(a []interface, b []interface) (inter []interface), however you would be relying on reflection and type casting so that you can compare, which will add latency and make your code harder to read. It's probably easier to maintain and read to write a separate function for each type you care about.
func intersectionString(a []string, b []string) (inter []string),
func intersectionInt(a []int, b []int) (inter []int),
func intersectionFloat64(a []Float64, b []Float64) (inter []Float64), ..ect
You can then create your own package and reuse once you settle how you want to implement it.
package intersection
func String(a []string, b []string) (inter []string)
func Int(a []int, b []int) (inter []int)
func Float64(a []Float64, b []Float64) (inter []Float64)
Here is my desired outcome
slice1 := []string{"foo", "bar","hello"}
slice2 := []string{"foo", "bar"}
difference(slice1, slice2)
=> ["hello"]
I am looking for the difference between the two string slices!
Assuming Go maps are ~O(1), here is an ~O(n) difference function that works on unsorted slices.
// difference returns the elements in `a` that aren't in `b`.
func difference(a, b []string) []string {
mb := make(map[string]struct{}, len(b))
for _, x := range b {
mb[x] = struct{}{}
}
var diff []string
for _, x := range a {
if _, found := mb[x]; !found {
diff = append(diff, x)
}
}
return diff
}
Depending on the size of the slices, different solutions might be best.
My answer assumes order doesn't matter.
Using simple loops, only to be used with smaller slices:
package main
import "fmt"
func difference(slice1 []string, slice2 []string) []string {
var diff []string
// Loop two times, first to find slice1 strings not in slice2,
// second loop to find slice2 strings not in slice1
for i := 0; i < 2; i++ {
for _, s1 := range slice1 {
found := false
for _, s2 := range slice2 {
if s1 == s2 {
found = true
break
}
}
// String not found. We add it to return slice
if !found {
diff = append(diff, s1)
}
}
// Swap the slices, only if it was the first loop
if i == 0 {
slice1, slice2 = slice2, slice1
}
}
return diff
}
func main() {
slice1 := []string{"foo", "bar", "hello"}
slice2 := []string{"foo", "world", "bar", "foo"}
fmt.Printf("%+v\n", difference(slice1, slice2))
}
Output:
[hello world]
Playground: http://play.golang.org/p/KHTmJcR4rg
I use the map to solve this problem
package main
import "fmt"
func main() {
slice1 := []string{"foo", "bar","hello"}
slice2 := []string{"foo", "bar","world"}
diffStr := difference(slice1, slice2)
for _, diffVal := range diffStr {
fmt.Println(diffVal)
}
}
func difference(slice1 []string, slice2 []string) ([]string){
diffStr := []string{}
m :=map [string]int{}
for _, s1Val := range slice1 {
m[s1Val] = 1
}
for _, s2Val := range slice2 {
m[s2Val] = m[s2Val] + 1
}
for mKey, mVal := range m {
if mVal==1 {
diffStr = append(diffStr, mKey)
}
}
return diffStr
}
output:
hello
world
func diff(a, b []string) []string {
temp := map[string]int{}
for _, s := range a {
temp[s]++
}
for _, s := range b {
temp[s]--
}
var result []string
for s, v := range temp {
if v != 0 {
result = append(result, s)
}
}
return result
}
If you want to handle duplicated strings, the v in the map can do that. And you can pick a.Remove(b) ( v>0 ) or b.Remove(a) (v<0)
func unique(slice []string) []string {
encountered := map[string]int{}
diff := []string{}
for _, v := range slice {
encountered[v] = encountered[v]+1
}
for _, v := range slice {
if encountered[v] == 1 {
diff = append(diff, v)
}
}
return diff
}
func main() {
slice1 := []string{"hello", "michael", "dorner"}
slice2 := []string{"hello", "michael"}
slice3 := []string{}
fmt.Println(unique(append(slice1, slice2...))) // [dorner]
fmt.Println(unique(append(slice2, slice3...))) // [michael michael]
}
As mentioned by ANisus, different approaches will suit different sizes of input slices. This solution will work in linear time O(n) independent of input size, but assumes that the "equality" includes index position.
Thus, in the OP's examples of:
slice1 := []string{"foo", "bar","hello"}
slice2 := []string{"foo", "bar"}
The entries foo and bar are equal not just due to value, but also due to their index in the slice.
Given these conditions, you can do something like:
package main
import "fmt"
func difference(s1, s2 []string) string {
var (
lenMin int
longest []string
out string
)
// Determine the shortest length and the longest slice
if len(s1) < len(s2) {
lenMin = len(s1)
longest = s2
} else {
lenMin = len(s2)
longest = s1
}
// compare common indeces
for i := 0; i < lenMin; i++ {
if s1[i] != s2[i] {
out += fmt.Sprintf("=>\t%s\t%s\n", s1[i], s2[i])
}
}
// add indeces not in common
for _, v := range longest[lenMin:] {
out += fmt.Sprintf("=>\t%s\n", v)
}
return out
}
func main() {
slice1 := []string{"foo", "bar", "hello"}
slice2 := []string{"foo", "bar"}
fmt.Print(difference(slice1, slice2))
}
Produces:
=> hello
Playground
If you change the slices to be:
func main() {
slice1 := []string{"foo", "baz", "hello"}
slice2 := []string{"foo", "bar"}
fmt.Print(difference(slice1, slice2))
}
It will produce:
=> baz bar
=> hello
Most of the other solutions here will fail to return the correct answer in case the slices contain duplicated elements.
This solution is O(n) time and O(n) space if the slices are already sorted, and O(n*log(n)) time O(n) space if they are not, but has the nice property of actually being correct. 🤣
func diff(a, b []string) []string {
a = sortIfNeeded(a)
b = sortIfNeeded(b)
var d []string
i, j := 0, 0
for i < len(a) && j < len(b) {
c := strings.Compare(a[i], b[j])
if c == 0 {
i++
j++
} else if c < 0 {
d = append(d, a[i])
i++
} else {
d = append(d, b[j])
j++
}
}
d = append(d, a[i:len(a)]...)
d = append(d, b[j:len(b)]...)
return d
}
func sortIfNeeded(a []string) []string {
if sort.StringsAreSorted(a) {
return a
}
s := append(a[:0:0], a...)
sort.Strings(s)
return s
}
If you know for sure that the slices are already sorted, you can remove the calls to sortIfNeeded (the reason for the defensive slice copy in sortIfNeeded is because sorting is done in-place, so we would be modifying the slices that are passed to diff).
See https://play.golang.org/p/lH-5L0aL1qr for tests showing correctness in face of duplicated entries.
I have this example but it works only for the elements of the first array "not present" in the second array
with generics
type HandleDiff[T comparable] func(item1 T, item2 T) bool
func HandleDiffDefault[T comparable](val1 T, val2 T) bool {
return val1 == val2
}
func Diff[T comparable](items1 []T, items2 []T, callback HandleDiff[T]) []T {
acc := []T{}
for _, item1 := range items1 {
find := false
for _, item2 := range items2 {
if callback(item1, item2) {
find = true
break
}
}
if !find {
acc = append(acc, item1)
}
}
return acc
}
usage
diff := Diff(items1, items2, HandleDiffDefault[string])
Why not keep it simple and use labels?
// returns items unique to slice1
func difference(slice1, slice2 []string) []string {
var diff []string
outer:
for _, v1 := range slice1 {
for _, v2 := range slice2 {
if v1 == v2 {
continue outer
}
}
diff = append(diff, v1)
}
return diff
}
https://go.dev/play/p/H46zSpfocHp
I would add a small change to the solution by #peterwilliams97, so that we can ignore the order of the input.
func difference(a, b []string) []string {
// reorder the input,
// so that we can check the longer slice over the shorter one
longer, shorter := a, b
if len(b) > len(a) {
longer, shorter = b, a
}
mb := make(map[string]struct{}, len(shorter))
for _, x := range shorter {
mb[x] = struct{}{}
}
var diff []string
for _, x := range longer {
if _, found := mb[x]; !found {
diff = append(diff, x)
}
}
return diff
}
The code below gives the absolute difference between strings regardless of the order. Space complexity O(n) and Time complexity O(n).
// difference returns the elements in a that aren't in b
func difference(a, b string) string {
longest, shortest := longestString(&a, &b)
var builder strings.Builder
var mem = make(map[rune]bool)
for _, s := range longest {
mem[s] = true
}
for _, s := range shortest {
if _, ok := mem[s]; ok {
mem[s] = false
}
}
for k, v := range mem {
if v == true {
builder.WriteRune(k)
}
}
return builder.String()
}
func longestString(a *string, b *string) ([]rune, []rune) {
if len(*a) > len(*b) {
return []rune(*a), []rune(*b)
}
return []rune(*b), []rune(*a)
}