Multiple variable assignment - go

The below code snippet confused me:
v, ok := a[1] // error: assignment count mismatch: 2 = 1
The above code throws an error because of count mismatch, but the below code snippet seems to work fine:
for i, id:= range ids
{
fmt.Printf("%d - ID: %d\n",i,id)
}
Does this mean that the range returns both the index and the value due to which the code snippet above works fine?

The range form of the for loop iterates over a slice or map.
When ranging over a slice, two values are returned for each iteration:
The first is the index of the slice;
The second is a copy of the element at that index.
Let's assume that we have a function that verify if a number is a prime number, and we have an array with the candidate numbers, than we can call the function in two different way:
var prime []int = []int{2, 3, 5, 7, 11, 13, 17, 19, 23}
func TestIsPrimeByValue(t *testing.T) {
for _, item := range prime {
if !IsPrime(item) {
t.Fail()
}
}
}
func TestIsPrimeByIndex(t *testing.T) {
for i := range prime {
if !IsPrime(prime[i]) {
t.Fail()
}
}
}
With the first loop, we are iterating the value of the array.
With the second loop, we are iterating the index of the array.

Related

How to implement remove range on string array golang?

How can I implement RemoveRange method in golang? It is a method in C# as shown here
I want to implement RemoveRange method on my hashCode string array and return new modified array back if possible with those ranges remove.
func removeRange(hashCode []string, idx int, count int) []string {
var temp []string
for i, s := range hashCode {
fmt.Println(i, s)
// confuse here on what to do
}
return temp
}
Simply slice the slice up until idx, skip count elements and append the rest to the result of the first slicing:
func removeRange(hashCode []string, idx int, count int) []string {
return append(hashCode[:idx], hashCode[idx+count:]...)
}
Testing it:
s := []string{"0", "1", "2", "3", "4", "5"}
fmt.Println(s)
s = removeRange(s, 1, 2)
fmt.Println(s)
Which outputs (try it on the Go Playground):
[0 1 2 3 4 5]
[0 3 4 5]
Note: the above implementation does not check whether indices are valid (whether they are in range). If not, the code could panic. Add necessary checks if you need to.
Note #2: the above implementation modifies the elements of the passed slice, the returned slice will share the backing array of the parameter. If you want to avoid this, if you want to leave the input intact and allocate a new slice for the result, then do so:
func removeRange(hashCode []string, idx int, count int) []string {
result := make([]string, 0, len(hashCode)-count)
result = append(result, hashCode[:idx]...)
result = append(result, hashCode[idx+count:]...)
return result
}
Try this one on the Go Playground.
You don't need a method or function for this at all in golang. Go slices can be subsliced and appended in place, which is how you can quickly and easily remove subsets from any slice.
Say you want to remove 2 elements, starting at index 2, you'd simply write:
Sub := append(original [:2], original [4:]...)
Demo
How this works:
original[:2] creates a sub-slice starting at 0, with a length of 2 elements (so index 0 and 1)
append because to this first part, we want to add the rest of the slice, minus the range we want to skip/remove
original[4:] creates another sub-slice, this time starting at index 4, and ending wherever original ends. Just like we don't explicitly mention 0 as the starting point in the first sub-slice, by not specifying a number of elements here, golang will just include all of the remaining elements in the slice.
... because append is a variadic function (built-in, but you get the point), we need to pass in every element we want to append as a new argument. The ... operator expands the sub-slice and passes in every element as a separate argument.
Because we assigned the new slice to a new variable, original will remain unchanged, so if you want to overwrite the slice, you just assign it to the same variable.
Note I wrote this on my phone, so markup and code may not be quite right, but this should answer your question at least
I've explained the code using // comments and if not commented, code is self explanatory.
package main
import (
"fmt"
"os"
)
func RemoveRange(s []string, index, count int) []string {
sLen := len(s)
// Similar semantics to match (similar) the behavior of
// C# implementation
switch {
case index < 0, count < 0: // arguments are not valid
fmt.Fprintln(os.Stderr, "error: argument out of range error")
return s
case index+count-1 >= sLen: // range results in exceeding the limit
fmt.Fprintln(os.Stderr, "error: argument error")
return s
}
// Create a slice p and pre-allocate the size required
// to store the resultant slice after removing range.
// Result := s[:] -> s[:index] + s[index+count:]
// Remove := s[index:index+count-1]
p := make([]string, 0, sLen-count)
p = append(p, s[:index]...)
p = append(p, s[index+count:]...)
return p
}
func main() {
s := []string{"0", "1", "2", "3", "4", "5"}
fmt.Println(s)
r := RemoveRange(s, 1, 3)
fmt.Println(r)
}
Output:
[0 1 2 3 4 5]
[0 4 5]

