如何在Go语言中动态地将对象分配给切片中的字符串键?

huangapple go评论89阅读模式
英文:

How to dynamically assign objects to a string key in slices in Go?

问题

我正在尝试从已创建的数组中创建一个新的数组。我已经有的数组是:

{
"id": 1,
"category": "fruits",
"name": "Apple",
"description": "Apple is my favorite fruit."
}

{
"id": 2,
"category": "colors",
"name": "Red",
"description": "Red color is always charming."
}

{
"id": 3,
"category": "flowers",
"name": "Lotus",
"description": "It is one of the most beautiful flowers in this world."
}

{
"id": 4,
"category": "colors",
"name": "Pink",
"description": "A romantic color, mostly liked by women."
}
{
"id": 5,
"category": "flowers",
"name": "Rose",
"description": "I love roses."
}

{
"id": 6,
"category": "fruits",
"name": "Mango",
"description": "Mango is one of my favorite fruits."
}

现在我需要创建一个数组,并填充数据,如下所示:

"elements":{
"fruits":{
0:{
"id": 1,
"category": "fruits",
"name": "Apple",
"description": "Apple is my favorite fruit."
}
1:{
"id": 6,
"category": "fruits",
"name": "Mango",
"description": "Mango is one of my favorite fruits."
}
}
"flowers":{
0:{
"id": 3,
"category": "flowers",
"name": "Lotus",
"description": "It is one of the most beautiful flowers in this world."
}
1:{
"id": 5,
"category": "flowers",
"name": "Rose",
"description": "I love roses."
}
}
"colors":{
0:{
"id": 2,
"category": "colors",
"name": "Red",
"description": "Red color is always charming."
}
1:{
"id": 4,
"category": "colors",
"name": "Pink",
"description": "A romantic color, mostly liked by women."
}
}
}

我尝试过的是:

arr 			:= make(map[string]interface{})
arrCate 		:= make(map[string]interface{})
arrCateFlower 	:= make(map[int]interface{})
arrCateColor    := make(map[int]interface{})
arrCateFruit 	:= make(map[int]interface{})

for index, data := range dataVals{
	if(data.Category == "flower"){
		arrCateFlower[index] = data
	}
	if(data.Category == "colors"){
		arrCateColor[index] = data	
	}
	if(data.Category == "fruits"){
		arrCateFruit[index] = data	
	}
}
arrCate["flowers"] = arrCateFlower
arrCate["colors"] = arrCateColor
arrCate["fruits"] = arrCateFruit
arr["elements"] = arrCate

其中dataVals包含顶部给出的未格式化数据。通过应用上述代码,我能够获得正确的输出。但我不认为这是一种高效的方式。如果我尝试像这样做:

arr 			:= make(map[string]interface{})
arrCate 		:= make(map[string]interface{})

for _, data := range dataVals{
  arrCate[data.Category] = data    
}
arr["elements"] = arrCate

那么我会得到类似于:

"elements":{
"fruits":{
"id": 6,
"category": "fruits",
"name": "Mango",
"description": "Mango is one of my favorite fruits."
}
"flowers":{
"id": 5,
"category": "flowers",
"name": "Rose",
"description": "I love roses."
}
"colors":{
"id": 4,
"category": "colors",
"name": "Pink",
"description": "A romantic color, mostly liked by women."
}
}

在循环中,只会得到该类别的最后一个元素。我不明白如何在不使用任何静态值的情况下获取数组中的所有元素。

我已经花了几个小时在这个问题上。有人能告诉我错在哪里吗?

英文:

I am trying to create an array from already created array. Array that I have is

{
  "id": 1,
  "category": "fruits",
  "name": "Apple",
  "description": "Apple is my favorite fruit."
}

{
  "id": 2,
  "category": "colors",
  "name": "Red",
  "description": "Red color is always charming."
}

{
  "id": 3,
  "category": "flowers",
  "name": "Lotus",
  "description": "It is one of the most beautiful flowers in this world."
}

{
  "id": 4,
  "category": "colors",
  "name": "Pink",
  "description": "A romantic color, mostly liked by women."
}
{
  "id": 5,
  "category": "flowers",
  "name": "Rose",
  "description": "I love roses."
}

