Vlada
Vlada

Reputation: 145

Receiving any array type in function arguments

I just started playing with Go. I started creating a function that accepts an array of integers and returning the chunks of that array. To see what I mean, here is that program:

package main

import (
    "fmt"
    "math"
)

func main() {
    a:= []int{1,2,3,4,5,6,2, 231, 521,21, 51}

    c:=chunks(a[:], 3)
    fmt.Println(c) // [[1 2 3] [4 5 6] [2 231 521] [51]]
}

func chunks(a []int, size int) [][]int{
    var f float64 = float64(len(a)) / float64(size)
    size_of_wrapper := int(math.Ceil(f))
    i := 0
    j := 0
    twoD := make([][]int, size_of_wrapper )

    for i < len(a) {
        if i + size < len(a) {
            twoD[j] = make([]int, size)
            twoD[j] = append(a[i:i+size])
            i = i + size
            j++
        } else {
                if i + size == len(a){
                i++
            } else {
                twoD[j] = make([]int, 1)
                    twoD[j] = append(a[len(a)-1:])
                i++
            }

        }
    }

    return twoD 
}

Now, I have a question. Can I turn this function to be able to receive an array that has strings in it or any other types? Also, can I set to return that same type at the end? In this case, I return an array of arrays that contain only integer values.

I seem to really struggle to find the solution to this problem. One of the posts that I have read recommended to use interface type for this kind of job. Is that the only one?

Upvotes: 1

Views: 133

Answers (2)

Thundercat
Thundercat

Reputation: 120941

Because Go does not yet have the generics, the options are to use reflection or to duplicate code for each type. Here's how to use the reflect package:

// chunkAny slices a into size chunks and puts result in cp. 
// The variable cp must be pointer to slice of a's type.
func chunkAny(a interface{}, size int, cp interface{}) {
    av := reflect.ValueOf(a)
    cv := reflect.ValueOf(cp).Elem()
    cv.SetLen(0) // reset length in case backing array allocated
    i, j := 0, size
    for j < av.Len() {
        cv.Set(reflect.Append(cv, av.Slice(i, j)))
        i += size
        j += size
    }
    cv.Set(reflect.Append(cv, av.Slice(i, av.Len())))
}

Call the function like this:

a := []string{"a", "b", "c", "d"}
var b [][]string
chunkAny(a, 3, &b)

Run it on the playground.

Upvotes: 1

hoenir
hoenir

Reputation: 139

Can I turn this function to be able to receive an array that has strings in it or any other types? Also, can I set to return that same type at the end? I

Unfortunately the Go programming language does not have generics. AFAIK Go will have generics in the next 2.0 release so right now you mainly have 3 maybe 4 options.

1. Using the []interface{} type

Your function declaration will look something like this.

func chunks(a []interface{}, size int) [][]interface{}

Using this approach will imply some type assertion.

2. Using the reflect package

The definition will look the same but this time in your implementation, instead of using the type assertion technique you will use the reflect package to determine your type and retrieve values. Remember you will pay a reasonable cost using this approach.

3. Using some unsafe.Pointer magic.

You could use this generic pointer type and do some pointer arithmetic in a C spirit way.

func chunks(a unsafe.Pointer, len uintptr, size uintptr) unsafe.Pointer

I know if you search Go does not oficially support doing pointer arithmetic but you can fake it using tricks like.

package main

import "fmt"
import "unsafe"

func main() {
    vals := []int{10, 20, 30, 40}
    start := unsafe.Pointer(&vals[0])
    size := unsafe.Sizeof(int(0))
    for i := 0; i < len(vals); i++ {
        item := *(*int)(unsafe.Pointer(uintptr(start) + size*uintptr(i)))
        fmt.Println(item)
    }
}

4. Try using go generate in your building phase

You can find more information on how to generate go code base on what types you provide.

Upvotes: 1

Related Questions