Mark C.
Mark C.

Reputation: 11

How do I return data from a 'for' loop in Go?

I'm trying to convert a []*Struct to a JSON response. I'm able to get the data from the []*Struct and iterate over it. The part I get stuck around is the iterating over a loop and then returning the data to an interface{}.

I've tried to putting an interface in an interface, but I just can't make ends meet. Here's what I have at the moment; I was told to turn to Stack Overflow if I had any issues.

package main

import (
   "log"
   "context"
   "cloud.google.com/go/datastore"
)

var ctx = context.Background()

type ItemList struct {
    Id    string    `datastore:"_id"`
    Name  string    `datastore:"name"`
}

type Data struct {
    ManyItems   []Item
}

type Item struct {
    Id    string    `json:"id"`
    Name  string    `json:"name"`
}

func Get() ([]*ItemList, error) {
  client, err := datastore.NewClient(ctx, "project-name")
  if err != nil {
     log.Println(err)
  }

  var Response []*ItemList
  query := datastore.NewQuery("query")
  _, err = client.GetAll(ctx, query, &Response)
  if err != nil {
     log.Println(err)
  }

  return Response, error
}

func Read() (interface{}) {
   resp, err := Get()
   if err != nil {
      log.Println(err)
   }

   for i, _ := range resp {
     // this is where i get stuck
     r := &Data{
               ManyItems: Item{
                        Id: resp[i].Id,
                        Name: resp[i].Name,
               },
          }
     return r
   }
}

How can I do it?

Upvotes: 1

Views: 11848

Answers (2)

filipe
filipe

Reputation: 2047

You are returning inside the for loop and this will only return the first item.

Also I see that ManyItems is an array of Item ([]Item) and you are assigning a single Item which is wrong.

Update: Here you have the complete code:

// Input Json data
type ItemList struct {
    Id    string    `datastore:"_id"`
    Name  string    `datastore:"name"`
}

//Convert *ItemList to *Item
func (list *ItemList) ToItem() *Item {
    return &Item {
        Id    : list.Id,
        Name  : list.Name,
    }
}
// Output Json data
type Data struct {
    ManyItems   []*Item `json:"data"`
}

//Output json item object
type Item struct {
    Id    string    `json:"id"`
    Name  string    `json:"name"`
}
// The read function
func Read() (string, error) {
   resp, err := Get()
   if err != nil {
      log.Println(err)
      return "", err
   }

   list := make([]Item, len(resp))
   for i, _ := range resp {
     list[i] = resp[i].ToItem()
   }

    b, err := json.Marshal(&Data{list})
    if err != nil {
        fmt.Println(err)
        return "", err
    }

   return string(b), nil
}

Test: Playground

Upvotes: 3

mkopriva
mkopriva

Reputation: 38303

You can predeclare a *Data variable and then inside the loop, on each iteration add the item to its ManyItems field. Since ItemList and Item have the same structure, ie the same fields in the same order, you can convert directly one to the other. (As long as you are on Go 1.8 or newer)

func Read() interface{} {
    resp, err := Get()
    if err != nil {
        log.Println(err)
        return nil
    }

    data := &Data{ManyItems: make([]Item, len(resp))}
    for i := range resp {
        data.ManyItems[i] = Item(*resp[i])
    }
    return data
}

However if possible it would be more efficient to just return the slice you already have, something like this.

type ItemList struct {
    Id   string `datastore:"_id" json:"id"`
    Name string `datastore:"name" json:"name"`
}

type Data struct {
    ManyItems []*ItemList
}

func Read() interface{} {
    resp, err := Get()
    if err != nil {
        log.Println(err)
        return nil
    }

    return &Data{ManyItems: resp}
}

Upvotes: 2

Related Questions