diff --git a/builtin/builtin.go b/builtin/builtin.go index 0107d276..35a9f687 100644 --- a/builtin/builtin.go +++ b/builtin/builtin.go @@ -830,6 +830,57 @@ var Builtins = []*Function{ } }, }, + + { + Name: "uniq", + Func: func(args ...any) (any, error) { + if len(args) != 1 { + return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args)) + } + + v := reflect.ValueOf(deref.Deref(args[0])) + if v.Kind() != reflect.Array && v.Kind() != reflect.Slice { + return nil, fmt.Errorf("cannot uniq %s", v.Kind()) + } + + size := v.Len() + ret := []any{} + + eq := func(i int) bool { + for _, r := range ret { + if runtime.Equal(v.Index(i).Interface(), r) { + return true + } + } + + return false + } + + for i := 0; i < size; i += 1 { + if eq(i) { + continue + } + + ret = append(ret, v.Index(i).Interface()) + } + + return ret, nil + }, + + Validate: func(args []reflect.Type) (reflect.Type, error) { + if len(args) != 1 { + return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args)) + } + + switch kind(args[0]) { + case reflect.Interface, reflect.Slice, reflect.Array: + return arrayType, nil + default: + return anyType, fmt.Errorf("cannot uniq %s", args[0]) + } + }, + }, + { Name: "concat", Safe: func(args ...any) (any, uint, error) { diff --git a/builtin/builtin_test.go b/builtin/builtin_test.go index ddfb4a49..499a838b 100644 --- a/builtin/builtin_test.go +++ b/builtin/builtin_test.go @@ -155,6 +155,8 @@ func TestBuiltin(t *testing.T) { {`flatten([["a", "b"], [1, 2]])`, []any{"a", "b", 1, 2}}, {`flatten([["a", "b"], [1, 2, [3, 4]]])`, []any{"a", "b", 1, 2, 3, 4}}, {`flatten([["a", "b"], [1, 2, [3, [[[["c", "d"], "e"]]], 4]]])`, []any{"a", "b", 1, 2, 3, "c", "d", "e", 4}}, + {`uniq([1, 15, "a", 2, 3, 5, 2, "a", 2, "b"])`, []any{1, 15, "a", 2, 3, 5, "b"}}, + {`uniq([[1, 2], "a", 2, 3, [1, 2], [1, 3]])`, []any{[]any{1, 2}, "a", 2, 3, []any{1, 3}}}, } for _, test := range tests {