Please suggest a better way for deep child comparison on map[interface{}]interface{}

xuanbao · · 438 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>A part of my program receives map of type <code>map[interface{}]interface{}</code> as input and it has to check for the presence of a certain value. Specifically, it has to confirm if the map contains a certain list and if the list contains a string called role1. In python, I&#39;d do it like this,</p> <pre><code>test = &#34;some-string&#34; mylist = map.get(&#34;mylist&#34;, []) return isinstance(mylist, (list, tuple,)) and test in mylist </code></pre> <p>... but with my extremely limited experience in Go, I&#39;ve come up with this,</p> <pre><code>test := &#34;some-string&#34; mylist = inputMap[&#34;mylist&#34;] if reflect.TypeOf(mylist).Kind() == reflect.Slice { for _, child := range mylist.([]interface{}) { if reflect.TypeOf(child).Kind() == reflect.String { if test == child { return true } } } } return false </code></pre> <p>I feel there must be a better way. What is the idiomatic way to solve this in Go?</p> <p>Let&#39;s say I don&#39;t have control over the input and it&#39;ll always be of type <code>map[interface{}]interface{}</code>. Is there a way I can reliably convert/cast it into a custom type without using reflect?</p> <hr/>**评论:**<br/><br/>neoasterisk: <pre><p>Since you mentioned that you are new to Go and it seems that you are trying to write Go code the way you write Python code, I would suggest you to watch Rob Pike&#39;s excellent talk &#34;Go proverbs&#34; especially the parts: <a href="https://youtu.be/PAAkCSZUG1c?t=7m35s" rel="nofollow">interface{} says nothing</a> and <a href="https://www.youtube.com/watch?v=PAAkCSZUG1c&amp;feature=youtu.be&amp;t=15m22s" rel="nofollow">Reflection is never clear</a>.</p></pre>lonahex: <pre><p>Thanks! </p></pre>the_jester: <pre><p>In general, the idiomatic answer is: don&#39;t. </p> <p>If you can possibly help it you should pass around something more specific than map[interface{}]interface{}. The latter could be a single-member struct or an interface. Your interface could even be a one-method interface that supplies a method that encompasses your &#34;some-string&#34; test. Then just use that method on the interface to satisfy your test.</p> <p>with a map[interface{}]MyMoreSpecificType you can just define your comparator as a typed method, and run it in your range function</p></pre>lonahex: <pre><p>The input to the program is completely out of my control. Think of it as any arbitrary JSON. It may contain <code>mylist</code> or may not. <code>mylist</code> could even be an integer or another map. What would be the ideal solution in this case?</p></pre>the_jester: <pre><p>You can unmarshal JSON into concrete type structs: <a href="http://blog.golang.org/json-and-go" rel="nofollow">http://blog.golang.org/json-and-go</a> - this can be done even with optional fields, just include named fields for the expected values, and the ones not present in the JSON will be skipped.</p> <p>Which is firmly preferable to do unless you have NO IDEA ahead of time what form your JSON data will take, in which case:</p> <ul> <li>Your life will be full of pain since you are getting fed arbitrary data, and</li> <li>Your code will end up looking pretty close to what you alread have.</li> </ul></pre>lonahex: <pre><p>The thing is that it is not actually JSON. My function receives <code>map[interface{}]interface{}</code> as input and I don&#39;t have control over it. I guess in this case, I&#39;m pretty much stuck with what I have. Thanks for the help!</p></pre>the_jester: <pre><p>Structurally speaking, whoever is passing you map/interface/interface is doing the dirty deed. That is a worthless data type that should be killed off at the first unmarshal step in a program.</p> <p>However if you know anything about that data - I.E. &#34;it will have either a list or a string in each tuple&#34;, then even though the caller was too lazy to do it, your code can structure the data; define your struct, marshal into it &#34;manually&#34;, and use that data for your loop &amp; test.</p></pre>danredux: <pre><p>I&#39;m not sure why no one recommended this, makes me think I might be misunderstanding your question, but reflect.DeepEqual seems to do exactly what you want.:</p> <p><a href="https://play.golang.org/p/oggz_D4-jQ" rel="nofollow">https://play.golang.org/p/oggz_D4-jQ</a></p></pre>rwbcxrz: <pre><p>You could achieve the same results using type assertions, but it&#39;s still not pretty:</p> <pre><code>func checkMap(m map[interface{}]interface{}, listName, test string) bool { _list, ok := m[listName] // see if m has a key of listName if !ok { return false } list, ok := _list.([]interface{}) // try to coerce _list into a []interface if !ok { return false } for _, listItem := range list { if child, ok := listItem.(string); ok { if test == child { return true } } } return false } sampleMap := map[interface{}]interface{}{ &#34;some-string&#34;: []interface{}{&#34;value1&#34;, &#34;value2&#34;}, } fmt.Println(checkMap(sampleMap, &#34;some-string&#34;, &#34;value1&#34;)) // true fmt.Println(checkMap(sampleMap, &#34;some-string&#34;, &#34;bogus&#34;)) // false fmt.Println(checkMap(sampleMap, &#34;bogus&#34;, &#34;value1&#34;)) // false </code></pre> <p>Overall, I&#39;d recommend refactoring the <code>map[interface{}]interface{}</code> into a more concrete type, but it&#39;s hard to say whether that&#39;s possible without more context.</p></pre>lonahex: <pre><blockquote> <p>list, ok := _list.([]interface{}) // try to coerce _list into a []interface</p> </blockquote> <p>The things is not that I don&#39;t know if it&#39;ll be present or not, but I don&#39;t even know if it&#39;ll be a list or not. It could very well be an integer, a string or another map. I guess this would fail in that case.</p> <blockquote> <p>Overall, I&#39;d recommend refactoring the map[interface{}]interface{} into a more concrete type, but it&#39;s hard to say whether that&#39;s possible without more context.</p> </blockquote> <p>I thought of that but the input is totally out of my control and I cannot influence it&#39;s overall structure in any way. Is there still a better way?</p></pre>shovelpost: <pre><p>So you are basically saying that you are the one that chose to have map[interface{}]interface{} as a function input. This is bad practice regardless of the fact that &#34;you are not sure about the JSON input&#34;. If you really want us to help give us examples of that JSON input in all it&#39;s forms. I am pretty sure there is a much better way to handle this.</p></pre>lonahex: <pre><p>I&#39;m not choosing it. It is a library I use that works like this. Actually, the library has a call that accepts a function as an argument and passes this beast to it which I have to process. Unfortunately for some reason, I cannot replace the lib with something else right now.</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

438 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传