In golang how to loop through string interface - go

I have an interface that has some strings. I want to print each item one by one. For example, consider the following interface.Here I want to print sud first then man
var x interface{} = []string{"sud", "man"}

You can use something like this:
var x interface{} = []string{"sud", "man"}
res, ok := x.([]string)
if !ok {
fmt.Errorf("error")
}
for i := range res {
fmt.Println(res[i])
}

Related

Append to golang slice passed as empty interface

How to append to empty interface (that has been verified to be a *[]struct)?
func main() {
var mySlice []myStruct // myStruct can be any struct (dynamic)
decode(&mySlice, "...")
}
func decode(dest interface{}, src string) {
// assume dest has been verified to be *[]struct
var modelType reflect.Type = getStructType(dest)
rows, fields := getRows(src)
for _, row := range rows {
// create new struct of type modelType and assign all fields
model := reflect.New(modelType)
for field := fields {
fieldValue := getRowValue(row, field)
model.Elem().FieldByName(field).Set(fieldValue)
}
castedModelRow := model.Elem().Interface()
// append model to dest; how to do this?
// dest = append(dest, castedModelRow)
}
}
Things I've tried:
This simply panics: reflect: call of reflect.Append on ptr Value (as we pass &mySlice instead of mySlice)
dest = reflect.Append(reflect.ValueOf(dest), reflect.ValueOf(castedModelRow))
This works but doesn't set the value back to dest... in main func, len(mySlice) remains 0 after decode function is called.
func decode(dest interface{}, src string) {
...
result := reflect.MakeSlice(reflect.SliceOf(modelType), rowCount, rowCount)
for _, row : range rows {
...
result = reflect.Append(result, reflect.ValueOf(castedModelRow))
}
dest = reflect.ValueOf(result)
}
Here's how to fix the second decode function shown in the question. The statement
dest = reflect.ValueOf(result)
modifies local variable dest, not the caller's value. Use the following statement to modify the caller's slice:
reflect.ValueOf(dest).Elem().Set(result)
The code in the question appends decoded elements after the elements created in reflect.MakeSlice. The resulting slice has len(rows) zero values followed by len(rows) decoded values. Fix by changing
result = reflect.Append(result, reflect.ValueOf(castedModelRow))
to:
result.Index(i).Set(model)
Here's the update version of the second decode function in the question:
func decode(dest interface{}, src string) {
var modelType reflect.Type = getStructType(dest)
rows, fields := getRows(src)
result := reflect.MakeSlice(reflect.SliceOf(modelType), len(rows), len(rows))
for i, row := range rows {
model := reflect.New(modelType).Elem()
for _, field := range fields {
fieldValue := getRowValue(row, field)
model.FieldByName(field).Set(fieldValue)
}
result.Index(i).Set(model)
}
reflect.ValueOf(dest).Elem().Set(result)
}
Run it on the Playground.
You were very close with your original solution. You had to de-reference the pointer before calling the append operation. This solution would be helpful if your dest already had some existing elements and you don't want to lose them by creating a newSlice.
tempDest := reflect.ValueOf(dest).Elem()
tempDest = reflect.Append(tempDest, reflect.ValueOf(model.Interface()))
Similar to how #I Love Reflection pointed out, you finally need to set the new slice back to the pointer.
reflect.ValueOf(dest).Elem().Set(tempDest)
Overall Decode:
var modelType reflect.Type = getStructType(dest)
rows, fields := getRows(src)
tempDest := reflect.ValueOf(dest).Elem()
for _, row := range rows {
model := reflect.New(modelType).Elem()
for _, field := range fields {
fieldValue := getRowValue(row, field)
model.FieldByName(field).Set(fieldValue)
}
tempDest = reflect.Append(tempDest, reflect.ValueOf(model.Interface()))
}
reflect.ValueOf(dest).Elem().Set(tempDest)

How to print variable for, in outside for, in golang?