{
  "id": 6,
  "category": "fruits",
  "name": "Mango",
  "description": "Mango is one of my favorite fruits."
}

Now I need to create an array and populate data like:

"elements":{
   "fruits":{
      0:{
         "id": 1,
         "category": "fruits",
         "name": "Apple",
         "description": "Apple is my favorite fruit."
       }
     1:{
        "id": 6,
        "category": "fruits",
        "name": "Mango",
        "description": "Mango is one of my favorite fruits."
       }
     }
    "flowers":{
        0:{
            "id": 3,
            "category": "flowers",
            "name": "Lotus",
            "description": "It is one of the most beautiful flowers in this world."
         }
        1:{
          "id": 5,
          "category": "flowers",
          "name": "Rose",
          "description": "I love roses."
        }
      }
    "colors":{
       0:{
          "id": 2,
          "category": "colors",
          "name": "Red",
          "description": "Red color is always charming."
        }
      1:{
        "id": 4,
        "category": "colors",
        "name": "Pink",
        "description": "A romantic color, mostly liked by women."
       } 
    }
}

What I have tried is:

    arr 			:= make(map[string]interface{})
	arrCate 		:= make(map[string]interface{})
	arrCateFlower 	:= make(map[int]interface{})
	arrCateColor    := make(map[int]interface{})
	arrCateFruit 	:= make(map[int]interface{})
	
    for index, data := range dataVals{
		if(data.Category == "flower"){
			arrCateFlower[index] = data
		}
		if(data.Category == "colors"){
			arrCateColor[index] = data	
		}
		if(data.Category == "fruits"){
			arrCateFruit[index] = data	
		}
	}
	arrCate["flowers"] = arrCateFlower
	arrCate["colors"] = arrCateColor
	arrCate["fruits"] = arrCateFruit
	arr["elements"] = arrCate

Where dataVals contain the unformatted data given at the top. By applying the above code I am able to get the proper output. But I don't think it is efficient way. If I try something like

    arr 			:= make(map[string]interface{})
	arrCate 		:= make(map[string]interface{})
	
    for _, data := range dataVals{
      arrCate[data.Category] = data    
	}
	arr["elements"] = arrCate

Then I get something like:

"elements":{
   "fruits":{
              "id": 6,
              "category": "fruits",
              "name": "Mango",
              "description": "Mango is one of my favorite fruits."
            }
    "flowers":{
               "id": 5,
               "category": "flowers",
               "name": "Rose",
               "description": "I love roses."
              }
    "colors":{
               "id": 4,
               "category": "colors",
               "name": "Pink",
               "description": "A romantic color, mostly liked by women." 
             }
}

the last elements of that particular category in the loop. I don't understand how can I get all the elements in the array without using any static values in code.

I have already spent hours in this. Can anyone please tell what am i missing in it?

答案1

得分: 9

我希望你能接受额外的外部 {} 对。没有外部 {} 对的版本请参考:https://play.golang.org/p/SSTgln0qJc

为了方便他人对我的解决方案进行批评,我在这里包含了代码,稍作修改:

package main

import (
	"fmt"
	"encoding/json"
	"log"
	"strings"
)

var dataAsString = `` //将数据放在 `` 之间

type Item struct {
	Id          int    `json:"id"`
	Category    string `json:"category"`
	Name        string `json:"name"`
	Description string `json:"description"`
}

type CategoryToItemSliceMap map[string][]Item
type CategoryToIndexItemMap map[string]map[int]Item

