mirror of
https://github.com/metabarcoding/obitools4.git
synced 2025-06-29 08:10:45 +00:00
add min and max to the obitools expression language
This commit is contained in:
@ -268,7 +268,11 @@ func (marker *Marker) CheckTagLength() error {
|
||||
reverse_length[len(tags.Reverse)]++
|
||||
}
|
||||
|
||||
maxfl, _ := obiutils.MaxMap(forward_length)
|
||||
maxfl, _, err := obiutils.MaxMap(forward_length)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(forward_length) > 1 {
|
||||
others := make([]int, 0)
|
||||
@ -280,7 +284,11 @@ func (marker *Marker) CheckTagLength() error {
|
||||
return fmt.Errorf("forward tag length %d is not the same for all the PCRs : %v", maxfl, others)
|
||||
}
|
||||
|
||||
maxrl, _ := obiutils.MaxMap(reverse_length)
|
||||
maxrl, _, err := obiutils.MaxMap(reverse_length)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(reverse_length) > 1 {
|
||||
others := make([]int, 0)
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
// corresponds to the last commit, and not the one when the file will be
|
||||
// commited
|
||||
|
||||
var _Commit = "e065e29"
|
||||
var _Commit = "f1b9ac4"
|
||||
var _Version = "Release 4.4.0"
|
||||
|
||||
// Version returns the version of the obitools package.
|
||||
|
@ -135,6 +135,12 @@ var OBILang = gval.NewLanguage(
|
||||
length := obiutils.Len(args[0])
|
||||
return (float64)(length), nil
|
||||
}),
|
||||
gval.Function("min", func(args ...interface{}) (interface{}, error) {
|
||||
return obiutils.Min(args[0])
|
||||
}),
|
||||
gval.Function("max", func(args ...interface{}) (interface{}, error) {
|
||||
return obiutils.Max(args[0])
|
||||
}),
|
||||
gval.Function("contains", func(args ...interface{}) (interface{}, error) {
|
||||
if obiutils.IsAMap(args[0]) {
|
||||
val := reflect.ValueOf(args[0]).MapIndex(reflect.ValueOf(args[1]))
|
||||
|
@ -1,7 +1,10 @@
|
||||
package obiutils
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
@ -31,19 +34,200 @@ func MinMaxSlice[T constraints.Ordered](vec []T) (min, max T) {
|
||||
return
|
||||
}
|
||||
|
||||
func MaxMap[K comparable, T constraints.Ordered](values map[K]T) (K, T) {
|
||||
|
||||
if len(values) == 0 {
|
||||
log.Panicf("empty map")
|
||||
}
|
||||
|
||||
func MaxMap[K comparable, T constraints.Ordered](values map[K]T) (K, T, error) {
|
||||
var maxKey K
|
||||
var maxValue T
|
||||
|
||||
if len(values) == 0 {
|
||||
return maxKey, maxValue, errors.New("Empty map")
|
||||
}
|
||||
|
||||
first := true
|
||||
for k, v := range values {
|
||||
if v > maxValue {
|
||||
if v > maxValue || first {
|
||||
maxValue = v
|
||||
maxKey = k
|
||||
first = false
|
||||
}
|
||||
}
|
||||
return maxKey, maxValue
|
||||
|
||||
return maxKey, maxValue, nil
|
||||
}
|
||||
|
||||
func MinMap[K comparable, T constraints.Ordered](values map[K]T) (K, T, error) {
|
||||
var minKey K
|
||||
var minValue T
|
||||
if len(values) == 0 {
|
||||
return minKey, minValue, errors.New("Empty map")
|
||||
}
|
||||
|
||||
first := true
|
||||
for k, v := range values {
|
||||
if v < minValue || first {
|
||||
minValue = v
|
||||
minKey = k
|
||||
first = false
|
||||
}
|
||||
}
|
||||
|
||||
return minKey, minValue, nil
|
||||
}
|
||||
|
||||
// Min returns the smallest element in a slice/array or map,
|
||||
// or the value itself if data is a single comparable value.
|
||||
// Returns an error if the container is empty or the type is unsupported.
|
||||
func Min(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 minFromIterable(v)
|
||||
case reflect.Map:
|
||||
if v.Len() == 0 {
|
||||
return nil, errors.New("empty map")
|
||||
}
|
||||
return minFromMap(v)
|
||||
default:
|
||||
if !isOrderedKind(v.Kind()) {
|
||||
return nil, fmt.Errorf("unsupported type: %s", v.Kind())
|
||||
}
|
||||
// single comparable value → return it
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Max returns the largest element in a slice/array or map,
|
||||
// or the value itself if data is a single comparable value.
|
||||
// Returns an error if the container is empty or the type is unsupported.
|
||||
func Max(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 maxFromIterable(v)
|
||||
case reflect.Map:
|
||||
if v.Len() == 0 {
|
||||
return nil, errors.New("empty map")
|
||||
}
|
||||
return maxFromMap(v)
|
||||
default:
|
||||
if !isOrderedKind(v.Kind()) {
|
||||
return nil, fmt.Errorf("unsupported type: %s", v.Kind())
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
// maxFromIterable scans a slice/array to find the maximum.
|
||||
func maxFromIterable(v reflect.Value) (interface{}, error) {
|
||||
var best reflect.Value
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
elem := v.Index(i)
|
||||
if !isOrderedKind(elem.Kind()) {
|
||||
return nil, fmt.Errorf("unsupported element type: %s", elem.Kind())
|
||||
}
|
||||
if i == 0 || greater(elem, best) {
|
||||
best = elem
|
||||
}
|
||||
}
|
||||
return best.Interface(), nil
|
||||
}
|
||||
|
||||
// minFromIterable finds min in slice or array.
|
||||
func minFromIterable(v reflect.Value) (interface{}, error) {
|
||||
var minVal reflect.Value
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
elem := v.Index(i)
|
||||
if !isOrderedKind(elem.Kind()) {
|
||||
return nil, fmt.Errorf("unsupported element type: %s", elem.Kind())
|
||||
}
|
||||
if i == 0 || less(elem, minVal) {
|
||||
minVal = elem
|
||||
}
|
||||
}
|
||||
return minVal.Interface(), nil
|
||||
}
|
||||
|
||||
// maxFromMap scans map values to find the maximum.
|
||||
func maxFromMap(v reflect.Value) (interface{}, error) {
|
||||
var best reflect.Value
|
||||
first := true
|
||||
for _, key := range v.MapKeys() {
|
||||
elem := v.MapIndex(key)
|
||||
if !isOrderedKind(elem.Kind()) {
|
||||
return nil, fmt.Errorf("unsupported element type: %s", elem.Kind())
|
||||
}
|
||||
if first || greater(elem, best) {
|
||||
best = elem
|
||||
first = false
|
||||
}
|
||||
}
|
||||
return best.Interface(), nil
|
||||
}
|
||||
|
||||
// minFromMap finds min among map values.
|
||||
func minFromMap(v reflect.Value) (interface{}, error) {
|
||||
var minVal reflect.Value
|
||||
first := true
|
||||
for _, key := range v.MapKeys() {
|
||||
elem := v.MapIndex(key)
|
||||
if !isOrderedKind(elem.Kind()) {
|
||||
return nil, fmt.Errorf("unsupported element type: %s", elem.Kind())
|
||||
}
|
||||
if first || less(elem, minVal) {
|
||||
minVal = elem
|
||||
first = false
|
||||
}
|
||||
}
|
||||
return minVal.Interface(), nil
|
||||
}
|
||||
|
||||
// isOrderedKind reports whether k supports comparison ordering.
|
||||
func isOrderedKind(k reflect.Kind) bool {
|
||||
switch k {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Float32, reflect.Float64,
|
||||
reflect.String:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// less returns true if a < b for supported kinds.
|
||||
func less(a, b reflect.Value) bool {
|
||||
switch a.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return a.Int() < b.Int()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return a.Uint() < b.Uint()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return a.Float() < b.Float()
|
||||
case reflect.String:
|
||||
return a.String() < b.String()
|
||||
default:
|
||||
// should never happen if caller checked isOrderedKind
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// greater returns true if a > b for supported kinds.
|
||||
func greater(a, b reflect.Value) bool {
|
||||
switch a.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return a.Int() > b.Int()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return a.Uint() > b.Uint()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return a.Float() > b.Float()
|
||||
case reflect.String:
|
||||
return a.String() > b.String()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user