Reputation: 295
I'm trying to get the path to value from a json file:
for example :
x: 4
z:
y: 6
t: 8
a:
b:
p: 0
m:
c : 4
the output that i want is :
x=4, z.y=6, z.t=8, a.b.p=0, m.c=4
i wrote a functions below :
func parsecustom(aMap map[string]interface{}) (string, string) {
var sol string
var k string
for key, val := range aMap {
switch concreteVal := val.(type) {
case map[string]interface{}:
x, _ := parseMap(val.(map[string]interface{}))
sol = key + "." + x
k += sol + ","
case string:
sol = key + "=" + concreteVal + " "
k += sol + ","
case float64:
sol = key + "=" + strconv.FormatFloat(concreteVal, 'f', -1, 64) + " "
k+= sol + ","
default:
k = " "
}
}
return sol, TrimSuffix(k, ",")
}
but it didn't take into account this case :
z:
y: 6
t: 8
it override, it print only z.t=8 and ignore z.y=6
Any hints please how to solve this problem
Upvotes: 3
Views: 6353
Reputation: 5898
In this part of the switch, where you recurse, you are only considering the return value of sol
which is the last element to be parsed.
case map[string]interface{}:
x, _ := parseMap(val.(map[string]interface{}))
Given the values (assuming they will be iterated in order, not strictly true as maps are unordered):
z:
y: 6
t: 8
You would end up with a return from the recursive call as:
sol: "t=8"
k: "y=6 ,t=8 ,"
Then you take the first return value sol
, call it variable x
and prepend "z."
to it. This means that you ignore the "y=6"
part that is in k
. You are only considering sol
, which is the last value to be parsed by the recursive call.
Here is a runnable example, copy pasted from code in the question: https://play.golang.org/p/F8gwq9YMxWu
Update - solution:
Instead of attempting to return multiple things from each of the calls to parse, it would be easier to pass a string, a prefix, that should be added to all the values lower down the call stack. This way the values that are returned already have the desired end value. And can just be added to resulting string.
func parse(prefix string, m map[string]interface{}) string {
if len(prefix) > 0 { // only add the . if this is not the first call.
prefix = prefix + "."
}
// builder stores the results string, appended to it
var builder string
for mKey, mVal := range m {
// update a local prefix for this map key / value combination
pp := prefix + mKey
switch typedVal := mVal.(type) {
case string:
builder += fmt.Sprintf("%s%s, ", pp, typedVal)
case float64:
builder += fmt.Sprintf("%s.%-1.0f, ", pp, typedVal)
case map[string]interface{}:
// add all the values to the builder, you already know they are correct.
builder += parse(pp, typedVal)
}
}
// return the string that this call has built
return builder
}
To call it for the very first time, pass a prefix of ""
result := parse("", m)
Runnable example solution https://play.golang.org/p/Y-m9rQCY0xw
Upvotes: 1
Reputation: 8212
To address the problem that child values should bearing parents' name, I think when recursing, there needs to be a prefix
parameter. And instead of asking the parents to insert those prefix into the string, it would feel more normal to let the children write the prefix just as they are going to write their own parsed results. So I changed your code:
func parseMap(aMap map[string]interface{}, prefix string, b *strings.Builder) {
var sol string
for key, val := range aMap {
switch concreteVal := val.(type) {
case map[string]interface{}:
parseMap(concreteVal,prefix+key+".",b)
case string:
sol = key + "=" + concreteVal + " "
b.WriteString(prefix)
b.WriteString(sol)
b.WriteRune(',')
case float64:
sol = key + "=" + strconv.FormatFloat(concreteVal, 'f', -1, 64) + " "
b.WriteString(prefix)
b.WriteString(sol)
b.WriteRune(',')
default:
//What?
panic("Unsupported")
}
}
}
Since we are concating a lot of strings here, I used the strings.Builder
for string building. b.WriteString(x)
is just appeding x
to the tail of the string b
holds.
And because we now use a strings.builder
and a prefix
, so I write a function to wrap it:
func parsecustom(aMap map[string]interface{}) string {
b:=&strings.Builder{}
parseMap(aMap,"",b)
return strings.TrimSuffix(b.String(),",")
}
Now if you run fmt.Println(parsecustom(data))
, you can get output: x=4 ,z.t=8 ,z.y=6 ,a.b.p=0 ,m.c=4
as desired.
Full example: https://play.golang.org/p/_QIp2LRYjm7
Upvotes: 2