From c4aab84d295203d3aca0a388fa4bd82f8156c9b9 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Oct 2018 17:00:09 +0300 Subject: [PATCH 1/6] added prototype of ZIP function --- pkg/stdlib/objects/zip.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 pkg/stdlib/objects/zip.go diff --git a/pkg/stdlib/objects/zip.go b/pkg/stdlib/objects/zip.go new file mode 100644 index 00000000..580f1754 --- /dev/null +++ b/pkg/stdlib/objects/zip.go @@ -0,0 +1,18 @@ +package objects + +import ( + "context" + + "github.com/MontFerret/ferret/pkg/runtime/core" +) + +/* + * Returns an object assembled from the separate parameters keys and values. + * Keys and values must be arrays and have the same length. + * @params keys (Array of Objects) - result object keys. + * @params values (Array of Objects) - result object values. + * @returns (Object) - Object assembled from the separate parameters keys and values. + */ +func Zip(_ context.Context, args ...core.Value) (core.Value, error) { + return nil, nil +} From a01b2763f608cce11d9b2ca7875c690de6cb1d57 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Oct 2018 17:43:22 +0300 Subject: [PATCH 2/6] add unit test for ZIP function --- pkg/stdlib/objects/zip_test.go | 84 ++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 pkg/stdlib/objects/zip_test.go diff --git a/pkg/stdlib/objects/zip_test.go b/pkg/stdlib/objects/zip_test.go new file mode 100644 index 00000000..4afdbccb --- /dev/null +++ b/pkg/stdlib/objects/zip_test.go @@ -0,0 +1,84 @@ +package objects_test + +import ( + "context" + "testing" + + "github.com/MontFerret/ferret/pkg/runtime/values" + "github.com/MontFerret/ferret/pkg/stdlib/objects" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestZip(t *testing.T) { + Convey("Invalid arguments", t, func() { + Convey("When there are no arguments", func() { + actual, err := objects.Zip(context.Background()) + + So(err, ShouldBeError) + So(actual.Compare(values.None), ShouldEqual, 0) + }) + + Convey("When single argument", func() { + actual, err := objects.Zip(context.Background(), values.NewArray(0)) + + So(err, ShouldBeError) + So(actual.Compare(values.None), ShouldEqual, 0) + + actual, err = objects.Zip(context.Background(), values.NewInt(0)) + + So(err, ShouldBeError) + So(actual.Compare(values.None), ShouldEqual, 0) + }) + + Convey("When too many arguments", func() { + actual, err := objects.Zip(context.Background(), + values.NewArray(0), values.NewArray(0), values.NewArray(0)) + + So(err, ShouldBeError) + So(actual.Compare(values.None), ShouldEqual, 0) + }) + + Convey("When there is not array argument", func() { + actual, err := objects.Zip(context.Background(), values.NewArray(0), values.NewInt(0)) + + So(err, ShouldBeError) + So(actual.Compare(values.None), ShouldEqual, 0) + + actual, err = objects.Zip(context.Background(), values.NewInt(0), values.NewArray(0)) + + So(err, ShouldBeError) + So(actual.Compare(values.None), ShouldEqual, 0) + }) + + Convey("When there is not string element into keys array", func() { + keys := values.NewArrayWith(values.NewInt(0)) + vals := values.NewArrayWith(values.NewString("v1")) + + actual, err := objects.Zip(context.Background(), keys, vals) + + So(err, ShouldBeError) + So(actual.Compare(values.None), ShouldEqual, 0) + }) + + Convey("When 1 key and 0 values", func() { + keys := values.NewArrayWith(values.NewString("k1")) + vals := values.NewArray(0) + + actual, err := objects.Zip(context.Background(), keys, vals) + + So(err, ShouldBeError) + So(actual.Compare(values.None), ShouldEqual, 0) + }) + + Convey("When 0 keys and 1 values", func() { + keys := values.NewArray(0) + vals := values.NewArrayWith(values.NewString("v1")) + + actual, err := objects.Zip(context.Background(), keys, vals) + + So(err, ShouldBeError) + So(actual.Compare(values.None), ShouldEqual, 0) + }) + }) +} From fcb2a7d66d39696fd82ba1de943c6dc6c7a20a5e Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Oct 2018 17:43:52 +0300 Subject: [PATCH 3/6] change comment for ZIP function --- pkg/stdlib/objects/zip.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/stdlib/objects/zip.go b/pkg/stdlib/objects/zip.go index 580f1754..7f0f3f10 100644 --- a/pkg/stdlib/objects/zip.go +++ b/pkg/stdlib/objects/zip.go @@ -9,9 +9,9 @@ import ( /* * Returns an object assembled from the separate parameters keys and values. * Keys and values must be arrays and have the same length. - * @params keys (Array of Objects) - result object keys. - * @params values (Array of Objects) - result object values. - * @returns (Object) - Object assembled from the separate parameters keys and values. + * @params keys (Array of Strings) - an array of strings, to be used as key names in the result. + * @params values (Array of Objects) - an array of core.Value, to be used as key values. + * @returns (Object) - an object with the keys and values assembled. */ func Zip(_ context.Context, args ...core.Value) (core.Value, error) { return nil, nil From 1a4d99dfb96d4832af24c5f1c458b4a57c4bac63 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Oct 2018 19:05:45 +0300 Subject: [PATCH 4/6] added some new tests --- pkg/stdlib/objects/zip_test.go | 67 +++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/pkg/stdlib/objects/zip_test.go b/pkg/stdlib/objects/zip_test.go index 4afdbccb..c328ce5b 100644 --- a/pkg/stdlib/objects/zip_test.go +++ b/pkg/stdlib/objects/zip_test.go @@ -14,71 +14,120 @@ func TestZip(t *testing.T) { Convey("Invalid arguments", t, func() { Convey("When there are no arguments", func() { actual, err := objects.Zip(context.Background()) + expected := values.None So(err, ShouldBeError) - So(actual.Compare(values.None), ShouldEqual, 0) + So(actual.Compare(expected), ShouldEqual, 0) }) Convey("When single argument", func() { actual, err := objects.Zip(context.Background(), values.NewArray(0)) + expected := values.None So(err, ShouldBeError) - So(actual.Compare(values.None), ShouldEqual, 0) + So(actual.Compare(expected), ShouldEqual, 0) actual, err = objects.Zip(context.Background(), values.NewInt(0)) So(err, ShouldBeError) - So(actual.Compare(values.None), ShouldEqual, 0) + So(actual.Compare(expected), ShouldEqual, 0) }) Convey("When too many arguments", func() { actual, err := objects.Zip(context.Background(), values.NewArray(0), values.NewArray(0), values.NewArray(0)) + expected := values.None So(err, ShouldBeError) - So(actual.Compare(values.None), ShouldEqual, 0) + So(actual.Compare(expected), ShouldEqual, 0) }) Convey("When there is not array argument", func() { actual, err := objects.Zip(context.Background(), values.NewArray(0), values.NewInt(0)) + expected := values.None So(err, ShouldBeError) - So(actual.Compare(values.None), ShouldEqual, 0) + So(actual.Compare(expected), ShouldEqual, 0) actual, err = objects.Zip(context.Background(), values.NewInt(0), values.NewArray(0)) So(err, ShouldBeError) - So(actual.Compare(values.None), ShouldEqual, 0) + So(actual.Compare(expected), ShouldEqual, 0) }) Convey("When there is not string element into keys array", func() { keys := values.NewArrayWith(values.NewInt(0)) vals := values.NewArrayWith(values.NewString("v1")) + expected := values.None actual, err := objects.Zip(context.Background(), keys, vals) So(err, ShouldBeError) - So(actual.Compare(values.None), ShouldEqual, 0) + So(actual.Compare(expected), ShouldEqual, 0) }) Convey("When 1 key and 0 values", func() { keys := values.NewArrayWith(values.NewString("k1")) vals := values.NewArray(0) + expected := values.None actual, err := objects.Zip(context.Background(), keys, vals) So(err, ShouldBeError) - So(actual.Compare(values.None), ShouldEqual, 0) + So(actual.Compare(expected), ShouldEqual, 0) }) Convey("When 0 keys and 1 values", func() { keys := values.NewArray(0) vals := values.NewArrayWith(values.NewString("v1")) + expected := values.None actual, err := objects.Zip(context.Background(), keys, vals) So(err, ShouldBeError) - So(actual.Compare(values.None), ShouldEqual, 0) + So(actual.Compare(expected), ShouldEqual, 0) }) }) + + Convey("Zip 2 keys and 2 values", t, func() { + keys := values.NewArrayWith( + values.NewString("k1"), + values.NewString("k2"), + ) + vals := values.NewArrayWith( + values.NewString("v1"), + values.NewInt(2), + ) + expected := values.NewObjectWith( + values.NewObjectProperty("k1", values.NewString("v1")), + values.NewObjectProperty("k2", values.NewInt(2)), + ) + + actual, err := objects.Zip(context.Background(), keys, vals) + + So(err, ShouldBeError) + So(actual.Compare(expected), ShouldEqual, 0) + }) + + Convey("Zip 3 keys and 3 values. 1 key repeats", t, func() { + keys := values.NewArrayWith( + values.NewString("k1"), + values.NewString("k2"), + values.NewString("k1"), + ) + vals := values.NewArrayWith( + values.NewInt(1), + values.NewInt(2), + values.NewInt(3), + ) + expected := values.NewObjectWith( + values.NewObjectProperty("k1", values.NewInt(1)), + values.NewObjectProperty("k2", values.NewInt(2)), + ) + + actual, err := objects.Zip(context.Background(), keys, vals) + + So(err, ShouldBeError) + So(actual.Compare(expected), ShouldEqual, 0) + }) } From 13c14010d22c0d2495b08b73187f6ec7fa4bd9cb Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Oct 2018 20:17:44 +0300 Subject: [PATCH 5/6] small change in unit tests --- pkg/stdlib/objects/zip_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/stdlib/objects/zip_test.go b/pkg/stdlib/objects/zip_test.go index c328ce5b..f2088d98 100644 --- a/pkg/stdlib/objects/zip_test.go +++ b/pkg/stdlib/objects/zip_test.go @@ -105,7 +105,7 @@ func TestZip(t *testing.T) { actual, err := objects.Zip(context.Background(), keys, vals) - So(err, ShouldBeError) + So(err, ShouldBeNil) So(actual.Compare(expected), ShouldEqual, 0) }) @@ -127,7 +127,7 @@ func TestZip(t *testing.T) { actual, err := objects.Zip(context.Background(), keys, vals) - So(err, ShouldBeError) + So(err, ShouldBeNil) So(actual.Compare(expected), ShouldEqual, 0) }) } From 9bf8e1cd60220ae35c9d7634b3b0c60135b9af72 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 13 Oct 2018 20:18:30 +0300 Subject: [PATCH 6/6] added ZIP function --- pkg/stdlib/objects/lib.go | 1 + pkg/stdlib/objects/zip.go | 67 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/pkg/stdlib/objects/lib.go b/pkg/stdlib/objects/lib.go index 850ad6bd..b03f27e9 100644 --- a/pkg/stdlib/objects/lib.go +++ b/pkg/stdlib/objects/lib.go @@ -8,5 +8,6 @@ func NewLib() map[string]core.Function { "KEYS": Keys, "KEEP": Keep, "MERGE": Merge, + "ZIP": Zip, } } diff --git a/pkg/stdlib/objects/zip.go b/pkg/stdlib/objects/zip.go index 7f0f3f10..8918e3d9 100644 --- a/pkg/stdlib/objects/zip.go +++ b/pkg/stdlib/objects/zip.go @@ -1,9 +1,12 @@ package objects import ( + "fmt" + "context" "github.com/MontFerret/ferret/pkg/runtime/core" + "github.com/MontFerret/ferret/pkg/runtime/values" ) /* @@ -14,5 +17,67 @@ import ( * @returns (Object) - an object with the keys and values assembled. */ func Zip(_ context.Context, args ...core.Value) (core.Value, error) { - return nil, nil + err := core.ValidateArgs(args, 2, 2) + if err != nil { + return values.None, err + } + + for _, arg := range args { + if err = core.ValidateType(arg, core.ArrayType); err != nil { + return values.None, err + } + } + + keys := args[0].(*values.Array) + vals := args[1].(*values.Array) + + if keys.Length() != vals.Length() { + return values.None, core.Error( + core.ErrInvalidArgument, + fmt.Sprintf("keys and values must have the same length. got keys: %d, values: %d", + keys.Length(), vals.Length(), + ), + ) + } + + err = validateArrayOf(core.StringType, keys) + if err != nil { + return values.None, err + } + + zipped := values.NewObject() + + var k values.String + var val core.Value + var exists bool + keyExists := map[values.String]bool{} + + keys.ForEach(func(key core.Value, idx int) bool { + k = key.(values.String) + + // this is necessary to impelement ArangoDB's behavior. + // in ArangoDB the first value in values is + // associated with each key. Ex.: + // -- query -- + // > RETURN ZIP( + // > ["a", "b", "a"], [1, 2, 3] + // > ) + // -- result -- + // > [{"a": 1,"b": 2}] + if _, exists = keyExists[k]; exists { + return true + } + keyExists[k] = true + + val = vals.Get(values.NewInt(idx)) + + if values.IsCloneable(val) { + val = val.(core.Cloneable).Clone() + } + + zipped.Set(k, val) + return true + }) + + return zipped, nil }