How to get single items count in a slice with go?

For example, this is a slice:
[1, 2, 3, 3, 4]
want to get single data 1, 2, 4 's count and return count = 3.
Maybe remove duplicate items(include itself) is an idea, but didn't find suitalbe method.
What I tried:
func removeDuplicateItems() {
intSlice := []int{1, 2, 3, 3, 4}
fmt.Println(intSlice)
keys := make(map[int]bool)
list := []int{}
for _, entry := range intSlice {
if _, value := keys[entry]; !value {
keys[entry] = true
list = append(list, entry)
}
}
fmt.Println(list)
}
Got
[1 2 3 3 4]
[1 2 3 4]
I just changed your function a little bit:
func removeDuplicateItems() {
intSlice := []int{1, 2, 3, 3, 4}
fmt.Println(intSlice)
keys := make(map[int]int)
list := []int{}
for _, entry := range intSlice {
keys[entry]++
}
for k, v := range keys {
if v == 1 {
list = append(list, k)
}
}
fmt.Println(list)
}
https://play.golang.org/p/ESFLhC4VC-l
At this point the list is not sorted. If you want to sort your list afterward you need to use the sortpackage.
I suppose a really easy & quick way to get the count of unique values would be to use a map:
data := map[int]bool{}
cnt := 0 // count of unique values
for _, i := range intSlice {
if dup, ok := data[i]; !ok {
// we haven't seen value i before, assume it's unique
data[i] = false // add to map, mark as non-duplicate
cnt++ // increment unique count
} else if !dup {
// we have seen value i before, but this is only the second time
cnt-- // unique count goes down here
data[i] = true // mark this value as known duplicate
}
}
At the end of this loop, you'll have cnt with the count of unique values, and you can iterate the map data for all keys with value false to get the unique values that you found in the slice. All keys combined basically are the values in the slice, de-duplicated.

How to get rid of zero value in a int slice in Go?

I am trying to find even numbers in a list of numbers, here is my attempt:
package main
import "fmt"
func main() {
nums := []int{1, 2, 3, 4, 5, 6, 7}
res := []int{}
for n := range nums {
if n%2 == 0 {
res = append(res, n)
}
}
fmt.Println(res)
}
It seems straightforward; however, when I run the program, I got the result
[0 2 4 6]
Where does the zero come from? It must be from the empty slice res. How can I get rid of this zero?
for n := range nums {
// ...
}
n is not the elements of the nums slice, it is the index. So basically you tested and added the indices of the elements to your res result slice.
Instead do this:
for _, n := range nums {
// ...
}
With this change, output will be (try it on the Go Playground):
[2 4 6]
This is detailed in Spec: For statements, For statements with range clause:
For each iteration, iteration values are produced as follows if the respective iteration variables are present:
Range expression 1st value 2nd value
array or slice a [n]E, *[n]E, or []E index i int a[i] E
string s string type index i int see below rune
map m map[K]V key k K m[k] V
channel c chan E, <-chan E element e E

I need to assign a variable to itself when iterating for the closure context to keep the correct value

