feat: add which_max and which_min to retrieve extreme element indices

Implement reflection-based WhichMax and WhichMin to dynamically find the index or key of the maximum/minimum element in slices, arrays, or maps. Functions validate orderability, handle empty collections, and dispatch via reflect.Kind. Expose as which_max and which_min GVal functions, with float64 type assertions for compatibility and preserved error handling.
This commit is contained in:
Eric Coissac
2026-06-02 14:37:39 +02:00
parent 14064c919e
commit 13a93fce11
2 changed files with 124 additions and 0 deletions
+110
View File
@@ -365,6 +365,116 @@ func filterMaxFromIterable(v reflect.Value, maximum interface{}) (interface{}, e
return result.Interface(), nil
}
// whichMaxFromIterable returns the index of the maximum element in a slice/array.
func whichMaxFromIterable(v reflect.Value) (int, error) {
var best reflect.Value
bestIdx := 0
for i := 0; i < v.Len(); i++ {
elem := unwrapInterface(v.Index(i))
if !isOrderedKind(elem.Kind()) {
return 0, fmt.Errorf("unsupported element type: %s", elem.Kind())
}
if i == 0 || greater(elem, best) {
best = elem
bestIdx = i
}
}
return bestIdx, nil
}
// whichMinFromIterable returns the index of the minimum element in a slice/array.
func whichMinFromIterable(v reflect.Value) (int, error) {
var minVal reflect.Value
minIdx := 0
for i := 0; i < v.Len(); i++ {
elem := unwrapInterface(v.Index(i))
if !isOrderedKind(elem.Kind()) {
return 0, fmt.Errorf("unsupported element type: %s", elem.Kind())
}
if i == 0 || less(elem, minVal) {
minVal = elem
minIdx = i
}
}
return minIdx, nil
}
// whichMaxFromMap returns the key associated with the maximum value in a map.
func whichMaxFromMap(v reflect.Value) (interface{}, error) {
var best reflect.Value
var bestKey reflect.Value
first := true
for _, key := range v.MapKeys() {
elem := unwrapInterface(v.MapIndex(key))
if !isOrderedKind(elem.Kind()) {
return nil, fmt.Errorf("unsupported element type: %s", elem.Kind())
}
if first || greater(elem, best) {
best = elem
bestKey = key
first = false
}
}
return bestKey.Interface(), nil
}
// whichMinFromMap returns the key associated with the minimum value in a map.
func whichMinFromMap(v reflect.Value) (interface{}, error) {
var minVal reflect.Value
var minKey reflect.Value
first := true
for _, key := range v.MapKeys() {
elem := unwrapInterface(v.MapIndex(key))
if !isOrderedKind(elem.Kind()) {
return nil, fmt.Errorf("unsupported element type: %s", elem.Kind())
}
if first || less(elem, minVal) {
minVal = elem
minKey = key
first = false
}
}
return minKey.Interface(), nil
}
// WhichMax returns the key (for a map) or index (for a slice/array) of the maximum value.
func WhichMax(data interface{}) (interface{}, error) {
v := reflect.ValueOf(data)
switch v.Kind() {
case reflect.Slice, reflect.Array:
if v.Len() == 0 {
return nil, errors.New("empty slice or array")
}
return whichMaxFromIterable(v)
case reflect.Map:
if v.Len() == 0 {
return nil, errors.New("empty map")
}
return whichMaxFromMap(v)
default:
return nil, fmt.Errorf("unsupported type: %s", v.Kind())
}
}
// WhichMin returns the key (for a map) or index (for a slice/array) of the minimum value.
func WhichMin(data interface{}) (interface{}, error) {
v := reflect.ValueOf(data)
switch v.Kind() {
case reflect.Slice, reflect.Array:
if v.Len() == 0 {
return nil, errors.New("empty slice or array")
}
return whichMinFromIterable(v)
case reflect.Map:
if v.Len() == 0 {
return nil, errors.New("empty map")
}
return whichMinFromMap(v)
default:
return nil, fmt.Errorf("unsupported type: %s", v.Kind())
}
}
func filterMinFromMap(v reflect.Value, minimum interface{}) (interface{}, error) {
minVal := reflect.ValueOf(minimum)
result := reflect.MakeMap(v.Type())