Skip to content

Commit

Permalink
🐛 fix: maputil - fix wildcard(*) not working on func GetByPath. issues
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Jul 13, 2023
1 parent c7c8f5c commit 4affd6a
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 9 deletions.
39 changes: 30 additions & 9 deletions maputil/get.go
Expand Up @@ -4,6 +4,8 @@ import (
"reflect"
"strconv"
"strings"

"github.com/gookit/goutil/reflects"
)

// some consts for separators
Expand Down Expand Up @@ -101,27 +103,46 @@ func GetByPathKeys(mp map[string]any, keys []string) (val any, ok bool) {

// k is index number
idx, err := strconv.Atoi(k)
if err != nil {
return nil, false
}

if idx >= len(tData) {
if err != nil || idx >= len(tData) {
return nil, false
}
item = tData[idx]
default:
if k == Wildcard && kl == i+2 { // * is last key
return tData, true
}

rv := reflect.ValueOf(tData)
// check is slice
if rv.Kind() == reflect.Slice {
i, err := strconv.Atoi(k)
if err != nil {
if k == Wildcard {
// * is not last key, find sub item data
sl := make([]any, 0)
for si := 0; si < rv.Len(); si++ {
el := reflects.Indirect(rv.Index(si))
if el.Kind() != reflect.Map {
return nil, false
}

// el is map value.
if val, ok = GetByPathKeys(ToAnyMap(el.Interface()), keys[i+2:]); ok {
sl = append(sl, val)
}
}

if len(sl) > 0 {
return sl, true
}
return nil, false
}
if i >= rv.Len() {

// check k is index number
ii, err := strconv.Atoi(k)
if err != nil || ii >= rv.Len() {
return nil, false
}

item = rv.Index(i).Interface()
item = rv.Index(ii).Interface()
continue
}

Expand Down
82 changes: 82 additions & 0 deletions maputil/get_test.go
Expand Up @@ -4,7 +4,9 @@ import (
"reflect"
"testing"

"github.com/gookit/goutil/arrutil"
"github.com/gookit/goutil/dump"
"github.com/gookit/goutil/jsonutil"
"github.com/gookit/goutil/maputil"
"github.com/gookit/goutil/testutil/assert"
)
Expand Down Expand Up @@ -225,3 +227,83 @@ func TestEachAnyMap(t *testing.T) {
maputil.EachAnyMap(1, nil)
})
}

func TestGetByPathKeys(t *testing.T) {
val, ok := maputil.GetByPathKeys(map[string]any{}, nil)
assert.True(t, ok)
assert.Empty(t, val)

t.Run("sub string-map", func(t *testing.T) {
mp := map[string]any{
"top": map[string]string{"key": "value"},
}

val, ok := maputil.GetByPathKeys(mp, []string{"top", "key"})
assert.True(t, ok)
assert.Eq(t, "value", val)
})
t.Run("sub any-map", func(t *testing.T) {
mp := map[string]any{
"top": map[any]any{"key": "value"},
}

val, ok := maputil.GetByPathKeys(mp, []string{"top", "key"})
assert.True(t, ok)
assert.Eq(t, "value", val)
})

t.Run("sub []map[string]any", func(t *testing.T) {
mp := map[string]any{
"top": []map[string]any{
{"key": "value"},
{"key": "value1"},
},
}

val, ok := maputil.GetByPathKeys(mp, []string{"top", "1"})
assert.True(t, ok)
assert.NotEmpty(t, val)
assert.IsKind(t, reflect.Map, val)
val, ok = maputil.GetByPathKeys(mp, []string{"top", "10"})
assert.False(t, ok)
assert.Nil(t, val)
val, ok = maputil.GetByPathKeys(mp, []string{"top", "invalid"})
assert.False(t, ok)
assert.Nil(t, val)

val, ok = maputil.GetByPathKeys(mp, []string{"top", "*"})
assert.True(t, ok)
assert.IsKind(t, reflect.Slice, val)
assert.NotEmpty(t, val)
val, ok = maputil.GetByPathKeys(mp, []string{"top", "*", "key"})
assert.True(t, ok)
assert.IsKind(t, reflect.Slice, val)
assert.Len(t, val, 2)
})
}

// https://github.com/gookit/goutil/issues/109
func TestIssues_109(t *testing.T) {
mp := make(map[string]any)
err := jsonutil.DecodeString(`{
"success": true,
"result": {
"total": 2,
"records": [
{
"id": "59fab0fa-8f0a-4065-8863-1dae40166015"
},
{
"id": "7c1bd7f9-2ef4-44c8-9756-2e85156ca58f"
}
]
}
}`, &mp)
assert.NoErr(t, err)
dump.P(mp)

ids, ok := maputil.GetByPath("result.records.*.id", mp)
dump.P(ids, arrutil.AnyToStrings(ids))
assert.True(t, ok)
assert.Len(t, ids, 2)
}

0 comments on commit 4affd6a

Please sign in to comment.