Without i := i, I get an incorrect result (3, 3, 5, 9, 7, 15). With it, I get (0, 0, 3, 3, 6, 10), which is correct. Removing the assignment is similar to getting the value of i at the end of the loop. Why?
package main
import "fmt"
type Handler interface {
Handle(v int)
}
type Elem struct {
Handler Handler
}
var elems []*Elem
type handlerFunc func(v int)
func (h handlerFunc) Handle(v int) { h(v) }
func main() {
newElem := func(fn handlerFunc) {
elem := &Elem{Handler: handlerFunc(fn)}
elems = append(elems, elem)
}
for i := 0; i < 3; i++ {
i := i // *** Why? ***
newElem(func(v int) { fmt.Printf("%d, ", i+v) })
newElem(func(v int) { fmt.Printf("%d, ", i*v) })
}
for n, e := range elems {
if e.Handler != nil {
e.Handler.Handle(n)
}
}
fmt.Printf("\n")
}
The easiest way to visualize what is going on is to change the output of your functions to:
newElem(func(v int) { fmt.Printf("plus %d - %d+%d\n", i+v, i, v) })
newElem(func(v int) { fmt.Printf("times %d - %d*%d\n", i*v, i , v) })
With this change the output becomes:
plus 3 - 3+0
times 3 - 3*1
plus 5 - 3+2
times 9 - 3*3
plus 7 - 3+4
times 15 - 3*5
So, as you can see, i is 3 in all cases. This is because you are creating closures around the i variable, so the functions will use the current value of i when they run, which is 3 by the time the functions actually use i.
You can again see this if you change your code like the following:
http://play.golang.org/p/FRhr0n2oi7
The reason the assignment i := i inside your loop fixes the problem is because you are creating a new variable i inside the scope of your loop which is still closed over by the functions, but doesn't ever change. Each new iteration of the loop creates a new i so no previous i changes value.
Although this document describes a common mistake when dealing with goroutines and closures, it should shed a bit more light on the issue and potential solutions.
https://github.com/golang/go/wiki/CommonMistakes

How to remove items from a slice while ranging over it?

