Skip to content

Commit

Permalink
Feature/#10 zip (#113)
Browse files Browse the repository at this point in the history
* added prototype of ZIP function

* add unit test for ZIP function

* change comment for ZIP function

* added some new tests

* small change in unit tests

* added ZIP function
  • Loading branch information
3timeslazy authored and ziflex committed Oct 13, 2018
1 parent 446ce3e commit 5d0d9ec
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 0 deletions.
1 change: 1 addition & 0 deletions pkg/stdlib/objects/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ func NewLib() map[string]core.Function {
"KEYS": Keys,
"KEEP": Keep,
"MERGE": Merge,
"ZIP": Zip,
}
}
83 changes: 83 additions & 0 deletions pkg/stdlib/objects/zip.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package objects

import (
"fmt"

"context"

"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)

/*
* 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 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) {
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
}
133 changes: 133 additions & 0 deletions pkg/stdlib/objects/zip_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
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())
expected := values.None

So(err, ShouldBeError)
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(expected), ShouldEqual, 0)

actual, err = objects.Zip(context.Background(), values.NewInt(0))

So(err, ShouldBeError)
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(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(expected), ShouldEqual, 0)

actual, err = objects.Zip(context.Background(), values.NewInt(0), values.NewArray(0))

So(err, ShouldBeError)
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(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(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(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, ShouldBeNil)
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, ShouldBeNil)
So(actual.Compare(expected), ShouldEqual, 0)
})
}

0 comments on commit 5d0d9ec

Please sign in to comment.