mirror of
https://github.com/metabarcoding/obitools4.git
synced 2025-06-29 16:20:46 +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)]++
|
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 {
|
if len(forward_length) > 1 {
|
||||||
others := make([]int, 0)
|
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)
|
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 {
|
if len(reverse_length) > 1 {
|
||||||
others := make([]int, 0)
|
others := make([]int, 0)
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
// corresponds to the last commit, and not the one when the file will be
|
// corresponds to the last commit, and not the one when the file will be
|
||||||
// commited
|
// commited
|
||||||
|
|
||||||
var _Commit = "e065e29"
|
var _Commit = "f1b9ac4"
|
||||||
var _Version = "Release 4.4.0"
|
var _Version = "Release 4.4.0"
|
||||||
|
|
||||||
// Version returns the version of the obitools package.
|
// Version returns the version of the obitools package.
|
||||||
|
@ -135,6 +135,12 @@ var OBILang = gval.NewLanguage(
|
|||||||
length := obiutils.Len(args[0])
|
length := obiutils.Len(args[0])
|
||||||
return (float64)(length), nil
|
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) {
|
gval.Function("contains", func(args ...interface{}) (interface{}, error) {
|
||||||
if obiutils.IsAMap(args[0]) {
|
if obiutils.IsAMap(args[0]) {
|
||||||
val := reflect.ValueOf(args[0]).MapIndex(reflect.ValueOf(args[1]))
|
val := reflect.ValueOf(args[0]).MapIndex(reflect.ValueOf(args[1]))
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package obiutils
|
package obiutils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
log "github.com/sirupsen/logrus"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,19 +34,200 @@ func MinMaxSlice[T constraints.Ordered](vec []T) (min, max T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func MaxMap[K comparable, T constraints.Ordered](values map[K]T) (K, T) {
|
func MaxMap[K comparable, T constraints.Ordered](values map[K]T) (K, T, error) {
|
||||||
|
|
||||||
if len(values) == 0 {
|
|
||||||
log.Panicf("empty map")
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxKey K
|
var maxKey K
|
||||||
var maxValue T
|
var maxValue T
|
||||||
|
|
||||||
|
if len(values) == 0 {
|
||||||
|
return maxKey, maxValue, errors.New("Empty map")
|
||||||
|
}
|
||||||
|
|
||||||
|
first := true
|
||||||
for k, v := range values {
|
for k, v := range values {
|
||||||
if v > maxValue {
|
if v > maxValue || first {
|
||||||
maxValue = v
|
maxValue = v
|
||||||
maxKey = k
|
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