From 7e01fcc8a106308a9679e9d3fc2263a8041de435 Mon Sep 17 00:00:00 2001 From: Jakub Martin Date: Sun, 9 Jan 2022 13:56:53 +0100 Subject: [PATCH] Add zip function and relevant documentation. --- .goreleaser.yml | 8 +------- README.md | 29 ++++++++++++++++++++++++++ jql/functions/functions.go | 42 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 7 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index fcae95a..6839d0c 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,7 +1,6 @@ # Make sure to check the documentation at http://goreleaser.com before: hooks: - # you may remove this if you don't use vgo - go mod tidy builds: - binary: jql @@ -12,14 +11,9 @@ builds: - windows goarch: - amd64 + - arm64 env: - CGO_ENABLED=0 -archives: -- replacements: - darwin: Darwin - linux: Linux - windows: Windows - amd64: x86_64 checksum: name_template: 'checksums.txt' snapshot: diff --git a/README.md b/README.md index 8841a30..bb8dd34 100644 --- a/README.md +++ b/README.md @@ -263,6 +263,34 @@ You can also use _object_ to create objects, with arguments alternating keys and } ``` +#### zip + +Occasionally you might want to combine multiple arrays, you can use _zip_ to do that. For example, to create a list of key-value entries from an object, you can do the following: +``` +> cat test.json | jql '("countries" + (0 (zip + (keys) + ((keys)))))' +[ + [ + "eu_since", + "2004" + ], + [ + "european", + true + ], + [ + "name", + "Poland" + ], + [ + "population", + 38000000 + ] +] +``` + Now we're done with the **core** functionality of jql. The stuff so far will probably suffice for most use cases and even very complex data structures. However, here come more functions: @@ -515,6 +543,7 @@ eq,lt,gt: (Expression x Expression) -> (Expression[Bool]) range: With one arg: (Expression[Int]) -> (Expression[Array[Int]]) With two args: (Expression[Int] x Expression[Int]) -> (Expression[Array[Int]]) +zip: (Expression[Array[A]], Expression[Array[B]], ...) -> (Expression[Array[Array[A | B | ...]]]) and,or: (Expression[Bool]...) -> (Expression[Bool]) not: (Expression[Bool]) -> (Expression[Bool]) ifte: (Expression[Bool] x Expression[A] x Expression[B]) -> (Expression[A|B]) diff --git a/jql/functions/functions.go b/jql/functions/functions.go index ac67546..784aa6f 100644 --- a/jql/functions/functions.go +++ b/jql/functions/functions.go @@ -4,6 +4,7 @@ import ( "fmt" "reflect" "runtime/debug" + "sort" "strings" "github.com/cube2222/jql/jql" @@ -29,6 +30,7 @@ var Functions = map[string]func(ts ...jql.Expression) (jql.Expression, error){ "ifte": NewIfTE, "error": NewError, "recover": NewRecover, + "zip": NewZip, } type Element struct { @@ -152,6 +154,9 @@ func (s Keys) Get(arg interface{}) (interface{}, error) { for field := range typed { outFields = append(outFields, field) } + sort.Slice(outFields, func(i, j int) bool { + return outFields[i].(string) < outFields[j].(string) + }) return outFields, nil @@ -738,3 +743,40 @@ func (t Recover) Get(arg interface{}) (out interface{}, err error) { return value, nil } + +type Zip struct { + Arguments []jql.Expression +} + +func NewZip(ts ...jql.Expression) (jql.Expression, error) { + return Zip{Arguments: ts}, nil +} + +func (t Zip) Get(arg interface{}) (interface{}, error) { + args := make([][]interface{}, len(t.Arguments)) + for i, curArg := range t.Arguments { + curArgValue, err := curArg.Get(arg) + if err != nil { + return nil, fmt.Errorf("couldn't evaluate argument with index %d: %w", i, err) + } + + curArgValueTyped, ok := curArgValue.([]interface{}) + if !ok { + return nil, fmt.Errorf("zip expects arrays as arguments, received %v of type %s", curArgValue, reflect.TypeOf(curArgValue)) + } + args[i] = curArgValueTyped + } + + var out []interface{} + + for i := 0; ; i++ { + curOut := make([]interface{}, len(args)) + for j := range args { + if i >= len(args[j]) { + return out, nil + } + curOut[j] = args[j][i] + } + out = append(out, curOut) + } +}