{"action":"created","comment":{"url":"https://api.github.com/repos/jmespath/jmespath.go/pulls/comments/36019005","id":36019005,"diff_hunk":"@@ -0,0 +1,818 @@\n+package jmespath\n+\n+import (\n+\t\"encoding/json\"\n+\t\"errors\"\n+\t\"fmt\"\n+\t\"math\"\n+\t\"sort\"\n+\t\"strconv\"\n+\t\"strings\"\n+\n+\t\"github.com/jmespath/jmespath.go/jmespath/jputil\"\n+)\n+\n+type jpFunction func(arguments []interface{}) (interface{}, error)\n+\n+type jpType string\n+\n+const (\n+\tjpUnknown     jpType = \"unknown\"\n+\tjpNumber      jpType = \"number\"\n+\tjpString      jpType = \"string\"\n+\tjpArray       jpType = \"array\"\n+\tjpObject      jpType = \"object\"\n+\tjpArrayNumber jpType = \"array[number]\"\n+\tjpArrayString jpType = \"array[string]\"\n+\tjpExpref      jpType = \"expref\"\n+\tjpAny         jpType = \"any\"\n+)\n+\n+type functionEntry struct {\n+\tname      string\n+\targuments []argSpec\n+\thandler   jpFunction\n+\thasExpRef bool\n+}\n+\n+type argSpec struct {\n+\ttypes    []jpType\n+\tvariadic bool\n+}\n+\n+type byExprString struct {\n+\tintr     *treeInterpreter\n+\tnode     ASTNode\n+\titems    []interface{}\n+\thasError bool\n+}\n+\n+func (a *byExprString) Len() int {\n+\treturn len(a.items)\n+}\n+func (a *byExprString) Swap(i, j int) {\n+\ta.items[i], a.items[j] = a.items[j], a.items[i]\n+}\n+func (a *byExprString) Less(i, j int) bool {\n+\tfirst, err := a.intr.Execute(a.node, a.items[i])\n+\tif err != nil {\n+\t\ta.hasError = true\n+\t\t// Return a dummy value.\n+\t\treturn true\n+\t}\n+\tith, ok := first.(string)\n+\tif !ok {\n+\t\ta.hasError = true\n+\t\treturn true\n+\t}\n+\tsecond, err := a.intr.Execute(a.node, a.items[j])\n+\tif err != nil {\n+\t\ta.hasError = true\n+\t\t// Return a dummy value.\n+\t\treturn true\n+\t}\n+\tjth, ok := second.(string)\n+\tif !ok {\n+\t\ta.hasError = true\n+\t\treturn true\n+\t}\n+\treturn ith < jth\n+}\n+\n+type byExprFloat struct {\n+\tintr     *treeInterpreter\n+\tnode     ASTNode\n+\titems    []interface{}\n+\thasError bool\n+}\n+\n+func (a *byExprFloat) Len() int {\n+\treturn len(a.items)\n+}\n+func (a *byExprFloat) Swap(i, j int) {\n+\ta.items[i], a.items[j] = a.items[j], a.items[i]\n+}\n+func (a *byExprFloat) Less(i, j int) bool {\n+\tfirst, err := a.intr.Execute(a.node, a.items[i])\n+\tif err != nil {\n+\t\ta.hasError = true\n+\t\t// Return a dummy value.\n+\t\treturn true\n+\t}\n+\tith, ok := first.(float64)\n+\tif !ok {\n+\t\ta.hasError = true\n+\t\treturn true\n+\t}\n+\tsecond, err := a.intr.Execute(a.node, a.items[j])\n+\tif err != nil {\n+\t\ta.hasError = true\n+\t\t// Return a dummy value.\n+\t\treturn true\n+\t}\n+\tjth, ok := second.(float64)\n+\tif !ok {\n+\t\ta.hasError = true\n+\t\treturn true\n+\t}\n+\treturn ith < jth\n+}\n+\n+type functionCaller struct {\n+\tfunctionTable map[string]functionEntry\n+}\n+\n+func newFunctionCaller() *functionCaller {\n+\tcaller := &functionCaller{}\n+\tcaller.functionTable = map[string]functionEntry{\n+\t\t\"length\": functionEntry{\n+\t\t\tname: \"length\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpString, jpArray, jpObject}},\n+\t\t\t},\n+\t\t\thandler: jpfLength,\n+\t\t},\n+\t\t\"starts_with\": functionEntry{\n+\t\t\tname: \"starts_with\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpString}},\n+\t\t\t\targSpec{types: []jpType{jpString}},\n+\t\t\t},\n+\t\t\thandler: jpfStartsWith,\n+\t\t},\n+\t\t\"abs\": functionEntry{\n+\t\t\tname: \"abs\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpNumber}},\n+\t\t\t},\n+\t\t\thandler: jpfAbs,\n+\t\t},\n+\t\t\"avg\": functionEntry{\n+\t\t\tname: \"avg\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpArrayNumber}},\n+\t\t\t},\n+\t\t\thandler: jpfAvg,\n+\t\t},\n+\t\t\"ceil\": functionEntry{\n+\t\t\tname: \"ceil\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpNumber}},\n+\t\t\t},\n+\t\t\thandler: jpfCeil,\n+\t\t},\n+\t\t\"contains\": functionEntry{\n+\t\t\tname: \"contains\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpArray, jpString}},\n+\t\t\t\targSpec{types: []jpType{jpAny}},\n+\t\t\t},\n+\t\t\thandler: jpfContains,\n+\t\t},\n+\t\t\"ends_with\": functionEntry{\n+\t\t\tname: \"ends_with\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpString}},\n+\t\t\t\targSpec{types: []jpType{jpString}},\n+\t\t\t},\n+\t\t\thandler: jpfEndsWith,\n+\t\t},\n+\t\t\"floor\": functionEntry{\n+\t\t\tname: \"floor\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpNumber}},\n+\t\t\t},\n+\t\t\thandler: jpfFloor,\n+\t\t},\n+\t\t\"max\": functionEntry{\n+\t\t\tname: \"max\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpArrayNumber, jpArrayString}},\n+\t\t\t},\n+\t\t\thandler: jpfMax,\n+\t\t},\n+\t\t\"merge\": functionEntry{\n+\t\t\tname: \"merge\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpObject}, variadic: true},\n+\t\t\t},\n+\t\t\thandler: jpfMerge,\n+\t\t},\n+\t\t\"max_by\": functionEntry{\n+\t\t\tname: \"max_by\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpArray}},\n+\t\t\t\targSpec{types: []jpType{jpExpref}},\n+\t\t\t},\n+\t\t\thandler:   jpfMaxBy,\n+\t\t\thasExpRef: true,\n+\t\t},\n+\t\t\"sum\": functionEntry{\n+\t\t\tname: \"sum\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpArrayNumber}},\n+\t\t\t},\n+\t\t\thandler: jpfSum,\n+\t\t},\n+\t\t\"min\": functionEntry{\n+\t\t\tname: \"min\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpArrayNumber, jpArrayString}},\n+\t\t\t},\n+\t\t\thandler: jpfMin,\n+\t\t},\n+\t\t\"min_by\": functionEntry{\n+\t\t\tname: \"min_by\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpArray}},\n+\t\t\t\targSpec{types: []jpType{jpExpref}},\n+\t\t\t},\n+\t\t\thandler:   jpfMinBy,\n+\t\t\thasExpRef: true,\n+\t\t},\n+\t\t\"type\": functionEntry{\n+\t\t\tname: \"type\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpAny}},\n+\t\t\t},\n+\t\t\thandler: jpfType,\n+\t\t},\n+\t\t\"keys\": functionEntry{\n+\t\t\tname: \"keys\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpObject}},\n+\t\t\t},\n+\t\t\thandler: jpfKeys,\n+\t\t},\n+\t\t\"values\": functionEntry{\n+\t\t\tname: \"values\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpObject}},\n+\t\t\t},\n+\t\t\thandler: jpfValues,\n+\t\t},\n+\t\t\"sort\": functionEntry{\n+\t\t\tname: \"sort\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpArrayString, jpArrayNumber}},\n+\t\t\t},\n+\t\t\thandler: jpfSort,\n+\t\t},\n+\t\t\"sort_by\": functionEntry{\n+\t\t\tname: \"sort_by\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpArray}},\n+\t\t\t\targSpec{types: []jpType{jpExpref}},\n+\t\t\t},\n+\t\t\thandler:   jpfSortBy,\n+\t\t\thasExpRef: true,\n+\t\t},\n+\t\t\"join\": functionEntry{\n+\t\t\tname: \"join\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpString}},\n+\t\t\t\targSpec{types: []jpType{jpArrayString}},\n+\t\t\t},\n+\t\t\thandler: jpfJoin,\n+\t\t},\n+\t\t\"reverse\": functionEntry{\n+\t\t\tname: \"reverse\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpArray, jpString}},\n+\t\t\t},\n+\t\t\thandler: jpfReverse,\n+\t\t},\n+\t\t\"to_array\": functionEntry{\n+\t\t\tname: \"to_array\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpAny}},\n+\t\t\t},\n+\t\t\thandler: jpfToArray,\n+\t\t},\n+\t\t\"to_string\": functionEntry{\n+\t\t\tname: \"to_string\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpAny}},\n+\t\t\t},\n+\t\t\thandler: jpfToString,\n+\t\t},\n+\t\t\"to_number\": functionEntry{\n+\t\t\tname: \"to_number\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpAny}},\n+\t\t\t},\n+\t\t\thandler: jpfToNumber,\n+\t\t},\n+\t\t\"not_null\": functionEntry{\n+\t\t\tname: \"not_null\",\n+\t\t\targuments: []argSpec{\n+\t\t\t\targSpec{types: []jpType{jpAny}, variadic: true},\n+\t\t\t},\n+\t\t\thandler: jpfNotNull,\n+\t\t},\n+\t}\n+\treturn caller\n+}\n+\n+func (e *functionEntry) resolveArgs(arguments []interface{}) ([]interface{}, error) {\n+\tif len(e.arguments) == 0 {\n+\t\treturn arguments, nil\n+\t}\n+\tif !e.arguments[len(e.arguments)-1].variadic {\n+\t\tif len(e.arguments) != len(arguments) {\n+\t\t\treturn nil, errors.New(\"Incorrect number of args.\")\n+\t\t}\n+\t\tfor i, spec := range e.arguments {\n+\t\t\tuserArg := arguments[i]\n+\t\t\terr := spec.typeCheck(userArg)\n+\t\t\tif err != nil {\n+\t\t\t\treturn nil, err\n+\t\t\t}\n+\t\t}\n+\t\treturn arguments, nil\n+\t}\n+\tif len(arguments) < len(e.arguments) {\n+\t\treturn nil, errors.New(\"Invalid arity.\")\n+\t}\n+\treturn arguments, nil\n+}\n+\n+func (a *argSpec) typeCheck(arg interface{}) error {\n+\tfor _, t := range a.types {\n+\t\tswitch t {\n+\t\tcase jpNumber:\n+\t\t\tif _, ok := arg.(float64); ok {\n+\t\t\t\treturn nil\n+\t\t\t}\n+\t\tcase jpString:\n+\t\t\tif _, ok := arg.(string); ok {\n+\t\t\t\treturn nil\n+\t\t\t}\n+\t\tcase jpArray:\n+\t\t\tif _, ok := arg.([]interface{}); ok {\n+\t\t\t\treturn nil\n+\t\t\t}\n+\t\tcase jpObject:\n+\t\t\tif _, ok := arg.(map[string]interface{}); ok {\n+\t\t\t\treturn nil\n+\t\t\t}\n+\t\tcase jpArrayNumber:\n+\t\t\tif _, ok := jputil.ToArrayNum(arg); ok {\n+\t\t\t\treturn nil\n+\t\t\t}\n+\t\tcase jpArrayString:\n+\t\t\tif _, ok := jputil.ToArrayStr(arg); ok {\n+\t\t\t\treturn nil\n+\t\t\t}\n+\t\tcase jpAny:\n+\t\t\treturn nil\n+\t\tcase jpExpref:\n+\t\t\tif _, ok := arg.(expRef); ok {\n+\t\t\t\treturn nil\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn fmt.Errorf(\"Invalid type for: %v, expected: %#v\", arg, a.types)\n+}\n+\n+func (f *functionCaller) CallFunction(name string, arguments []interface{}, intr *treeInterpreter) (interface{}, error) {\n+\tentry, ok := f.functionTable[name]\n+\tif !ok {\n+\t\treturn nil, errors.New(\"Unknown function: \" + name)\n+\t}\n+\tresolvedArgs, err := entry.resolveArgs(arguments)\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\tif entry.hasExpRef {\n+\t\tvar extra []interface{}\n+\t\textra = append(extra, intr)\n+\t\tresolvedArgs = append(extra, resolvedArgs...)\n+\t}\n+\treturn entry.handler(resolvedArgs)\n+}\n+\n+func jpfAbs(arguments []interface{}) (interface{}, error) {\n+\tnum := arguments[0].(float64)\n+\treturn math.Abs(num), nil\n+}\n+\n+func jpfLength(arguments []interface{}) (interface{}, error) {\n+\targ := arguments[0]\n+\tif c, ok := arg.(string); ok {\n+\t\treturn float64(len(c)), nil\n+\t} else if c, ok := arg.([]interface{}); ok {\n+\t\treturn float64(len(c)), nil\n+\t} else if c, ok := arg.(map[string]interface{}); ok {\n+\t\treturn float64(len(c)), nil\n+\t}\n+\treturn nil, errors.New(\"Could not compute length().\")\n+}\n+\n+func jpfStartsWith(arguments []interface{}) (interface{}, error) {\n+\tsearch := arguments[0].(string)\n+\tprefix := arguments[1].(string)\n+\treturn strings.HasPrefix(search, prefix), nil\n+}\n+\n+func jpfAvg(arguments []interface{}) (interface{}, error) {\n+\t// We've already type checked the value so we can safely use\n+\t// type assertions.\n+\targs := arguments[0].([]interface{})\n+\tlength := float64(len(args))\n+\tnumerator := 0.0\n+\tfor _, n := range args {\n+\t\tnumerator += n.(float64)\n+\t}\n+\treturn numerator / length, nil\n+}\n+func jpfCeil(arguments []interface{}) (interface{}, error) {\n+\tval := arguments[0].(float64)\n+\treturn math.Ceil(val), nil\n+}\n+func jpfContains(arguments []interface{}) (interface{}, error) {\n+\tsearch := arguments[0]\n+\tel := arguments[1]\n+\tif searchStr, ok := search.(string); ok {\n+\t\tif elStr, ok := el.(string); ok {\n+\t\t\treturn strings.Index(searchStr, elStr) != -1, nil\n+\t\t}\n+\t\treturn false, nil\n+\t}\n+\t// Otherwise this is a generic contains for []interface{}\n+\tgeneral := search.([]interface{})\n+\tfor _, item := range general {\n+\t\tif item == el {\n+\t\t\treturn true, nil\n+\t\t}\n+\t}\n+\treturn false, nil\n+}\n+func jpfEndsWith(arguments []interface{}) (interface{}, error) {\n+\tsearch := arguments[0].(string)\n+\tsuffix := arguments[1].(string)\n+\treturn strings.HasSuffix(search, suffix), nil\n+}\n+func jpfFloor(arguments []interface{}) (interface{}, error) {\n+\tval := arguments[0].(float64)\n+\treturn math.Floor(val), nil\n+}\n+func jpfMax(arguments []interface{}) (interface{}, error) {\n+\tif items, ok := jputil.ToArrayNum(arguments[0]); ok {\n+\t\tif len(items) == 0 {\n+\t\t\treturn nil, nil\n+\t\t}\n+\t\tif len(items) == 1 {\n+\t\t\treturn items[0], nil\n+\t\t}\n+\t\tbest := items[0]\n+\t\tfor _, item := range items[1:] {\n+\t\t\tif item > best {\n+\t\t\t\tbest = item\n+\t\t\t}\n+\t\t}\n+\t\treturn best, nil\n+\t}\n+\t// Otherwise we're dealing with a max() of strings.\n+\titems, _ := jputil.ToArrayStr(arguments[0])\n+\tif len(items) == 0 {\n+\t\treturn nil, nil\n+\t}\n+\tif len(items) == 1 {\n+\t\treturn items[0], nil\n+\t}\n+\tbest := items[0]\n+\tfor _, item := range items[1:] {\n+\t\tif item > best {\n+\t\t\tbest = item\n+\t\t}\n+\t}\n+\treturn best, nil\n+}\n+func jpfMerge(arguments []interface{}) (interface{}, error) {\n+\tfinal := make(map[string]interface{})\n+\tfor _, m := range arguments {\n+\t\tmapped := m.(map[string]interface{})\n+\t\tfor key, value := range mapped {\n+\t\t\tfinal[key] = value\n+\t\t}\n+\t}\n+\treturn final, nil\n+}\n+func jpfMaxBy(arguments []interface{}) (interface{}, error) {\n+\tintr := arguments[0].(*treeInterpreter)\n+\tarr := arguments[1].([]interface{})\n+\texp := arguments[2].(expRef)\n+\tnode := exp.ref\n+\tif len(arr) == 0 {\n+\t\treturn nil, nil\n+\t} else if len(arr) == 1 {\n+\t\treturn arr[0], nil\n+\t}\n+\tstart, err := intr.Execute(node, arr[0])\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\tif t, ok := start.(float64); ok {\n+\t\tbestVal := t\n+\t\tbestItem := arr[0]\n+\t\tfor _, item := range arr[1:] {\n+\t\t\tresult, err := intr.Execute(node, item)\n+\t\t\tif err != nil {\n+\t\t\t\treturn nil, err\n+\t\t\t}\n+\t\t\tcurrent, ok := result.(float64)\n+\t\t\tif !ok {\n+\t\t\t\treturn nil, errors.New(\"Invalid type, must be number\")\n+\t\t\t}\n+\t\t\tif current > bestVal {\n+\t\t\t\tbestVal = current\n+\t\t\t\tbestItem = item\n+\t\t\t}\n+\t\t}\n+\t\treturn bestItem, nil\n+\t} else if t, ok := start.(string); ok {\n+\t\tbestVal := t\n+\t\tbestItem := arr[0]\n+\t\tfor _, item := range arr[1:] {\n+\t\t\tresult, err := intr.Execute(node, item)\n+\t\t\tif err != nil {\n+\t\t\t\treturn nil, err\n+\t\t\t}\n+\t\t\tcurrent, ok := result.(string)\n+\t\t\tif !ok {\n+\t\t\t\treturn nil, errors.New(\"Invalid type, must be string\")\n+\t\t\t}\n+\t\t\tif current > bestVal {\n+\t\t\t\tbestVal = current\n+\t\t\t\tbestItem = item\n+\t\t\t}\n+\t\t}\n+\t\treturn bestItem, nil\n+\t} else {\n+\t\treturn nil, errors.New(\"Invalid type, must be number of string.\")\n+\t}\n+}\n+func jpfSum(arguments []interface{}) (interface{}, error) {\n+\titems, _ := jputil.ToArrayNum(arguments[0])\n+\tsum := 0.0\n+\tfor _, item := range items {\n+\t\tsum += item\n+\t}\n+\treturn sum, nil\n+}\n+\n+func jpfMin(arguments []interface{}) (interface{}, error) {\n+\tif items, ok := jputil.ToArrayNum(arguments[0]); ok {\n+\t\tif len(items) == 0 {\n+\t\t\treturn nil, nil\n+\t\t}\n+\t\tif len(items) == 1 {\n+\t\t\treturn items[0], nil\n+\t\t}\n+\t\tbest := items[0]\n+\t\tfor _, item := range items[1:] {\n+\t\t\tif item < best {\n+\t\t\t\tbest = item\n+\t\t\t}\n+\t\t}\n+\t\treturn best, nil\n+\t}\n+\titems, _ := jputil.ToArrayStr(arguments[0])\n+\tif len(items) == 0 {\n+\t\treturn nil, nil\n+\t}\n+\tif len(items) == 1 {\n+\t\treturn items[0], nil\n+\t}\n+\tbest := items[0]\n+\tfor _, item := range items[1:] {\n+\t\tif item < best {\n+\t\t\tbest = item\n+\t\t}\n+\t}\n+\treturn best, nil\n+}\n+\n+func jpfMinBy(arguments []interface{}) (interface{}, error) {\n+\tintr := arguments[0].(*treeInterpreter)\n+\tarr := arguments[1].([]interface{})\n+\texp := arguments[2].(expRef)\n+\tnode := exp.ref\n+\tif len(arr) == 0 {\n+\t\treturn nil, nil\n+\t} else if len(arr) == 1 {\n+\t\treturn arr[0], nil\n+\t}\n+\tstart, err := intr.Execute(node, arr[0])\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\tif t, ok := start.(float64); ok {\n+\t\tbestVal := t\n+\t\tbestItem := arr[0]\n+\t\tfor _, item := range arr[1:] {\n+\t\t\tresult, err := intr.Execute(node, item)\n+\t\t\tif err != nil {\n+\t\t\t\treturn nil, err\n+\t\t\t}\n+\t\t\tcurrent, ok := result.(float64)\n+\t\t\tif !ok {\n+\t\t\t\treturn nil, errors.New(\"Invalid type, must be number\")\n+\t\t\t}\n+\t\t\tif current < bestVal {\n+\t\t\t\tbestVal = current\n+\t\t\t\tbestItem = item\n+\t\t\t}\n+\t\t}\n+\t\treturn bestItem, nil\n+\t} else if t, ok := start.(string); ok {\n+\t\tbestVal := t\n+\t\tbestItem := arr[0]\n+\t\tfor _, item := range arr[1:] {\n+\t\t\tresult, err := intr.Execute(node, item)\n+\t\t\tif err != nil {\n+\t\t\t\treturn nil, err\n+\t\t\t}\n+\t\t\tcurrent, ok := result.(string)\n+\t\t\tif !ok {\n+\t\t\t\treturn nil, errors.New(\"Invalid type, must be string\")\n+\t\t\t}\n+\t\t\tif current < bestVal {\n+\t\t\t\tbestVal = current\n+\t\t\t\tbestItem = item\n+\t\t\t}\n+\t\t}\n+\t\treturn bestItem, nil\n+\t} else {\n+\t\treturn nil, errors.New(\"Invalid type, must be number of string.\")\n+\t}\n+}\n+func jpfType(arguments []interface{}) (interface{}, error) {\n+\targ := arguments[0]\n+\tif _, ok := arg.(float64); ok {\n+\t\treturn \"number\", nil\n+\t}\n+\tif _, ok := arg.(string); ok {\n+\t\treturn \"string\", nil\n+\t}\n+\tif _, ok := arg.([]interface{}); ok {\n+\t\treturn \"array\", nil\n+\t}\n+\tif _, ok := arg.(map[string]interface{}); ok {\n+\t\treturn \"object\", nil\n+\t}\n+\tif arg == nil {\n+\t\treturn \"null\", nil\n+\t}\n+\tif arg == true || arg == false {\n+\t\treturn \"boolean\", nil\n+\t}\n+\treturn nil, errors.New(\"Unknown type\")\n+}\n+func jpfKeys(arguments []interface{}) (interface{}, error) {\n+\targ := arguments[0].(map[string]interface{})\n+\tcollected := []interface{}{}\n+\tfor key := range arg {\n+\t\tcollected = append(collected, key)\n+\t}\n+\treturn collected, nil\n+}\n+func jpfValues(arguments []interface{}) (interface{}, error) {\n+\targ := arguments[0].(map[string]interface{})\n+\tcollected := []interface{}{}\n+\tfor _, value := range arg {\n+\t\tcollected = append(collected, value)\n+\t}\n+\treturn collected, nil\n+}\n+func jpfSort(arguments []interface{}) (interface{}, error) {\n+\tif items, ok := jputil.ToArrayNum(arguments[0]); ok {\n+\t\td := sort.Float64Slice(items)\n+\t\tsort.Stable(d)\n+\t\tfinal := make([]interface{}, len(d))\n+\t\tfor i, val := range d {\n+\t\t\tfinal[i] = val\n+\t\t}\n+\t\treturn final, nil\n+\t}\n+\t// Otherwise we're dealing with sort()'ing strings.\n+\titems, _ := jputil.ToArrayStr(arguments[0])\n+\td := sort.StringSlice(items)\n+\tsort.Stable(d)\n+\tfinal := make([]interface{}, len(d))\n+\tfor i, val := range d {\n+\t\tfinal[i] = val\n+\t}\n+\treturn final, nil\n+}\n+func jpfSortBy(arguments []interface{}) (interface{}, error) {\n+\tintr := arguments[0].(*treeInterpreter)\n+\tarr := arguments[1].([]interface{})\n+\texp := arguments[2].(expRef)\n+\tnode := exp.ref\n+\tif len(arr) == 0 {\n+\t\treturn arr, nil\n+\t} else if len(arr) == 1 {\n+\t\treturn arr, nil\n+\t}\n+\tstart, err := intr.Execute(node, arr[0])\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\tif _, ok := start.(float64); ok {\n+\t\tsortable := &byExprFloat{intr, node, arr, false}\n+\t\tsort.Stable(sortable)\n+\t\tif sortable.hasError {\n+\t\t\treturn nil, errors.New(\"Error in sort_by comparison\")\n+\t\t}\n+\t\treturn arr, nil\n+\t} else if _, ok := start.(string); ok {\n+\t\tsortable := &byExprString{intr, node, arr, false}\n+\t\tsort.Stable(sortable)\n+\t\tif sortable.hasError {\n+\t\t\treturn nil, errors.New(\"Error in sort_by comparison\")\n+\t\t}\n+\t\treturn arr, nil\n+\t} else {\n+\t\treturn nil, errors.New(\"Invalid type, must be number of string.\")\n+\t}\n+}\n+func jpfJoin(arguments []interface{}) (interface{}, error) {\n+\tsep := arguments[0].(string)\n+\t// We can't just do arguments[1].([]string), we have to\n+\t// manually convert each item to a string.\n+\tarrayStr := []string{}\n+\tfor _, item := range arguments[1].([]interface{}) {\n+\t\tarrayStr = append(arrayStr, item.(string))\n+\t}\n+\treturn strings.Join(arrayStr, sep), nil\n+}\n+func jpfReverse(arguments []interface{}) (interface{}, error) {\n+\tif s, ok := arguments[0].(string); ok {\n+\t\tr := []rune(s)\n+\t\tfor i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {\n+\t\t\tr[i], r[j] = r[j], r[i]\n+\t\t}\n+\t\treturn string(r), nil\n+\t}\n+\titems := arguments[0].([]interface{})\n+\tlength := len(items)\n+\treversed := make([]interface{}, length)\n+\tfor i, item := range items {\n+\t\treversed[length-(i+1)] = item\n+\t}\n+\treturn reversed, nil\n+}\n+func jpfToArray(arguments []interface{}) (interface{}, error) {\n+\tif _, ok := arguments[0].([]interface{}); ok {\n+\t\treturn arguments[0], nil\n+\t}\n+\tresult := make([]interface{}, 1)","path":"jmespath/functions.go","position":771,"original_position":771,"commit_id":"ade519d51529c92f1c9bcb9f7bc9b6d09cb23375","original_commit_id":"ade519d51529c92f1c9bcb9f7bc9b6d09cb23375","user":{"login":"jasdel","id":961963,"avatar_url":"https://avatars.githubusercontent.com/u/961963?v=3","gravatar_id":"","url":"https://api.github.com/users/jasdel","html_url":"https://github.com/jasdel","followers_url":"https://api.github.com/users/jasdel/followers","following_url":"https://api.github.com/users/jasdel/following{/other_user}","gists_url":"https://api.github.com/users/jasdel/gists{/gist_id}","starred_url":"https://api.github.com/users/jasdel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jasdel/subscriptions","organizations_url":"https://api.github.com/users/jasdel/orgs","repos_url":"https://api.github.com/users/jasdel/repos","events_url":"https://api.github.com/users/jasdel/events{/privacy}","received_events_url":"https://api.github.com/users/jasdel/received_events","type":"User","site_admin":false},"body":"I think the return here could just be a subslice instead of a new array.\r\n\r\n```go\r\nreturn arguments[:1:1], nil\r\n```\r\n\r\nhttp://play.golang.org/p/HuOmqAPmPL, the midden middle parameter of a sub slice statment is the capacity of the new slice value.","created_at":"2015-07-31T21:37:50Z","updated_at":"2015-07-31T21:37:50Z","html_url":"https://github.com/jmespath/jmespath.go/pull/2#discussion_r36019005","pull_request_url":"https://api.github.com/repos/jmespath/jmespath.go/pulls/2","_links":{"self":{"href":"https://api.github.com/repos/jmespath/jmespath.go/pulls/comments/36019005"},"html":{"href":"https://github.com/jmespath/jmespath.go/pull/2#discussion_r36019005"},"pull_request":{"href":"https://api.github.com/repos/jmespath/jmespath.go/pulls/2"}}},"pull_request":{"url":"https://api.github.com/repos/jmespath/jmespath.go/pulls/2","id":41071430,"html_url":"https://github.com/jmespath/jmespath.go/pull/2","diff_url":"https://github.com/jmespath/jmespath.go/pull/2.diff","patch_url":"https://github.com/jmespath/jmespath.go/pull/2.patch","issue_url":"https://api.github.com/repos/jmespath/jmespath.go/issues/2","number":2,"state":"open","locked":false,"title":"Initial commit of jmespath.go for code review","user":{"login":"jamesls","id":368057,"avatar_url":"https://avatars.githubusercontent.com/u/368057?v=3","gravatar_id":"","url":"https://api.github.com/users/jamesls","html_url":"https://github.com/jamesls","followers_url":"https://api.github.com/users/jamesls/followers","following_url":"https://api.github.com/users/jamesls/following{/other_user}","gists_url":"https://api.github.com/users/jamesls/gists{/gist_id}","starred_url":"https://api.github.com/users/jamesls/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jamesls/subscriptions","organizations_url":"https://api.github.com/users/jamesls/orgs","repos_url":"https://api.github.com/users/jamesls/repos","events_url":"https://api.github.com/users/jamesls/events{/privacy}","received_events_url":"https://api.github.com/users/jamesls/received_events","type":"User","site_admin":false},"body":"It would be great to get feedback on the jmespath.go implementation.  I've diffed this against an empty branch so the entire repo shows the diff, but the `*.go` files are the main things I'd like feedback on.\r\n\r\nFeel free to comment on anything no matter how small.  I'm definitely interested in anything at all I can improve, especially if it's not idiomatic go.\r\n\r\ncc @jmespath/owners @jasdel\r\n","created_at":"2015-07-28T23:08:40Z","updated_at":"2015-07-31T21:37:50Z","closed_at":null,"merged_at":null,"merge_commit_sha":"458206ad88c86541737891c8394b9fd6b9a1954f","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/jmespath/jmespath.go/pulls/2/commits","review_comments_url":"https://api.github.com/repos/jmespath/jmespath.go/pulls/2/comments","review_comment_url":"https://api.github.com/repos/jmespath/jmespath.go/pulls/comments{/number}","comments_url":"https://api.github.com/repos/jmespath/jmespath.go/issues/2/comments","statuses_url":"https://api.github.com/repos/jmespath/jmespath.go/statuses/ade519d51529c92f1c9bcb9f7bc9b6d09cb23375","head":{"label":"jmespath:initial-review","ref":"initial-review","sha":"ade519d51529c92f1c9bcb9f7bc9b6d09cb23375","user":{"login":"jmespath","id":6053930,"avatar_url":"https://avatars.githubusercontent.com/u/6053930?v=3","gravatar_id":"","url":"https://api.github.com/users/jmespath","html_url":"https://github.com/jmespath","followers_url":"https://api.github.com/users/jmespath/followers","following_url":"https://api.github.com/users/jmespath/following{/other_user}","gists_url":"https://api.github.com/users/jmespath/gists{/gist_id}","starred_url":"https://api.github.com/users/jmespath/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmespath/subscriptions","organizations_url":"https://api.github.com/users/jmespath/orgs","repos_url":"https://api.github.com/users/jmespath/repos","events_url":"https://api.github.com/users/jmespath/events{/privacy}","received_events_url":"https://api.github.com/users/jmespath/received_events","type":"Organization","site_admin":false},"repo":{"id":39148229,"name":"jmespath.go","full_name":"jmespath/jmespath.go","owner":{"login":"jmespath","id":6053930,"avatar_url":"https://avatars.githubusercontent.com/u/6053930?v=3","gravatar_id":"","url":"https://api.github.com/users/jmespath","html_url":"https://github.com/jmespath","followers_url":"https://api.github.com/users/jmespath/followers","following_url":"https://api.github.com/users/jmespath/following{/other_user}","gists_url":"https://api.github.com/users/jmespath/gists{/gist_id}","starred_url":"https://api.github.com/users/jmespath/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmespath/subscriptions","organizations_url":"https://api.github.com/users/jmespath/orgs","repos_url":"https://api.github.com/users/jmespath/repos","events_url":"https://api.github.com/users/jmespath/events{/privacy}","received_events_url":"https://api.github.com/users/jmespath/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/jmespath/jmespath.go","description":"Golang implementation of JMESPath.","fork":false,"url":"https://api.github.com/repos/jmespath/jmespath.go","forks_url":"https://api.github.com/repos/jmespath/jmespath.go/forks","keys_url":"https://api.github.com/repos/jmespath/jmespath.go/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jmespath/jmespath.go/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jmespath/jmespath.go/teams","hooks_url":"https://api.github.com/repos/jmespath/jmespath.go/hooks","issue_events_url":"https://api.github.com/repos/jmespath/jmespath.go/issues/events{/number}","events_url":"https://api.github.com/repos/jmespath/jmespath.go/events","assignees_url":"https://api.github.com/repos/jmespath/jmespath.go/assignees{/user}","branches_url":"https://api.github.com/repos/jmespath/jmespath.go/branches{/branch}","tags_url":"https://api.github.com/repos/jmespath/jmespath.go/tags","blobs_url":"https://api.github.com/repos/jmespath/jmespath.go/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jmespath/jmespath.go/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jmespath/jmespath.go/git/refs{/sha}","trees_url":"https://api.github.com/repos/jmespath/jmespath.go/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jmespath/jmespath.go/statuses/{sha}","languages_url":"https://api.github.com/repos/jmespath/jmespath.go/languages","stargazers_url":"https://api.github.com/repos/jmespath/jmespath.go/stargazers","contributors_url":"https://api.github.com/repos/jmespath/jmespath.go/contributors","subscribers_url":"https://api.github.com/repos/jmespath/jmespath.go/subscribers","subscription_url":"https://api.github.com/repos/jmespath/jmespath.go/subscription","commits_url":"https://api.github.com/repos/jmespath/jmespath.go/commits{/sha}","git_commits_url":"https://api.github.com/repos/jmespath/jmespath.go/git/commits{/sha}","comments_url":"https://api.github.com/repos/jmespath/jmespath.go/comments{/number}","issue_comment_url":"https://api.github.com/repos/jmespath/jmespath.go/issues/comments{/number}","contents_url":"https://api.github.com/repos/jmespath/jmespath.go/contents/{+path}","compare_url":"https://api.github.com/repos/jmespath/jmespath.go/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jmespath/jmespath.go/merges","archive_url":"https://api.github.com/repos/jmespath/jmespath.go/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jmespath/jmespath.go/downloads","issues_url":"https://api.github.com/repos/jmespath/jmespath.go/issues{/number}","pulls_url":"https://api.github.com/repos/jmespath/jmespath.go/pulls{/number}","milestones_url":"https://api.github.com/repos/jmespath/jmespath.go/milestones{/number}","notifications_url":"https://api.github.com/repos/jmespath/jmespath.go/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jmespath/jmespath.go/labels{/name}","releases_url":"https://api.github.com/repos/jmespath/jmespath.go/releases{/id}","created_at":"2015-07-15T16:34:31Z","updated_at":"2015-07-16T00:49:46Z","pushed_at":"2015-07-28T23:08:41Z","git_url":"git://github.com/jmespath/jmespath.go.git","ssh_url":"git@github.com:jmespath/jmespath.go.git","clone_url":"https://github.com/jmespath/jmespath.go.git","svn_url":"https://github.com/jmespath/jmespath.go","homepage":null,"size":212,"stargazers_count":1,"watchers_count":1,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":2,"forks":0,"open_issues":2,"watchers":1,"default_branch":"master"}},"base":{"label":"jmespath:empty","ref":"empty","sha":"24f4d5c6044980cf02d1f2ea64b6360f0578f715","user":{"login":"jmespath","id":6053930,"avatar_url":"https://avatars.githubusercontent.com/u/6053930?v=3","gravatar_id":"","url":"https://api.github.com/users/jmespath","html_url":"https://github.com/jmespath","followers_url":"https://api.github.com/users/jmespath/followers","following_url":"https://api.github.com/users/jmespath/following{/other_user}","gists_url":"https://api.github.com/users/jmespath/gists{/gist_id}","starred_url":"https://api.github.com/users/jmespath/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmespath/subscriptions","organizations_url":"https://api.github.com/users/jmespath/orgs","repos_url":"https://api.github.com/users/jmespath/repos","events_url":"https://api.github.com/users/jmespath/events{/privacy}","received_events_url":"https://api.github.com/users/jmespath/received_events","type":"Organization","site_admin":false},"repo":{"id":39148229,"name":"jmespath.go","full_name":"jmespath/jmespath.go","owner":{"login":"jmespath","id":6053930,"avatar_url":"https://avatars.githubusercontent.com/u/6053930?v=3","gravatar_id":"","url":"https://api.github.com/users/jmespath","html_url":"https://github.com/jmespath","followers_url":"https://api.github.com/users/jmespath/followers","following_url":"https://api.github.com/users/jmespath/following{/other_user}","gists_url":"https://api.github.com/users/jmespath/gists{/gist_id}","starred_url":"https://api.github.com/users/jmespath/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmespath/subscriptions","organizations_url":"https://api.github.com/users/jmespath/orgs","repos_url":"https://api.github.com/users/jmespath/repos","events_url":"https://api.github.com/users/jmespath/events{/privacy}","received_events_url":"https://api.github.com/users/jmespath/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/jmespath/jmespath.go","description":"Golang implementation of JMESPath.","fork":false,"url":"https://api.github.com/repos/jmespath/jmespath.go","forks_url":"https://api.github.com/repos/jmespath/jmespath.go/forks","keys_url":"https://api.github.com/repos/jmespath/jmespath.go/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jmespath/jmespath.go/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jmespath/jmespath.go/teams","hooks_url":"https://api.github.com/repos/jmespath/jmespath.go/hooks","issue_events_url":"https://api.github.com/repos/jmespath/jmespath.go/issues/events{/number}","events_url":"https://api.github.com/repos/jmespath/jmespath.go/events","assignees_url":"https://api.github.com/repos/jmespath/jmespath.go/assignees{/user}","branches_url":"https://api.github.com/repos/jmespath/jmespath.go/branches{/branch}","tags_url":"https://api.github.com/repos/jmespath/jmespath.go/tags","blobs_url":"https://api.github.com/repos/jmespath/jmespath.go/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jmespath/jmespath.go/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jmespath/jmespath.go/git/refs{/sha}","trees_url":"https://api.github.com/repos/jmespath/jmespath.go/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jmespath/jmespath.go/statuses/{sha}","languages_url":"https://api.github.com/repos/jmespath/jmespath.go/languages","stargazers_url":"https://api.github.com/repos/jmespath/jmespath.go/stargazers","contributors_url":"https://api.github.com/repos/jmespath/jmespath.go/contributors","subscribers_url":"https://api.github.com/repos/jmespath/jmespath.go/subscribers","subscription_url":"https://api.github.com/repos/jmespath/jmespath.go/subscription","commits_url":"https://api.github.com/repos/jmespath/jmespath.go/commits{/sha}","git_commits_url":"https://api.github.com/repos/jmespath/jmespath.go/git/commits{/sha}","comments_url":"https://api.github.com/repos/jmespath/jmespath.go/comments{/number}","issue_comment_url":"https://api.github.com/repos/jmespath/jmespath.go/issues/comments{/number}","contents_url":"https://api.github.com/repos/jmespath/jmespath.go/contents/{+path}","compare_url":"https://api.github.com/repos/jmespath/jmespath.go/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jmespath/jmespath.go/merges","archive_url":"https://api.github.com/repos/jmespath/jmespath.go/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jmespath/jmespath.go/downloads","issues_url":"https://api.github.com/repos/jmespath/jmespath.go/issues{/number}","pulls_url":"https://api.github.com/repos/jmespath/jmespath.go/pulls{/number}","milestones_url":"https://api.github.com/repos/jmespath/jmespath.go/milestones{/number}","notifications_url":"https://api.github.com/repos/jmespath/jmespath.go/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jmespath/jmespath.go/labels{/name}","releases_url":"https://api.github.com/repos/jmespath/jmespath.go/releases{/id}","created_at":"2015-07-15T16:34:31Z","updated_at":"2015-07-16T00:49:46Z","pushed_at":"2015-07-28T23:08:41Z","git_url":"git://github.com/jmespath/jmespath.go.git","ssh_url":"git@github.com:jmespath/jmespath.go.git","clone_url":"https://github.com/jmespath/jmespath.go.git","svn_url":"https://github.com/jmespath/jmespath.go","homepage":null,"size":212,"stargazers_count":1,"watchers_count":1,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":2,"forks":0,"open_issues":2,"watchers":1,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/jmespath/jmespath.go/pulls/2"},"html":{"href":"https://github.com/jmespath/jmespath.go/pull/2"},"issue":{"href":"https://api.github.com/repos/jmespath/jmespath.go/issues/2"},"comments":{"href":"https://api.github.com/repos/jmespath/jmespath.go/issues/2/comments"},"review_comments":{"href":"https://api.github.com/repos/jmespath/jmespath.go/pulls/2/comments"},"review_comment":{"href":"https://api.github.com/repos/jmespath/jmespath.go/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/jmespath/jmespath.go/pulls/2/commits"},"statuses":{"href":"https://api.github.com/repos/jmespath/jmespath.go/statuses/ade519d51529c92f1c9bcb9f7bc9b6d09cb23375"}}}}