mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-25 22:00:58 +02:00
tpl/collections: Use MapRange/SetIterKey/SetIterValue for Where, Sort and Merge
Some relevant benchmarks: Where with maps: ``` cpu: Apple M1 Pro │ master.bench │ fix-mapkeys.bench │ │ sec/op │ sec/op vs base │ WhereMap-10 79.26µ ± 1% 26.58µ ± 1% -66.46% (p=0.002 n=6) │ master.bench │ fix-mapkeys.bench │ │ B/op │ B/op vs base │ WhereMap-10 56685.0 ± 0% 111.0 ± 1% -99.80% (p=0.002 n=6) │ master.bench │ fix-mapkeys.bench │ │ allocs/op │ allocs/op vs base │ WhereMap-10 2003.000 ± 0% 4.000 ± 0% -99.80% (p=0.002 n=6) ``` Merge: ``` │ master.bench │ fix-mapkeys.bench │ │ sec/op │ sec/op vs base │ Merge-10 3.285µ ± 0% 2.268µ ± 1% -30.96% (p=0.002 n=6) │ master.bench │ fix-mapkeys.bench │ │ B/op │ B/op vs base │ Merge-10 3.079Ki ± 0% 1.891Ki ± 0% -38.58% (p=0.002 n=6) │ master.bench │ fix-mapkeys.bench │ │ allocs/op │ allocs/op vs base │ Merge-10 64.00 ± 0% 26.00 ± 0% -59.38% (p=0.002 n=6) ``` Sort: ``` cpu: Apple M1 Pro │ master.bench │ fix-mapkeys.bench │ │ sec/op │ sec/op vs base │ SortMap-10 1008.0n ± 1% 915.5n ± 0% -9.18% (p=0.002 n=6) │ master.bench │ fix-mapkeys.bench │ │ B/op │ B/op vs base │ SortMap-10 640.0 ± 0% 512.0 ± 0% -20.00% (p=0.002 n=6) │ master.bench │ fix-mapkeys.bench │ │ allocs/op │ allocs/op vs base │ SortMap-10 16.00 ± 0% 15.00 ± 0% -6.25% (p=0.002 n=6) ```
This commit is contained in:
@@ -75,9 +75,13 @@ func caseInsensitiveLookup(m, k reflect.Value) (reflect.Value, bool) {
|
|||||||
return v, hreflect.IsTruthfulValue(v)
|
return v, hreflect.IsTruthfulValue(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, key := range m.MapKeys() {
|
k2 := reflect.New(m.Type().Key()).Elem()
|
||||||
if strings.EqualFold(k.String(), key.String()) {
|
|
||||||
return m.MapIndex(key), true
|
iter := m.MapRange()
|
||||||
|
for iter.Next() {
|
||||||
|
k2.SetIterKey(iter)
|
||||||
|
if strings.EqualFold(k.String(), k2.String()) {
|
||||||
|
return iter.Value(), true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,17 +94,28 @@ func mergeMap(dst, src reflect.Value) reflect.Value {
|
|||||||
// If the destination is Params, we must lower case all keys.
|
// If the destination is Params, we must lower case all keys.
|
||||||
_, lowerCase := dst.Interface().(maps.Params)
|
_, lowerCase := dst.Interface().(maps.Params)
|
||||||
|
|
||||||
|
k := reflect.New(dst.Type().Key()).Elem()
|
||||||
|
v := reflect.New(dst.Type().Elem()).Elem()
|
||||||
|
|
||||||
// Copy the destination map.
|
// Copy the destination map.
|
||||||
for _, key := range dst.MapKeys() {
|
iter := dst.MapRange()
|
||||||
v := dst.MapIndex(key)
|
for iter.Next() {
|
||||||
out.SetMapIndex(key, v)
|
k.SetIterKey(iter)
|
||||||
|
v.SetIterValue(iter)
|
||||||
|
out.SetMapIndex(k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all keys in src not already in destination.
|
// Add all keys in src not already in destination.
|
||||||
// Maps of the same type will be merged.
|
// Maps of the same type will be merged.
|
||||||
for _, key := range src.MapKeys() {
|
k = reflect.New(src.Type().Key()).Elem()
|
||||||
sv := src.MapIndex(key)
|
sv := reflect.New(src.Type().Elem()).Elem()
|
||||||
dv, found := caseInsensitiveLookup(dst, key)
|
|
||||||
|
iter = src.MapRange()
|
||||||
|
for iter.Next() {
|
||||||
|
sv.SetIterValue(iter)
|
||||||
|
k.SetIterKey(iter)
|
||||||
|
|
||||||
|
dv, found := caseInsensitiveLookup(dst, k)
|
||||||
|
|
||||||
if found {
|
if found {
|
||||||
// If both are the same map key type, merge.
|
// If both are the same map key type, merge.
|
||||||
@@ -112,14 +127,15 @@ func mergeMap(dst, src reflect.Value) reflect.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if dve.Type().Key() == sve.Type().Key() {
|
if dve.Type().Key() == sve.Type().Key() {
|
||||||
out.SetMapIndex(key, mergeMap(dve, sve))
|
out.SetMapIndex(k, mergeMap(dve, sve))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if lowerCase && key.Kind() == reflect.String {
|
kk := k
|
||||||
key = reflect.ValueOf(strings.ToLower(key.String()))
|
if lowerCase && k.Kind() == reflect.String {
|
||||||
|
kk = reflect.ValueOf(strings.ToLower(k.String()))
|
||||||
}
|
}
|
||||||
out.SetMapIndex(key, sv)
|
out.SetMapIndex(kk, sv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -99,18 +99,21 @@ func (ns *Namespace) Sort(ctx context.Context, l any, args ...any) (any, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
keys := seqv.MapKeys()
|
|
||||||
for i := 0; i < seqv.Len(); i++ {
|
|
||||||
p.Pairs[i].Value = seqv.MapIndex(keys[i])
|
|
||||||
|
|
||||||
|
iter := seqv.MapRange()
|
||||||
|
i := 0
|
||||||
|
for iter.Next() {
|
||||||
|
key := iter.Key()
|
||||||
|
value := iter.Value()
|
||||||
|
p.Pairs[i].Value = value
|
||||||
if sortByField == "" {
|
if sortByField == "" {
|
||||||
p.Pairs[i].Key = keys[i]
|
p.Pairs[i].Key = key
|
||||||
} else if sortByField == "value" {
|
} else if sortByField == "value" {
|
||||||
p.Pairs[i].Key = p.Pairs[i].Value
|
p.Pairs[i].Key = p.Pairs[i].Value
|
||||||
} else {
|
} else {
|
||||||
v := p.Pairs[i].Value
|
v := p.Pairs[i].Value
|
||||||
var err error
|
var err error
|
||||||
for i, elemName := range path {
|
for j, elemName := range path {
|
||||||
v, err = evaluateSubElem(ctxv, v, elemName)
|
v, err = evaluateSubElem(ctxv, v, elemName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -120,12 +123,13 @@ func (ns *Namespace) Sort(ctx context.Context, l any, args ...any) (any, error)
|
|||||||
}
|
}
|
||||||
// Special handling of lower cased maps.
|
// Special handling of lower cased maps.
|
||||||
if params, ok := v.Interface().(maps.Params); ok {
|
if params, ok := v.Interface().(maps.Params); ok {
|
||||||
v = reflect.ValueOf(params.GetNested(path[i+1:]...))
|
v = reflect.ValueOf(params.GetNested(path[j+1:]...))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.Pairs[i].Key = v
|
p.Pairs[i].Key = v
|
||||||
}
|
}
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -409,7 +409,6 @@ func (ns *Namespace) checkWhereArray(ctxv, seqv, kv, mv reflect.Value, path []st
|
|||||||
for i, elemName := range path {
|
for i, elemName := range path {
|
||||||
var err error
|
var err error
|
||||||
vvv, err = evaluateSubElem(ctxv, vvv, elemName)
|
vvv, err = evaluateSubElem(ctxv, vvv, elemName)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -442,9 +441,12 @@ func (ns *Namespace) checkWhereArray(ctxv, seqv, kv, mv reflect.Value, path []st
|
|||||||
// checkWhereMap handles the where-matching logic when the seqv value is a Map.
|
// checkWhereMap handles the where-matching logic when the seqv value is a Map.
|
||||||
func (ns *Namespace) checkWhereMap(ctxv, seqv, kv, mv reflect.Value, path []string, op string) (any, error) {
|
func (ns *Namespace) checkWhereMap(ctxv, seqv, kv, mv reflect.Value, path []string, op string) (any, error) {
|
||||||
rv := reflect.MakeMap(seqv.Type())
|
rv := reflect.MakeMap(seqv.Type())
|
||||||
keys := seqv.MapKeys()
|
k := reflect.New(seqv.Type().Key()).Elem()
|
||||||
for _, k := range keys {
|
elemv := reflect.New(seqv.Type().Elem()).Elem()
|
||||||
elemv := seqv.MapIndex(k)
|
iter := seqv.MapRange()
|
||||||
|
for iter.Next() {
|
||||||
|
k.SetIterKey(iter)
|
||||||
|
elemv.SetIterValue(iter)
|
||||||
switch elemv.Kind() {
|
switch elemv.Kind() {
|
||||||
case reflect.Array, reflect.Slice:
|
case reflect.Array, reflect.Slice:
|
||||||
r, err := ns.checkWhereArray(ctxv, elemv, kv, mv, path, op)
|
r, err := ns.checkWhereArray(ctxv, elemv, kv, mv, path, op)
|
||||||
|
Reference in New Issue
Block a user