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
Related
I'm trying to traverse a map in decreasing order of the values stored against keys. I've tried:
func frequencySort(s string) string {
var frequency map[string]int
chararray := strings.Split(s , "")
var a []int
var arranged map[int]string
for k , v := range frequency {
arranged[v] = k
}
for k := range arranged {
a = append(a , k)
}
sort.Sort(sort.Reverse(sort.IntSlice{a}))
}
Let's say the Map structure is :
"a" : 9
"b" : 7
"c" : 19
"d" : 11
and I'm trying to traverse it such that the output is :
"c" : 19
"d" : 11
"a" : 9
"b" : 7
The two map approach you have in your example will break as soon as you have more than one key in frequency with the same value, say "a":7 and "b":7, then you would lose data in arranged since keys have to be unique.
To avoid this you could create a helper type that will hold the map's contents temporarily, just for sorting purposes. Something like this:
package main
import (
"fmt"
"sort"
)
var m = map[string]int{
"a": 9,
"b": 7,
"c": 19,
"d": 11,
}
type entry struct {
val int
key string
}
type entries []entry
func (s entries) Len() int { return len(s) }
func (s entries) Less(i, j int) bool { return s[i].val < s[j].val }
func (s entries) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func main() {
var es entries
for k, v := range m {
es = append(es, entry{val: v, key: k})
}
sort.Sort(sort.Reverse(es))
for _, e := range es {
fmt.Printf("%q : %d\n", e.key, e.val)
}
}
https://play.golang.org/p/TPb0zNCtXO
For example,
package main
import (
"fmt"
"sort"
)
type frequncy struct {
c string
f int
}
func frequencies(s string) []frequncy {
m := make(map[string]int)
for _, r := range s {
m[string(r)]++
}
a := make([]frequncy, 0, len(m))
for c, f := range m {
a = append(a, frequncy{c: c, f: f})
}
sort.Slice(a, func(i, j int) bool { return a[i].f > a[j].f })
return a
}
func main() {
s := "aaaaabcbcbcbzxyyxzzsoaz"
fmt.Println(s)
f := frequencies(s)
fmt.Println(f)
}
Playground: https://play.golang.org/p/d9i3yL1x4K
Output:
aaaaabcbcbcbzxyyxzzsoaz
[{a 6} {b 4} {z 4} {c 3} {x 2} {y 2} {s 1} {o 1}]
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
}
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)
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
It would be convenient to be able to say something like:
for _, element := reverse range mySlice {
...
}
Edit: I asked this question a long time ago, it is 2022 now and the generic solution by #Ivan below seems like the way to go!
No there is no convenient operator for this to add to the range one in place. You'll have to do a normal for loop counting down:
s := []int{5, 4, 3, 2, 1}
for i := len(s)-1; i >= 0; i-- {
fmt.Println(s[i])
}
You can also do:
s := []int{5, 4, 3, 2, 1}
for i := range s {
fmt.Println(s[len(s)-1-i]) // Suggestion: do `last := len(s)-1` before the loop
}
Output:
1
2
3
4
5
Also here: http://play.golang.org/p/l7Z69TV7Vl
Variation with index
for k := range s {
k = len(s) - 1 - k
// now k starts from the end
}
How about use defer:
s := []int{5, 4, 3, 2, 1}
for i, _ := range s {
defer fmt.Println(s[i])
}
One could use a channel to reverse a list in a function without duplicating it. It makes the code nicer in my sense.
package main
import (
"fmt"
)
func reverse(lst []string) chan string {
ret := make(chan string)
go func() {
for i, _ := range lst {
ret <- lst[len(lst)-1-i]
}
close(ret)
}()
return ret
}
func main() {
elms := []string{"a", "b", "c", "d"}
for e := range reverse(elms) {
fmt.Println(e)
}
}
In 2022, you could use generics to reverse any slice in-place:
func reverse[S ~[]E, E any](s S) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
When I need to extract elements from a slice and reverse range, I use something like this code:
// reverse range
// Go Playground: https://play.golang.org/p/gx6fJIfb7fo
package main
import (
"fmt"
)
type Elem struct {
Id int64
Name string
}
type Elems []Elem
func main() {
mySlice := Elems{{Id: 0, Name: "Alice"}, {Id: 1, Name: "Bob"}, {Id: 2, Name: "Carol"}}
for i, element := range mySlice {
fmt.Printf("Normal range: [%v] %+v\n", i, element)
}
//mySlice = Elems{}
//mySlice = Elems{{Id: 0, Name: "Alice"}}
if last := len(mySlice) - 1; last >= 0 {
for i, element := last, mySlice[0]; i >= 0; i-- {
element = mySlice[i]
fmt.Printf("Reverse range: [%v] %+v\n", i, element)
}
} else {
fmt.Println("mySlice empty")
}
}
Output:
Normal range: [0] {Id:0 Name:Alice}
Normal range: [1] {Id:1 Name:Bob}
Normal range: [2] {Id:2 Name:Carol}
Reverse range: [2] {Id:2 Name:Carol}
Reverse range: [1] {Id:1 Name:Bob}
Reverse range: [0] {Id:0 Name:Alice}
Playground: https://play.golang.org/p/gx6fJIfb7fo
You can use the funk.ForEachRight method from go-funk:
results := []int{}
funk.ForEachRight([]int{1, 2, 3, 4}, func(x int) {
results = append(results, x)
})
fmt.Println(results) // []int{4, 3, 2, 1}