ohler55 / ojg

Optimized JSON for Go
MIT License
857 stars 49 forks source link

Feature Request: Some way to iterate across object properties (k, v) or at least get keys #169

Closed scr-oath closed 6 months ago

scr-oath commented 6 months ago

JSONPath-Plus has one feature that might help in this respect - the ~ modifier to the * wildcard which returns keys or indices...

{
  "foo": {
    "abc": 1,
    "def": 2
  }
}
path, err := jp.ParseString("$.foo.*~")
if err != nil {
  return err
}
keys := path.Get(data)

Alternatively, if Expr had a "ForEach" that could call a callback with key, value or index, value that would be useful too!

scr-oath commented 6 months ago

Or, if jp.Walk has a mechanism to do this - perhaps that could be used… hmm… maybe we could check the length of path? But… the issue is - when you walk the path may be unpredictable or different from what you expect? What if you could walk from a given jp.Expr - that would let you know the starting point and then be able to compare the length to that +1 to be the keys…

scr-oath commented 6 months ago

I guess this works - but is there any nicer way to get the path as a string than fmt.Sprint?

func TestWalkLen(t *testing.T) {
    const jsonData = `{"foo":{"bar":1,"baz":2, "buz":{"a":1,"b":2,"c":3}}}`
    o, err := oj.ParseString(jsonData)
    require.NoError(t, err)
    jp.Walk(jp.C("foo").First(o), func(path jp.Expr, value any) {
        if len(path) == 2 {
            t.Log(fmt.Sprint(path[1]), value)
        }
    }, true)
}
=== RUN   TestWalk2
    keys_test.go:40: bar 1
    keys_test.go:40: baz 2
--- PASS: TestWalk2 (0.00s)
PASS
ohler55 commented 6 months ago

Note that jp.Expr has a String() method so you could just do t.Log(path[1:].String(), value).

scr-oath commented 6 months ago

I see. Frag didn't have one and I didn't think of slice semantics 👏

However... I realize that I have an object with deep values and this does a lot of extra work ignoring all of the deeper leaves…

Do you have any ideas on an efficient way to traverse flatly - just the key value or index value pairs somehow?

ohler55 commented 6 months ago

Do any of the leaf names collide with each other? For example {x:1, y:{x:2}}? If not then the last fragment of the path could be used otherwise you could turn the path into a string and use that as the key.

ohler55 commented 6 months ago

Did you figure it out on your own or do you want to continue?

scr-oath commented 6 months ago

Well the fallback technique is to just get the First and cast to map[string]any. Not terrible but I was just hopeful for flat for each iteration. You can close this one as less interesting than the other one I filed about bulk reshaping a la merging keys not clobbering the entire object