The datastore.GetMulti(c appengine.Context, key []*Key, dst interface{}) API allows me to get 1000 entities at most. I want to get more.
An obvious way to solve this generically is to create a wrapper function mypkg.GetMulti() which sub slices (key[0:1000], key[1000:2000]...) the original arguments and calls datastore.GetMulti() several times with them.
It's pretty clear how to sub slice key []*Key, but how do I sub slice dst interface{} which could be:
// dst must be a []S, []*S, []I or []P, for some struct type S, some interface
// type I, or some non-interface non-pointer type P such that P or *P
// implements PropertyLoadSaver. If an []I, each element must be a valid dst
// for Get: it must be a struct pointer or implement PropertyLoadSaver.
//
// As a special case, PropertyList is an invalid type for dst, even though a
// PropertyList is a slice of structs. It is treated as invalid to avoid being
// mistakenly passed when []PropertyList was intended.
Since you are the caller of datastore.GetMulti which takes an interface{} argument, you can provide any concrete value as that argument; it doesn't need to be converted to the empty-interface type beforehand. In other words, anything and everything implements the empty interface, so just pass that thing.
func GetMulti() {
mySlice := make([]Whatever, 3000, 3000)
for i := 0; i < 3; i++ {
subSlice := mySlice[i * 1000 : (i + 1) * 1000]
datastore.GetMulti(c,k, subSlice) // 'c' and 'k' assumed to be defined
}
}
In case mypkg.GetMulti should be a generic function, taking an interface{} value as well, then you'll have to use reflection as in the following example where instead of fmt.Println with the length of the subslice you'd call datastore.GetMulti with each subslice:
package main
import "fmt"
import "reflect"
func GetMulti(i interface{}) {
v := reflect.ValueOf(i)
if v.Kind() != reflect.Slice {
panic("argument not a slice")
}
l := v.Len()
p := (l / 1000)
for i := 0; i < p; i++ {
fmt.Println(v.Slice(i*1000, (i+1)*1000).Len())
}
fmt.Println(v.Slice(p*1000, l).Len())
}
func main() {
s := make([]int, 3560, 3560)
GetMulti(s)
}
Related
This is my golang code
package test
import (
"fmt"
"testing"
)
func TestOne(t *testing.T) {
bytes := make([]byte, 0)
bytes = append(bytes, 1, 2, 3) // pass
bytes = append(bytes, []byte{1, 2, 3}...) // pass
bytes = append(bytes, "hello"...) // pass too, ok. reference: As a special case, it is legal to append a string to a byte slice
}
func TestTwo(t *testing.T) {
printBytes([]byte{1, 2, 3}...) // pass
printBytes("abcdefg"...) // fail
}
func printBytes(b ...byte) {
fmt.Println(b)
}
These are some code in strings.Builder
func (b *Builder) WriteString(s string) (int, error) {
b.copyCheck()
b.buf = append(b.buf, s...)
return len(s), nil
}
The param s can be regards as slice type when be used in function append .
But I defined a function printBytes like append,
when I invoke like this
printBytes("abcdefg"...)
The "abcdefg" seems like not be regards as a type slice
From the append documentation:
As a special case, it is legal to append a string to a byte slice, like this:
slice = append([]byte("hello "), "world"...)
Other than this special case (and a similar case for copy), string is not treated like a slice type in Go.
"Built-in" functions like this are allowed to have special cases that don't strictly follow the general type rules in Go, because their behaviour is actually part of the language specification itself. See Appending to and copying slices.
First of all, apologies if this question is confused since I'm just trying out Go and have no idea what I'm doing. I have a struct composed of a variety of attributes of different types, example:
type foo struct {
bar string
baz int
bez []string
(...)
Initially I wanted to iterate over all these attributes and print the value if it existed, but I realized you cannot range over a struct the same way you could, say, a list or map. So I've tried out a few tricks with no luck (like trying to iterate over a separate list of attributes), and I think it's better I just ask for help because I'm probably in over my head here.
The idea is that if I create a new instance of this struct, I'd like to be able to then only print values that are set:
obj := foo{"bar_string", 1}
Given that the string slice bez is not set in obj, I'd like to be able to do something like (pseudo):
for i in obj:
print i
Giving:
"bar_string"
1
Ideally, not printing [] which I guess is the zero value for bez.
Am I approaching this whole thing wrong? The reason I'm not using a map is because I'd like the attributes to be different types, and I'd like future differing objects I'm working in to be organized into structs for clarity.
Go doesn't have builtin struct iteration. The for ... range statement is applicable only to:
all entries of an array, slice, string or map, or values received on a channel
or defined types with one of those underlying types (e.g. type Foo []int)
If you want to iterate over a struct known at compile time, you might be better off just accessing fields one by one.
If you want to must iterate over a struct not known at compile time, you can use the reflect package (not recommended):
type Foo struct {
Bar string
Baz int
Quux []int
}
// x := Foo{"bar", 1, nil}
func printAny(x interface{}) {
v := reflect.ValueOf(x)
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
if !reflect.DeepEqual(field.Interface(), reflect.Zero(field.Type()).Interface()) {
fmt.Println(field)
// bar
// 1
}
}
}
...but it's slower and there are some gotchas, for example:
field.Interface() panics if the field is unexported
in the if clause you can't just use the comparison operator == because operands might be not comparable:
you have to make sure that the zero value for field types is what you expect
If your goal is to just print the struct, you can simply implement the Stringer interface, where you can do type-safe checks the way you want without reflect:
type Foo struct {
Bar string
Baz int
Quux []int
}
func (f Foo) String() string {
s := []string{f.Bar, strconv.Itoa(f.Baz)}
if f.Quux != nil {
s = append(s, fmt.Sprintf("%v", f.Quux))
}
return strings.Join(s, "\n")
}
func main() {
fmt.Println(Foo{"bar", 1, nil})
// bar
// 1
}
A Go playground
I am passing in a string array and an empty integer array into a function. The point of the function is to convert each element of the string array to an integer and store that into the integer array. When I print the integer array from within the function itself, everything is fine. However, when I try to print the integer array outside of the function, it prints an empty array.
employeeDataInt is the integer array, and employeeDataString is the string array.
I apologize if this is a dumb question but I am new to go. Thanks
package main
import (
"bufio"
"fmt"
"log"
"os"
"strconv"
"strings"
)
func strToInt(employeeDataString []string, emplyoeeDataInt []int) []int {
for _, i := range employeeDataString[2:] {
j, err := strconv.Atoi(i)
if err != nil {
panic(err)
}
employeeDataInt = append(employeeDataInt, j)
fmt.Println(employeeDataInt) //this prints out the appropriate array
}
return employeeDataInt
}
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter file name: ")
fileName, err := reader.ReadString('\n')
if err != nil {
log.Fatalf("failed opening file: %s", err)
}
fileName = strings.TrimSuffix(fileName, "\n")
file, err := os.Open(fileName)
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)
var employeeLine []string
for scanner.Scan() {
employeeLine = append(employeeLine, scanner.Text())
}
file.Close()
var employeeDataString = []int{}
for _, employee := range employeeLine {
employeeDataString := strings.Split(employee, " ")
strToInt(employeeDataString, employeeDataInt)
fmt.Println(playerData2) //this is outputting just `[]`
}
}
You aren't taking the value of the array and thus the Slice you passed into the function might or might not be updated correctly.
strToInt(employeeDataString, employeeDataInt)
// should be
employeeDataInt = strToInt(employeeDataString, employeeDataInt)
And while at it, you are never assigning playerData2. So fmt.Println(playerData2) will always be [].
But aside from that there are some subtle issues with your usage of Arrays/Slices here:
First the difference between Slices and Arrays:
Go does not allow you to directly work with Arrays.
Unless they have a fixed length ([3]int{} or []int{1,2,3]) you aren't actually looking at an array but at a Slice ([]int).
The slice is just a pointer to an array (along with it's capacity and some other info) and it essentially allows Go to safely muck around with arrays because you never grow an existing array (the size of an array is fixed at initialization). So you can never append to an array.
What Go does to give you the illusion of appending to an array is having a larger than required underlying array, and the Slice controls the access to that array. So if the underlying array has a capacity of 5 and you already stored 3 items in it you can do 2 append operations without having to allocate a new array and copy the existing array elements to the new memory location.
So when you are passing a []int you are actually passing an array pointer (by value) around.
This leads to the next gotcha in your code: The use of append.
As mentioned above, append takes a Slice, looks at the underlying array and how much space is actually left and then adds to it or allocates a new array. If a new array is allocated append returns a new slice that points to the new array.
So calling:
foo := []{1,2,3}
append(foo, 4)
append(foo, 5)
append(foo, 6)
fmt.Print(foo)
// => might return 1,2,3,4,5
You always have to take the return value of append otherwise you risk still referencing the "old" slice that didn't get the new items appended.
So the correct way to grow a Slice, or work with Slices in general is to keep in mind that: Slices are passed by value, so always keep updating your variables with the return values of Slice modifying functions.
There are a few issues in your code:
You're discarding the return value of strToInt.
You're trying to utilize employeeDataInt in main but it is undefined there (which should be causing a compile error, not a runtime issue).
You're declaring employeeDataString twice, in two different scopes in main (inside and outside the for loop), with two different types ([]string and []int). The outer-scoped variable is unused, so should also be causing a compile error.
You're printing playerData2 which is never defined or used - again, this should be causing a compiler error, not incorrect behavior.
Given there were compile errors in the code, either some crucial code was missing from your post, or you did not notice/mention the compile errors.
The correct code within main would be:
var employeeDataInt []int // Seems like you just have the wrong variable name here
for _, employee := range employeeLine {
employeeDataString := strings.Split(employee, " ")
// You're missing the assignment here
employeeDataInt = strToInt(employeeDataString, employeeDataInt)
fmt.Println(employeeDataInt) // This was referencing the wrong variable
}
I would like to write a program that receive a array (of string, int, or whatever) and create another array of the same type contain only the first element.
For example:
for a array of strings arr := []string("hello", "world")
my output would be arr2 := []string(arr[0]);
I can't use the copy function because to do that, i would have to create(make) a new slice for it. And in this case, i still have to discover which type the first array is (string, int, bool, and so on...)
Maybe I could use the reflect.TypeOf() but i would still not know how to use that information to create the same type of slice or array.
i'm not considering to use conditionals for that.
For example:
if reflect.TypeOf(arr) == []int {
arr := []int(arr[0])
} else if reflect.TypeOf(arr) == []string
arr := []string(arr[0])
} ...
I would be glad get a help on there.
Thanks in advance.
You could just subslice it in place:
s2 := s1[0:1]
But if you really need to create a new slice, you can do it like this:
func f(s interface{}) interface{} {
v := reflect.ValueOf(s)
t := v.Type()
res := reflect.MakeSlice(t, 1, 1)
res.Index(0).Set(v.Index(0))
return res.Interface()
}
Playground: http://play.golang.org/p/w1N3pgvAwr.
http://play.golang.org/p/jdWZ9boyrh
I am getting this error
prog.go:29: invalid receiver type *[]Sentence ([]Sentence is an unnamed type)
prog.go:30: cannot range over S (type *[]Sentence)
[process exited with non-zero status]
when my function tries to receive structure array.
What does it mean by an unnamed type? Why can't it be named? I can name it outside function and also pass them as arguments with them being named.
It does not work. So I just passed []Sentence as an argument and solve the problem that I need to. But when passing them as arguments, I had to return a new copy.
I still think that it would be nice if I can just let the function receive the struct array and does not have to return anything.
Like below:
func (S *[]Sentence)MarkC() {
for _, elem := range S {
elem.mark = "C"
}
}
var arrayC []Sentence
for i:=0; i<5; i++ {
var new_st Sentence
new_st.index = i
arrayC = append(arrayC, new_st)
}
//MarkC(arrayC)
//fmt.Println(arrayC)
//Expecting [{0 C} {1 C} {2 C} {3 C} {4 C}]
//but not working
It is not working either with []Sentence.
Is there anyway that I can make a function receive Struct array?
I'm still learning Go but it seems that it wants the type named. You know, "array of sentences" - that is really an anonymous type. You just have to name it.
(also, use for or one-variable form of range to avoid copying elements (and discarding your changes))
type Sentence struct {
mark string
index int
}
type SentenceArr []Sentence
func (S SentenceArr)MarkC() {
for i := 0; i < len(S); i++ {
S[i].mark = "S"
}
}