func main() {
	// 首先读取数据,我们使用解码器,因为输入是作为单独的 JSON 对象流而给出的,而不是一个大的单一对象。

	decoder := json.NewDecoder(strings.NewReader(dataAsString))
	var ourData []Item
	for decoder.More() {
		var it Item
		err := decoder.Decode(&it)
		if err != nil {
			log.Fatalln(err)
		}
		ourData = append(ourData, it)
	}

	// 根据类别收集项目
	catToItemSlice := CategoryToItemSliceMap{}
	for _, v := range ourData {
		catToItemSlice[v.Category] = append(catToItemSlice[v.Category], v)
	}

	// 将这些切片转换为 int -> Item 映射,以便我们获得编码 JSON 中的索引号
	catToIndexItemMap := CategoryToIndexItemMap{}
	for k, v := range catToItemSlice {
		if catToIndexItemMap[k] == nil {
			catToIndexItemMap[k] = map[int]Item{}
		}
		for index, item := range v {
			catToIndexItemMap[k][index] = item
		}
	}

	// 最简单的方法是获得 "elements: ",而不需要额外的外部 {} 大括号对
	fmt.Printf("elements: ")

	// 我们的输出中只有一个 JSON 对象,而且是一个映射,所以我们可以使用 Unmarshal,而不需要流编码器。并使用 MarshalIndent 获得漂亮的缩进。
	out, err := json.MarshalIndent(catToIndexItemMap, "", "    ")
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Println(string(out))

}
英文:

https://play.golang.org/p/y-I6Fb_61R

I hope you can live with the additional outer {} pair.

And without the outer {} pair: https://play.golang.org/p/SSTgln0qJc

To not just have a bunch of links and to enable easy criticism of my solution by others, I include the code here, slightly redacted:

package main
import (
"fmt"
"encoding/json"
"log"
"strings"
)
var dataAsString = `` //put data between the ``
type Item struct {
Id          int    `json:"id"`
Category    string `json:"category"`
Name        string `json:"name"`
Description string `json:"description"`
}
type CategoryToItemSliceMap map[string][]Item
type CategoryToIndexItemMap map[string]map[int]Item
func main() {
// first read the data, we use a decoder as the input was given
// as a stream of seperate json objects and not a big single one.
decoder := json.NewDecoder(strings.NewReader(dataAsString))
var ourData []Item
for decoder.More() {
var it Item
err := decoder.Decode(&it)
if err != nil {
log.Fatalln(err)
}
ourData = append(ourData, it)
}
// collect items according to categories
catToItemSlice := CategoryToItemSliceMap{}
for _,v := range ourData {
catToItemSlice[v.Category] = append(catToItemSlice[v.Category],v)
}
// turn those slices into int -> Item maps so we get the index numbers
// in the encoded json
catToIndexItemMap := CategoryToIndexItemMap{}
for k,v := range catToItemSlice {
if catToIndexItemMap[k] == nil {
catToIndexItemMap[k] = map[int]Item{}
}
for index, item := range v {
catToIndexItemMap[k][index] = item
}
}
// easiest way to get the "elements: " without an additional outer {} 
// brace pair
fmt.Printf("elements: ")
// We only have one json object in the output and that is a map, so we
// can use Unmarshal and don't need a streaming encoder. And get nice
// indentation with MarshalIndent.
out, err := json.MarshalIndent(catToIndexItemMap, "", "    ")
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(out))
}

答案2

得分: 0

// 如果属性名称相等,则将值设置为字段
// target接收对象的值
func Assign(target interface{}, object interface{}) {
t := reflect.ValueOf(target).Elem()
o := reflect.ValueOf(object).Elem()
for i := 0; i < o.NumField(); i++ {
for j := 0; j < t.NumField(); j++ {
if o.Type().Field(i).Name == t.Type().Field(j).Name {
t.Field(j).Set(o.Field(i))
}
}
}
}
// 使用此示例,对象接口不同但字段相等
// Assign(&target, &object)

英文:
// If atributes names equals , set value to fields
// target receive values of object
func Assign(target interface{}, object interface{}) {
t := reflect.ValueOf(target).Elem()
o := reflect.ValueOf(object).Elem()
for i := 0; i &lt; o.NumField(); i++ {
for j := 0; j &lt; t.NumField(); j++ {
if o.Type().Field(i).Name == t.Type().Field(j).Name {
t.Field(j).Set(o.Field(i))
}
}
}
}
// Use this exemple objects interfaces diffrents but fields are equals
// Assign(&amp;target, &amp;object)

huangapple
  • 本文由 发表于 2017年6月1日 14:14:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/44299808.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定