go - golang test parameterized - go

In python I can easily do
#pytest.mark.parametrize('input, expected', [(1, 2), [2, 3]])
def test_tutu(input, expected):
assert input + 1 == expected
How can I do the same in golang ? without writting myself a loop
func tutu(a int) int {
return a + 1
}
func Test_tutu(t *testing.T) {
tests := []struct {
input int
expected int
}{
{input: 1, expected: 2},
{input: 2, expected: 3},
}
for _, tt := range tests {
t.Run("", func(t *testing.T) {
assert.Equal(t, tutu(tt.input), tt.expected)
})
}
}
So what would be the equivalent of this python parametrize in golang ?
def parametrize(all_args_name: str, all_values: List[Any], fn: Callable):
args_name = all_args_name.split(',')
for values in all_values:
args = {k: v for k, v in zip(args_name, values)}
fn(**args)

The closest thing GO has is subtests, but you would still need to write the for loop, like you already did in the second example.

I found a way using reflect
func parametrize[V any, T any](fn T, allValues [][]V) {
v := reflect.ValueOf(fn)
for _, a := range allValues {
vargs := make([]reflect.Value, len(a))
for i, b := range a {
vargs[i] = reflect.ValueOf(b)
}
v.Call(vargs)
}
}
func tutu(a int) int {
return a + 1
}
func Test_tutu(t *testing.T) {
testsArgs := [][]any{
{t, 1, 2}, {t, 3, 4},
}
test := func(t *testing.T, input int, expected int) {
assert.Equal(t, tutu(input), expected)
}
parametrize(test, testsArgs)
}

Related

When is the slice element covered in this code?

I logged every element before I appending it. But the result looks like that some element is covered.
I do not know when it is covered.
package main
import "fmt"
func main() {
graph := [][]int{
[]int{3, 1},
[]int{4, 6, 7, 2, 5},
[]int{4, 6, 3},
[]int{6, 4},
[]int{7, 6, 5},
[]int{6},
[]int{7},
[]int{},
}
fmt.Println(allPathsSourceTarget(graph))
}
func allPathsSourceTarget(graph [][]int) [][]int {
n := len(graph) - 1
result := make([][]int, 0, 200)
var pathRecord func(target, path []int)
pathRecord = func(target, path []int) {
if (len(target) == 0) && (path[len(path)-1] == n) {
fmt.Println("insert into", path) // should end with 7
result = append(result, path)
}
for _, v := range target {
pathRecord(graph[v], append(path, v))
}
}
for _, v := range graph[0] {
pathRecord(graph[v], []int{0, v})
}
return result
}
Every element in the result should end with 7.
You issue is with this line:
pathRecord(graph[v], append(path, v))
Go is so "smart" so he's trying to reuse the same slice allocated memory and you actually change the path you already added to result. ):
try this instead:
newPath = make([]int, len(path))
copy(newPath, path)
pathRecord(graph[v], append(newPath, v))
This works for me. I assume it's weirdness with append and slices
I assume that the memory backing "this append" is passed into the recursive function as the slice is acting something like a pointer
Then the next time it's the same memory so it gets overwritten
So you need to take a copy at each recursion to stop it overwriting
pathRecord = func(target, path []int) {
if (len(target) == 0) && (path[len(path)-1] == n) {
var c []int = make([]int, len(path))
copy(c, path)
//fmt.Println("insert into", payload) // should end with 7
result = append(result, c)
}
for _, v := range target {
pathRecord(graph[v], append(path, v)) //this append
}
}

Traverse a Map in decreasing order of values

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}]

How to use join multiple structs into the same one

