大佬们求解一个 go map 无序的问题

2021-06-02 20:34:34 +08:00
 liyaojian

要求:需要根据用户传入的 jsonStr 中的nameuser_id的顺序拼接其值。

代码:

package main

import (
	"encoding/json"
	"fmt"
	"reflect"
)

func main() {
	jsonStr := `{"name":"tom","user_id":"123"}` // 这是传入的参数,name 与 user_id 顺序不能确定先后
	var str string
	m := make(map[string]interface{})
	_ = json.Unmarshal([]byte(jsonStr), &m)
	
	v := reflect.ValueOf(m)
	keys := v.MapKeys()
	for _, key := range keys {
		v1 := v.MapIndex(key).Interface().(string)
		str += v1
	}
	fmt.Println(str)
	// 由于 map 无序,不能固定输出:tom123
	// 如何保持与 json 中键一致,固定输出?
	// 比如若 json_str := `{"user_id":"123""name":"tom"}` 则输出 123tom
}

在线运行: https://play.golang.org/p/_ZMfsISpKWz

还请大佬们赐教,感激不尽。

3073 次点击
所在节点    Go 编程语言
30 条回复
Jason0803
2021-06-02 20:46:37 +08:00
为什么不用 struct
sunny352787
2021-06-02 20:48:07 +08:00
确定参数就用 struct,不确定就全取出来塞到 slice 里排序再用
hello2060
2021-06-02 20:51:19 +08:00
不懂 go, 但是你既然只能转到 map, map 里的 key 不是按照输入顺序排序, 那 map 是没法解决这问题的.
liyaojian
2021-06-02 20:54:03 +08:00
@Jason0803 #1
@sunny352787 #2
struct 也不能确定用户传过来的 name 和 user_id 的顺序啊,我需要获取 name 和 user_id 对应顺序的值拼接字符串,不同的顺序拼接的字符串不一样
liyaojian
2021-06-02 20:55:52 +08:00
@hello2060 #3 所以问下论坛里的大佬有没有其他方法解决这个问题,jsonStr 是用户的传参,这个不能变
virusdefender
2021-06-02 20:57:48 +08:00
如果只有这么两个字段的话,正则处理下就好了
lujjjh
2021-06-02 21:01:58 +08:00
很奇怪的需求,但也是有办法的: https://play.golang.org/p/PAYLlXZjhgF
Kisesy
2021-06-02 21:11:04 +08:00
sphawkcn
2021-06-02 21:19:41 +08:00
@lujjjh #7 在以前的 AAuto (现在叫 aardio )的圈子里,有个人跟你一样叫 lujjjh,请问是你吗?我的朋友
xiaoyiyu
2021-06-02 21:26:27 +08:00
struct + String() 不就可以了 https://play.golang.org/p/V6RXkf-sceA
hello2060
2021-06-02 21:30:35 +08:00
@liyaojian 如果只要能 work 就行, 你搜整个 string 看那个在前哪个在后就可以了, 搜"name":和"user_id":
hello2060
2021-06-02 21:35:07 +08:00
我不懂 go, 随便搜了下, 问题也没仔细看 https://play.golang.org/p/yZ5DxZLIMXC 这个是不是可以你看看
rekulas
2021-06-02 21:52:42 +08:00
很奇怪的需求 用第三方库可以容易的实现 采用类似其他语言的 loop 对象的 key 即可
```
package main

import (
"github.com/tidwall/gjson"
"log"
)

type loop func(key gjson.Result,value gjson.Result)

func main() {
jsonStr := `{"name":"tom","user_id":"123"}`
expectedResult := ""
result := gjson.Parse(jsonStr)
result.ForEach(func(key, value gjson.Result) bool {
expectedResult += value.String()
return true
})
log.Println(expectedResult)
}
```

不过我敢肯定,这个功能的设计绝对是有问题的。。
rekulas
2021-06-02 21:56:18 +08:00
才发现跟#8 的重复了
SorcererXW
2021-06-02 21:57:51 +08:00
直接用正则表达式把所有 key 提取出来就能知道顺序了
https://stackoverflow.com/questions/24300112/regex-to-match-keys-in-json
CEBBCAT
2021-06-02 22:02:01 +08:00
其实按照规范来说,JSON 的键值对是无序的[1],可以主张更换数据结构来规避这个“需要 JSON 的键按序解析”的问题。但假如这个用户比较顶……

再回到这个问题,map 的遍历在 Go 标准实现中也是无序的,所以楼主你这是在做无用功,或者说危险功。

我能想到的是定义一个 interface,实现一个 String()方法。然后在它的 UnmarshalJSON()中根据`"name"`和`"user_id"`的先后出现位置,来返回不同的 interface 。


1. https://stackoverflow.com/a/16870531
CEBBCAT
2021-06-02 22:03:27 +08:00
话说回来,我在这里祈祷楼主知道“XY 问题”这个名词,不要等到最后才说出真实的需求……
lujjjh
2021-06-02 22:19:21 +08:00
@sphawkcn 是我

@CEBBCAT 认同你的观点,既然规范里明确说了是无序的,就**不应该**依赖某种语言 /某种库下有序的特定实现。

Go 1 开始刻意把迭代 map 的顺序设计成随机,也是为了防止程序员依赖某个 Go 版本实现下的迭代顺序,而不同实现的迭代顺序是有可能不同的,就会造成可移植性的问题,索性设计成随机了。

顺便分享一个近期的故事: https://twitter.com/zty0826/status/1398477411000360960
hallDrawnel
2021-06-02 22:26:01 +08:00
把 key 复制出来放到 slice 排序后从 map 取值拼接。这是要做啥?某种奇怪的需要按照顺序的签名 or 校验操作?
liyaojian
2021-06-02 22:38:52 +08:00
@CEBBCAT #17 感谢,学到一个新词😄

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/780962

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX