Skip to content

Commit 418fed5

Browse files
committed
Add toPairs() and fromPairs()
1 parent 323f23b commit 418fed5

File tree

3 files changed

+69
-1
lines changed

3 files changed

+69
-1
lines changed

builtin/builtin.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,70 @@ var Builtins = []*ast.Function{
584584
return anyType, fmt.Errorf("cannot get values from %s", args[0])
585585
},
586586
},
587+
{
588+
Name: "toPairs",
589+
Func: func(args ...any) (any, error) {
590+
if len(args) != 1 {
591+
return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
592+
}
593+
v := reflect.ValueOf(args[0])
594+
if v.Kind() != reflect.Map {
595+
return nil, fmt.Errorf("cannot transform %s to pairs", v.Kind())
596+
}
597+
keys := v.MapKeys()
598+
out := make([][2]any, len(keys))
599+
for i, key := range keys {
600+
out[i] = [2]any{key.Interface(), v.MapIndex(key).Interface()}
601+
}
602+
return out, nil
603+
},
604+
Validate: func(args []reflect.Type) (reflect.Type, error) {
605+
if len(args) != 1 {
606+
return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
607+
}
608+
switch kind(args[0]) {
609+
case reflect.Interface, reflect.Map:
610+
return arrayType, nil
611+
}
612+
return anyType, fmt.Errorf("cannot transform %s to pairs", args[0])
613+
},
614+
},
615+
{
616+
Name: "fromPairs",
617+
Func: func(args ...any) (any, error) {
618+
if len(args) != 1 {
619+
return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
620+
}
621+
v := reflect.ValueOf(args[0])
622+
if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
623+
return nil, fmt.Errorf("cannot transform %s from pairs", v)
624+
}
625+
out := reflect.MakeMap(mapType)
626+
for i := 0; i < v.Len(); i++ {
627+
pair := deref(v.Index(i))
628+
if pair.Kind() != reflect.Array && pair.Kind() != reflect.Slice {
629+
return nil, fmt.Errorf("invalid pair %v", pair)
630+
}
631+
if pair.Len() != 2 {
632+
return nil, fmt.Errorf("invalid pair length %v", pair)
633+
}
634+
key := pair.Index(0)
635+
value := pair.Index(1)
636+
out.SetMapIndex(key, value)
637+
}
638+
return out.Interface(), nil
639+
},
640+
Validate: func(args []reflect.Type) (reflect.Type, error) {
641+
if len(args) != 1 {
642+
return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
643+
}
644+
switch kind(args[0]) {
645+
case reflect.Interface, reflect.Slice, reflect.Array:
646+
return mapType, nil
647+
}
648+
return anyType, fmt.Errorf("cannot transform %s from pairs", args[0])
649+
},
650+
},
587651
{
588652
Name: "sort",
589653
Func: func(args ...any) (any, error) {

builtin/builtin_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ func TestBuiltin(t *testing.T) {
9494
{`get({foo: 1, bar: 2}, "unknown")`, nil},
9595
{`"foo" in keys({foo: 1, bar: 2})`, true},
9696
{`1 in values({foo: 1, bar: 2})`, true},
97+
{`len(toPairs({foo: 1, bar: 2}))`, 2},
98+
{`len(toPairs({}))`, 0},
99+
{`fromPairs([["foo", 1], ["bar", 2]])`, map[any]any{"foo": 1, "bar": 2}},
100+
{`fromPairs(toPairs({foo: 1, bar: 2}))`, map[any]any{"foo": 1, "bar": 2}},
97101
{`groupBy(1..9, # % 2)`, map[any][]any{0: {2, 4, 6, 8}, 1: {1, 3, 5, 7, 9}}},
98102
{`groupBy(1..9, # % 2)[0]`, []any{2, 4, 6, 8}},
99103
{`groupBy(1..3, # > 1)[true]`, []any{2, 3}},

builtin/utils.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ var (
99
anyType = reflect.TypeOf(new(any)).Elem()
1010
integerType = reflect.TypeOf(0)
1111
floatType = reflect.TypeOf(float64(0))
12-
stringType = reflect.TypeOf("")
1312
arrayType = reflect.TypeOf([]any{})
13+
mapType = reflect.TypeOf(map[any]any{})
1414
)
1515

1616
func kind(t reflect.Type) reflect.Kind {

0 commit comments

Comments
 (0)