expr-lang / expr

Expression language and expression evaluation for Go
https://expr-lang.org
MIT License
6.16k stars 394 forks source link

Add uniq() builtin #647

Closed antonmedv closed 4 weeks ago

jippi commented 4 months ago

Not sure if usable or not, but I implemented this recently in my own project (source + docs)

I couldn't figure out how to make the input accept cmp.Ordered instead of specific types :)

and inline below

func UniqSlice[T cmp.Ordered](in []T) []T {
    slices.Sort(in)

    return slices.Compact(in)
}

// Uniq takes a list of strings or interface{}, sorts them
// and remove duplicated values
var Uniq = expr.Function(
    "uniq",
    func(args ...any) (any, error) {
        switch elements := args[0].(type) {
        case []any:
            var result []string

            for _, element := range elements {
                result = append(result, fmt.Sprintf("%s", element))
            }

            return UniqSlice(result), nil

        case []string:
            return UniqSlice(elements), nil

        default:
            return nil, fmt.Errorf("invalid input, must be an array of [string] or [interface], got %T", args[0])
        }
    },
    new(func([]any) []string),    // []any -> []string (when using map() that always return []any)
    new(func([]string) []string), // []string -> []string
)
antonmedv commented 4 months ago

I couldn't figure out how to make the input accept cmp.Ordered instead of specific types :)

This is one of the limitations of our virtual machine. As we use stack of []any, information on type is "lost" (can be retrieved via reflection).

For uniq() builtin to work not only with strings, we need to implement a more general approach, probably implementing in in bytecode or in VM.

Implementing uniq() is much ore tricky 👀

OlgaNovg commented 1 month ago

Could u pls be more concrete and provide at least signature?)

antonmedv commented 4 weeks ago

Fixed in #705