I'm trying to play around with recursive structs, where when I have multiple I can add them together, creating a new struct with those embedded. However, I'm not sure what the proper way to approach this is.
I've included a code snippet below to further illustrate what I mean.
package main
import "fmt"
type Container struct {
F int
Collection []SubContainer
}
type SubContainer struct {
Key string
Value int
}
func main() {
commits := map[string]int{
"a": 1,
"b": 2,
"c": 3,
"d": 4,
}
sc := []SubContainer{}
c := []Container{}
count := 0
for k, v := range commits {
sc = append(sc, SubContainer{Key: k, Value: v})
count++
if len(sc) == 2 {
c = append(c, Container{Collection: sc, F: count})
sc = nil
}
}
for _, r := range c {
fmt.Println(r)
}
}
Result:
{2 [{a 1} {b 2}]}
{4 [{c 3} {d 4}]}
Desired result:
{6 {2 [{a 1} {b 2}]} {4 [{c 3} {d 4}]}}
Playground link: https://play.golang.org/p/j6rbhgcOoT
One caveat I'm having trouble wrapping my head around is that the commits length may change (I was initially thinking I could just create a different parent struct). Any suggestions would be appreciated... Is doing this somehow with recursive structs the right approach to accomplish this? Thanks!
I attempted to get close to the desired output without being sure about the exact goal, you will find below a modified version of the snippet you provided.
You can use the String() string method on a collection to customize the format.
package main
import "fmt"
type ContainerCollection struct {
Count int
List []Container
}
func (cc ContainerCollection) String() string {
total := 0
for _, c := range cc.List {
total += c.F
}
return fmt.Sprintf("{%d %v}", total, cc.List)
}
type Container struct {
F int
Collection []SubContainer
}
type SubContainer struct {
Key string
Value int
}
func main() {
commits := map[string]int{
"a": 1,
"b": 2,
"c": 3,
"d": 4,
}
c := ContainerCollection{Count: 0}
sc := []SubContainer{}
for k, v := range commits {
sc = append(sc, SubContainer{Key: k, Value: v})
c.Count++
if len(sc) == 2 {
c.List = append(c.List, Container{Collection: sc, F: c.Count})
sc = []SubContainer{}
}
}
// Should also cover odd number of commits
if len(sc) != 0 {
c.Count++
c.List = append(c.List, Container{Collection: sc, F: c.Count})
}
// for _, r := range c.List { fmt.Println(r) }
fmt.Println(c)
}
Result:
{6 [{2 [{a 1} {b 2}]} {4 [{c 3} {d 4}]}]}
Playground
Here's something with minimal modification to your code (just added a 'super' container, which is basically a summary struct). One probably need this only if this is being passed to another library/package/over the net etc., otherwise just maintaining the totalCount may be enough.
package main
import "fmt"
type SuperContainer struct {
TotalCount int
Containers []Container
}
type Container struct {
F int
Collection []SubContainer
}
type SubContainer struct {
Key string
Value int
}
func main() {
var totalCount int
commits := map[string]int{
"a": 1,
"b": 2,
"c": 3,
"d": 4,
}
sc := []SubContainer{}
c := []Container{}
count := 0
for k, v := range commits {
sc = append(sc, SubContainer{Key: k, Value: v})
count++
if len(sc) == 2 {
totalCount += count
c = append(c, Container{Collection: sc, F: count})
sc = nil
}
}
for _, r := range c {
fmt.Println(r)
}
supC := SuperContainer{TotalCount: totalCount, Containers: c}
fmt.Println(supC)
}
Playground: https://play.golang.org/p/yN3N3gHaCX

How to convert interface{} to []int?

I am programming in Go programming language.
Say there's a variable of type interface{} that contains an array of integers. How do I convert interface{} back to []int?
I have tried
interface_variable.([]int)
The error I got is:
panic: interface conversion: interface is []interface {}, not []int
It's a []interface{} not just one interface{}, you have to loop through it and convert it:
the 2022 answer
https://go.dev/play/p/yeihkfIZ90U
func ConvertSlice[E any](in []any) (out []E) {
out = make([]E, 0, len(in))
for _, v := range in {
out = append(out, v.(E))
}
return
}
the pre-go1.18 answer
http://play.golang.org/p/R441h4fVMw
func main() {
a := []interface{}{1, 2, 3, 4, 5}
b := make([]int, len(a))
for i := range a {
b[i] = a[i].(int)
}
fmt.Println(a, b)
}
As others have said, you should iterate the slice and convert the objects one by one.
Is better to use a type switch inside the range in order to avoid panics:
a := []interface{}{1, 2, 3, 4, 5}
b := make([]int, len(a))
for i, value := range a {
switch typedValue := value.(type) {
case int:
b[i] = typedValue
break
default:
fmt.Println("Not an int: ", value)
}
}
fmt.Println(a, b)
http://play.golang.org/p/Kbs3rbu2Rw
Func return value is interface{} but real return value is []interface{}, so try this instead:
func main() {
values := returnValue.([]interface{})
for i := range values {
fmt.Println(values[i])
}
}

Is there a way to iterate over a slice in reverse in Go?

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}

Resources