在Go语言中递归打印map中的所有值。

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

Print all values in map recursively in Go

问题

假设我有这样一张地图:

var results map[string]interface{}

值可以是任何类型,甚至是另一个地图。我该如何打印出所有的值?如果值是一个数组,我想要逐个打印数组中的每个项。如果它是另一个地图,我想要在地图上递归调用同一个函数。

英文:

Suppose I have a map of this type:

var results map[string]interface{}

The values can be anything, even another map. How would I print all the values? If the value is an array, I want to print each item in the array individually. If it is another map, I want to recursively call the same function on the map.

答案1

得分: 1

我无耻地从某个网站上抄袭了这段代码:

import (
	"fmt"
	"reflect"
	"strings"
)

/*
InspectStruct打印实例化结构体的内部细节。非常适用于调试。
用法:InspectStruct(req, 0) -> 打印所有子元素
*/

func InspectStructV(val reflect.Value, level int) {
	if val.Kind() == reflect.Interface && !val.IsNil() {
		elm := val.Elem()
		if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
			val = elm
		}
	}
	if val.Kind() == reflect.Ptr {
		val = val.Elem()
	}

	for i := 0; i < val.NumField(); i++ {
		valueField := val.Field(i)
		typeField := val.Type().Field(i)
		address := "not-addressable"

		if valueField.Kind() == reflect.Interface && !valueField.IsNil() {
			elm := valueField.Elem()
			if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
				valueField = elm
			}
		}

		if valueField.Kind() == reflect.Ptr {
			valueField = valueField.Elem()

		}
		if valueField.CanAddr() {
			address = fmt.Sprintf("0x%X", valueField.Addr().Pointer())
		}

		fmt.Printf("%vField Name: %s,\t Field Value: %v,\t Address: %v\t, Field type: %v\t, Field kind: %v\n",
			strings.Repeat("\t", level),
			typeField.Name,
			//valueField.Interface(),
			address,
			typeField.Type,
			valueField.Kind())

		if valueField.Kind() == reflect.Struct {
			InspectStructV(valueField, level+1)
		}
	}
}

func InspectStruct(v interface{}, level int) {
	InspectStructV(reflect.ValueOf(v), level)
}

这段代码用于打印实例化结构体的内部细节,非常适用于调试。你可以使用InspectStruct(req, 0)来打印所有子元素。

英文:

I shamelessly ripped this from a site some time ago:

import (
&quot;fmt&quot;
&quot;reflect&quot;
&quot;strings&quot;
)
/*
InspectStruct prints the guts of an instantiated struct. Very handy for debugging
usage: InspectStruct(req, 0) -&gt; prints all children
*/
func InspectStructV(val reflect.Value, level int) {
if val.Kind() == reflect.Interface &amp;&amp; !val.IsNil() {
elm := val.Elem()
if elm.Kind() == reflect.Ptr &amp;&amp; !elm.IsNil() &amp;&amp; elm.Elem().Kind() == reflect.Ptr {
val = elm
}
}
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
for i := 0; i &lt; val.NumField(); i++ {
valueField := val.Field(i)
typeField := val.Type().Field(i)
address := &quot;not-addressable&quot;
if valueField.Kind() == reflect.Interface &amp;&amp; !valueField.IsNil() {
elm := valueField.Elem()
if elm.Kind() == reflect.Ptr &amp;&amp; !elm.IsNil() &amp;&amp; elm.Elem().Kind() == reflect.Ptr {
valueField = elm
}
}
if valueField.Kind() == reflect.Ptr {
valueField = valueField.Elem()
}
if valueField.CanAddr() {
address = fmt.Sprintf(&quot;0x%X&quot;, valueField.Addr().Pointer())
}
fmt.Printf(&quot;%vField Name: %s,\t Field Value: %v,\t Address: %v\t, Field type: %v\t, Field kind: %v\n&quot;,
strings.Repeat(&quot;\t&quot;, level),
typeField.Name,
//valueField.Interface(),
address,
typeField.Type,
valueField.Kind())
if valueField.Kind() == reflect.Struct {
InspectStructV(valueField, level+1)
}
}
}
func InspectStruct(v interface{}, level int) {
InspectStructV(reflect.ValueOf(v), level)
}

答案2

得分: 0

使用fmt.Printf中的%v可以遍历内部的映射并打印元素,通过调用每个元素的String()方法。现在,如果你想改变内部类型的默认打印方式,只需在该类型上实现String()方法。在下面的示例中,我创建了一个每行打印一个整数的[]int版本。

package main

import (
	"fmt"
	"strings"
)

type OnePerLineInt []int

func (ip OnePerLineInt) String() string {
	var ret []string
	for _, a := range ip {
		ret = append(ret, fmt.Sprintf("Item: %v\n", a))
	}
	return strings.Join(ret, "")
}

func main() {
	arr := &OnePerLineInt{1, 2, 3}
	arr1 := []int{4, 5, 6}
	foo := make(map[string]interface{})
	bar := make(map[string]interface{})
	bar["a"] = "foobar1"
	bar["b"] = "foobar2"
	foo["1"] = arr
	foo["2"] = bar
	foo["3"] = arr1
	fmt.Printf("foo contents: %v\n", foo)
	fmt.Printf("bar is :%v\n", bar)
}

输出结果为:

foo contents: map[1:Item: 1
Item: 2
Item: 3
2:map[a:foobar1 b:foobar2] 3:[4 5 6]]
bar is :map[a:foobar1 b:foobar2]

你可以在这里查看代码:http://play.golang.org/p/kc6xm7dHwx

英文:

Using %v on fmt.Printf will traverse the inner maps and print the elements
too by calling String() on each. Now if you want to change this default printing on an inner type you just have to implement String() on that type. in the following example i created a version of []int that prints one int per line.

package main
import (
&quot;fmt&quot;
&quot;strings&quot;
)
type OnePerLineInt []int
func (ip OnePerLineInt) String() string {
var ret []string
for _, a := range ip {
ret = append(ret, fmt.Sprintf(&quot;Item: %v\n&quot;, a))
}
return strings.Join(ret, &quot;&quot;)
}
func main() {
arr := &amp;OnePerLineInt{1, 2, 3}
arr1 := []int{4, 5 , 6}
foo := make(map[string]interface{})
bar := make(map[string]interface{})
bar[&quot;a&quot;] = &quot;foobar1&quot;
bar[&quot;b&quot;] = &quot;foobar2&quot;
foo[&quot;1&quot;] = arr
foo[&quot;2&quot;] = bar
foo[&quot;3&quot;] = arr1
fmt.Printf(&quot;foo contents: %v\n&quot;, foo)
fmt.Printf(&quot;bar is :%v\n&quot;, bar)
}

prints:

foo contents: map[1:Item: 1
Item: 2
Item: 3
2:map[a:foobar1 b:foobar2] 3:[4 5 6]]
bar is :map[a:foobar1 b:foobar2]

http://play.golang.org/p/kc6xm7dHwx

huangapple
  • 本文由 发表于 2015年2月28日 06:32:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/28775485.html
匿名

发表评论

匿名网友

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

确定