What is the best way to remove items from a slice while ranging over it?
For example:
type MultiDataPoint []*DataPoint
func (m MultiDataPoint) Json() ([]byte, error) {
for i, d := range m {
err := d.clean()
if ( err != nil ) {
//Remove the DP from m
}
}
return json.Marshal(m)
}
As you have mentioned elsewhere, you can allocate new memory block and copy only valid elements to it. However, if you want to avoid the allocation, you can rewrite your slice in-place:
i := 0 // output index
for _, x := range s {
if isValid(x) {
// copy and increment index
s[i] = x
i++
}
}
// Prevent memory leak by erasing truncated values
// (not needed if values don't contain pointers, directly or indirectly)
for j := i; j < len(s); j++ {
s[j] = nil
}
s = s[:i]
Full example: http://play.golang.org/p/FNDFswPeDJ
Note this will leave old values after index i in the underlying array, so this will leak memory until the slice itself is garbage collected, if values are or contain pointers. You can solve this by setting all values to nil or the zero value from i until the end of the slice before truncating it.
I know its answered long time ago but i use something like this in other languages, but i don't know if it is the golang way.
Just iterate from back to front so you don't have to worry about indexes that are deleted. I am using the same example as Adam.
m = []int{3, 7, 2, 9, 4, 5}
for i := len(m)-1; i >= 0; i-- {
if m[i] < 5 {
m = append(m[:i], m[i+1:]...)
}
}
There might be better ways, but here's an example that deletes the even values from a slice:
m := []int{1,2,3,4,5,6}
deleted := 0
for i := range m {
j := i - deleted
if (m[j] & 1) == 0 {
m = m[:j+copy(m[j:], m[j+1:])]
deleted++
}
}
Note that I don't get the element using the i, d := range m syntax, since d would end up getting set to the wrong elements once you start deleting from the slice.
Here is a more idiomatic Go way to remove elements from slices.
temp := s[:0]
for _, x := range s {
if isValid(x) {
temp = append(temp, x)
}
}
s = temp
Playground link: https://play.golang.org/p/OH5Ymsat7s9
Note: The example and playground links are based upon #tomasz's answer https://stackoverflow.com/a/20551116/12003457
One other option is to use a normal for loop using the length of the slice and subtract 1 from the index each time a value is removed. See the following example:
m := []int{3, 7, 2, 9, 4, 5}
for i := 0; i < len(m); i++ {
if m[i] < 5 {
m = append(m[:i], m[i+1:]...)
i-- // -1 as the slice just got shorter
}
}
I don't know if len() uses enough resources to make any difference but you could also run it just once and subtract from the length value too:
m := []int{3, 7, 2, 9, 4, 5}
for i, s := 0, len(m); i < s; i++ {
if m[i] < 5 {
m = append(m[:i], m[i+1:]...)
s--
i--
}
}
Something like:
m = append(m[:i], m[i+1:]...)
You don't even need to count backwards but you do need to check that you're at the end of the array where the suggested append() will fail. Here's an example of removing duplicate positive integers from a sorted list:
// Remove repeating numbers
numbers := []int{1, 2, 3, 3, 4, 5, 5}
log.Println(numbers)
for i, numbersCount, prevNum := 0, len(numbers), -1; i < numbersCount; numbersCount = len(numbers) {
if numbers[i] == prevNum {
if i == numbersCount-1 {
numbers = numbers[:i]
} else {
numbers = append(numbers[:i], numbers[i+1:]...)
}
continue
}
prevNum = numbers[i]
i++
}
log.Println(numbers)
Playground: https://play.golang.org/p/v93MgtCQsaN
I just implement a method which removes all nil elements in slice.
And I used it to solve a leetcode problems, it works perfectly.
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeNil(lists *[]*ListNode) {
for i := 0; i < len(*lists); i++ {
if (*lists)[i] == nil {
*lists = append((*lists)[:i], (*lists)[i+1:]...)
i--
}
}
}
You can avoid memory leaks, as suggested in #tomasz's answer, controlling the capacity of the underlying array with a full slice expression. Look at the following function that remove duplicates from a slice of integers:
package main
import "fmt"
func removeDuplicates(a []int) []int {
for i, j := 0, 1; i < len(a) && j < len(a); i, j = i+1, j+1 {
if a[i] == a[j] {
copy(a[j:], a[j+1:])
// resize the capacity of the underlying array using the "full slice expression"
// a[low : high : max]
a = a[: len(a)-1 : len(a)-1]
i--
j--
}
}
return a
}
func main() {
a := []int{2, 3, 3, 3, 6, 9, 9}
fmt.Println(a)
a = removeDuplicates(a)
fmt.Println(a)
}
// [2 3 3 3 6 9 9]
// [2 3 6 9]
For reasons #tomasz has explained, there are issues with removing in place. That's why it is practice in golang not to do that, but to reconstruct the slice. So several answers go beyond the answer of #tomasz.
If elements should be unique, it's practice to use the keys of a map for this. I like to contribute an example of deletion by use of a map.
What's nice, the boolean values are available for a second purpose. In this example I calculate Set a minus Set b. As Golang doesn't have a real set, I make sure the output is unique. I use the boolean values as well for the algorithm.
The map gets close to O(n). I don't know the implementation. append() should be O(n). So the runtime is similar fast as deletion in place. Real deletion in place would cause a shifting of the upper end to clean up. If not done in batch, the runtime should be worse.
In this special case, I also use the map as a register, to avoid a nested loop over Set a and Set b to keep the runtime close to O(n).
type Set []int
func differenceOfSets(a, b Set) (difference Set) {
m := map[int]bool{}
for _, element := range a {
m[element] = true
}
for _, element := range b {
if _, registered := m[element]; registered {
m[element] = false
}
}
for element, present := range m {
if present {
difference = append(difference, element)
}
}
return difference
}
Try Sort and Binary search.
Example:
package main
import (
"fmt"
"sort"
)
func main() {
// Our slice.
s := []int{3, 7, 2, 9, 4, 5}
// 1. Iterate over it.
for i, v := range s {
func(i, v int) {}(i, v)
}
// 2. Sort it. (by whatever condition of yours)
sort.Slice(s, func(i, j int) bool {
return s[i] < s[j]
})
// 3. Cut it only once.
i := sort.Search(len(s), func(i int) bool { return s[i] >= 5 })
s = s[i:]
// That's it!
fmt.Println(s) // [5 7 9]
}
https://play.golang.org/p/LnF6o0yMJGT

Resources