expr-lang/expr

Add uniq() builtin

Opened this issue · 2 comments

Add uniq() builtin
jippi commented

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
)

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 👀