I have problem how to print a variable for, in outside for, in Go?
I'm using library GJSON gjson
I have try many way , I just entered the global variable but just appear final index,
like:
datePriodGlobal = DatePeriod.String()
and
datePriodGlobal = DatePeriod.String()
another way I try but appear just final index too, like below:
tempPayments:= "Envelope.Body.GetCustomReportResponse.GetCustomReportResult.ContractSummary.PaymentCalendarList.PaymentCalendar."
resultMapPriodTest := gjson.Get(jsonString,tempPayments + "#.Date")
resultContractsSubmittedTest := gjson.Get(jsonString, tempPayments + "#.ContractsSubmitted")
var datePriodGlobal string
for _, DatePeriod := range resultMapPriodTest.Array()[1:13] {
datePriodGlobal = fmt.Sprintf("%s", DatePeriod.String())
}
var contractsSubmittedGlobal string
for _, ContractsSubmitted := range resultContractsSubmittedTest.Array()[1:13]{
contractsSubmittedGlobal = fmt.Sprintf("%s", ContractsSubmitted.String())
}
fmt.Printf("%s | %s \t|",datePriodGlobal, contractsSubmittedGlobal)
}
I have json like this:
I will suggest just iterate over the PaymentCalendar as a slice of JSON objects rather than querying each field using the indexes as their pseudo-ids.
Here is a simple demonstration:
func main() {
jsonString := `
{
"PaymentCalendarList": {
"PaymentCalendar": [
{"ContractSubmitted": 10,
"Date": "2018-01-01T01:01:01"},
{"ContractSubmitted": 20,
"Date": "2018-01-01T02:02:02"},
{"ContractSubmitted": 30,
"Date": "2018-01-01T03:03:03"}
{"ContractSubmitted": 40,
"Date": "2018-01-01T04:04:04"}
{"ContractSubmitted": 50,
"Date": "2018-01-01T05:05:05"}
]
}
}`
result := gjson.Get(jsonString, "PaymentCalendarList.PaymentCalendar")
for _, paymentCal := range result.Array()[0:3] {
date := paymentCal.Get("Date")
contractSubmit := paymentCal.Get("ContractSubmitted")
fmt.Printf("%s | %s\n", date, contractSubmit)
}
}
Playground
"Cannot use 'DatePeriod' (type Result) as type string in assignment"
So, the variable DatePeriod is a Result type, not a String. You're specifying you want to print a string with %s, but not giving fmt.Sprintf a string, causing that error. The Sprintf is unnecessary if the value given was already a String.
Looking at gjson.go, the Result type has a String() method, so you'd want instead DatePeriod.String().
EDIT:
From your latest edit, I think I see your second issue. Your loops replace the ...Global string variables each time, so you'll only ever get the last value in the slice you've passed to range. Since your slices are identical in length, you might be better off with something like this:
resultMapPriodTest := gjson.Get(jsonString,tempPayments + "#.Date")
resultContractsSubmittedTest := gjson.Get(jsonString, tempPayments + "#.ContractsSubmitted")
dateArray := resultMapPriodTest.Array()[1:13]
contractsArray := resultContractsSubmittedTest.Array()[1:13]
for i := 0; i<len(dateArray); i++ {
d := dateArray[i].String()
c := contractsArray[i].String()
fmt.Printf("%s | %s \t|", d, c)
}

Comparing superset object in golang

I want to compare two objects
expected:= `"{\"method\":\"GET\",\"body\":{},\"uploadCount\":0}"`
result := `"{\"name\":\"xyz\",\"method\":\"GET\",\"body\":{},\"uploadCount\":0}"`
Now as we can see result is super-set of expected but when I use reflect.deepequal, it says false because it exactly compare two objects. I want to compare if result is superset of expected or not.
func main(){
result := "{\"name\":\"xyz\",\"method\":\"GET\",\"body\":{},\"uploadCount\":0}"
expected := "{\"method\":\"GET\",\"body\":{},\"uploadCount\":0}"
var _result interface{}
var _expected interface{}
json.Unmarshal([]byte(result),&_result)
json.Unmarshal([]byte(expected),&_expected)
reflect.deepequal(_result,_expected)
}
Without any refinements you could do something like
func main() {
result := "{\"name\":\"xyz\",\"method\":\"GET\",\"body\":{},\"uploadCount\":0}"
expected := "{\"method\":\"GET\",\"body\":{},\"uploadCount\":0}"
var _result map[string]interface{}
var _expected map[string]interface{}
json.Unmarshal([]byte(result), &_result)
json.Unmarshal([]byte(expected), &_expected)
isSuperset := true
for k, v := range _expected {
if !reflect.DeepEqual(v, _result[k]) {
isSuperset = false
break
}
}
fmt.Println(isSuperset)
}

Is there a way to write generic code to find out whether a slice contains specific element in Go?

I want to know is there a generic way to write code to judge whether a slice contains an element, I find it will frequently useful since there is a lot of logic to fist judge whether specific elem is already in a slice and then decide what to do next. But there seemed not a built-in method for that(For God's sake, why?)
I try to use interface{} to do that like:
func sliceContains(slice []interface{}, elem interface{}) bool {
for _, item := range slice {
if item == elem {
return true
}
}
return false
}
I thought interface{} is sort of like Object of Java, but apparently, I was wrong. Should I write this every time meet with a new struct of slice? Isn't there a generic way to do this?
You can do it with reflect, but it will be MUCH SLOWER than a non-generic equivalent function:
func Contains(slice, elem interface{}) bool {
sv := reflect.ValueOf(slice)
// Check that slice is actually a slice/array.
// you might want to return an error here
if sv.Kind() != reflect.Slice && sv.Kind() != reflect.Array {
return false
}
// iterate the slice
for i := 0; i < sv.Len(); i++ {
// compare elem to the current slice element
if elem == sv.Index(i).Interface() {
return true
}
}
// nothing found
return false
}
func main(){
si := []int {3, 4, 5, 10, 11}
ss := []string {"hello", "world", "foo", "bar"}
fmt.Println(Contains(si, 3))
fmt.Println(Contains(si, 100))
fmt.Println(Contains(ss, "hello"))
fmt.Println(Contains(ss, "baz"))
}
How much slower? about x50-x60 slower:
Benchmarking against a non generic function of the form:
func ContainsNonGeneic(slice []int, elem int) bool {
for _, i := range slice {
if i == elem {
return true
}
}
return false
}
I'm getting:
Generic: N=100000, running time: 73.023214ms 730.23214 ns/op
Non Generic: N=100000, running time: 1.315262ms 13.15262 ns/op
You can make it using the reflect package like that:
func In(s, e interface{}) bool {
slice, elem := reflect.ValueOf(s), reflect.ValueOf(e)
for i := 0; i < slice.Len(); i++ {
if reflect.DeepEqual(slice.Index(i).Interface(), elem.Interface()) {
return true
}
}
return false
}
Playground examples: http://play.golang.org/p/TQrmwIk6B4
Alternatively, you can:
define an interface and make your slices implement it
use maps instead of slices
just write a simple for loop
What way to choose depends on the problem you are solving.
I'm not sure what your specific context is, but you'll probably want to use a map to check if something already exists.
package main
import "fmt"
type PublicClassObjectBuilderFactoryStructure struct {
Tee string
Hee string
}
func main() {
// Empty structs occupy zero bytes.
mymap := map[interface{}]struct{}{}
one := PublicClassObjectBuilderFactoryStructure{Tee: "hi", Hee: "hey"}
two := PublicClassObjectBuilderFactoryStructure{Tee: "hola", Hee: "oye"}
three := PublicClassObjectBuilderFactoryStructure{Tee: "hi", Hee: "again"}
mymap[one] = struct{}{}
mymap[two] = struct{}{}
// The underscore is ignoring the value, which is an empty struct.
if _, exists := mymap[one]; exists {
fmt.Println("one exists")
}
if _, exists := mymap[two]; exists {
fmt.Println("two exists")
}
if _, exists := mymap[three]; exists {
fmt.Println("three exists")
}
}
Another advantage of using maps instead of a slice is that there is a built-in delete function for maps. https://play.golang.org/p/dmSyyryyS8
If you want a rather different solution, you might try the code-generator approach offered by tools such as Gen. Gen writes source code for each concrete class you want to hold in a slice, so it supports type-safe slices that let you search for the first match of an element.
(Gen also offers a few other kinds of collection and allows you to write your own.)

How to use OR operator properly in Golang

How can i do this simplified in Golang
var planningDate string
date, ok := data["planningDate"]
if !ok {
planningDate = util.TimeStamp()
} else {
planningDate = date
}
Thanx
I don't see any way to do this in a single line, as there is no ternary operator in Go. You cannot use | either as operands are not numbers. However, here is a solution in three lines (assuming date was just a temporary variable):
planningDate, ok := data["planningDate"]
if !ok {
planningDate = util.TimeStamp()
}
You can do something like:
func T(exp bool, a, b interface{}) interface{} {
if exp {
return a
}
return b
}
and use it whenever you want, like a ternary-operator:
planningDate = T((ok), date, util.TimeStamp())

Resources