diff --git a/jsonlogic.go b/jsonlogic.go index 94e4a7c..8a07230 100644 --- a/jsonlogic.go +++ b/jsonlogic.go @@ -6,6 +6,7 @@ import ( "io" "math" "reflect" + "sort" "strings" "github.com/mitchellh/copystructure" @@ -114,6 +115,40 @@ func _inRange(value interface{}, values interface{}) bool { return toString(value) >= toString(i) && toString(j) >= toString(value) } +// Expect values to be in alphabetical ascending order +func _inSorted(value interface{}, values interface{}) bool { + valuesSlice := values.([]interface{}) + + findElement := func(i int) bool { + element := valuesSlice[i] + + if isSlice(valuesSlice[i]) { + sliceElement := valuesSlice[i].([]interface{}) + start := sliceElement[0] + end := sliceElement[1] + + return (toString(start) <= toString(value) && toString(end) >= toString(value)) || toString(end) > toString(value) + } + + return toString(element) >= toString(value) + } + + i := sort.Search(len(valuesSlice), findElement) + if i >= len(valuesSlice) { + return false + } + + if isSlice(valuesSlice[i]) { + sliceElement := valuesSlice[i].([]interface{}) + start := sliceElement[0] + end := sliceElement[1] + + return toString(start) <= toString(value) && toString(end) >= toString(value) + } + + return toString(valuesSlice[i]) == toString(value) +} + func _in(value interface{}, values interface{}) bool { if isString(values) { return strings.Contains(values.(string), value.(string)) @@ -580,6 +615,10 @@ func operation(operator string, values, data interface{}) interface{} { return _in(parsed[0], parsed[1]) } + if operator == "in_sorted" { + return _inSorted(parsed[0], parsed[1]) + } + if operator == "%" { return mod(parsed[0], parsed[1]) } diff --git a/jsonlogic_test.go b/jsonlogic_test.go index bd8058e..904dc25 100644 --- a/jsonlogic_test.go +++ b/jsonlogic_test.go @@ -184,6 +184,59 @@ func TestListOfRanges(t *testing.T) { assert.JSONEq(t, expected, result.String()) } +func TestInSortedOperator(t *testing.T) { + rule := strings.NewReader(`{ + "filter": [ + {"var": "people"}, + {"in_sorted": [ + {"var": ".age"}, + [ + 11.00, + [12, 14], + [13, 18], + 2, + "20", + [32, 38], + "a", + ["b", "d"] + ] + ]} + ] + }`) + + data := strings.NewReader(`{ + "people": [ + {"age":"18", "name":"John"}, + {"age":20, "name":"Luke"}, + {"age":18, "name":"Mark"}, + {"age":40, "name":"Donald"}, + {"age":11, "name":"Mickey"}, + {"age":"1", "name":"Minnie"}, + {"age":2, "name":"Mario"}, + {"age":"a", "name":"Mario"}, + {"age":"c", "name":"Princess"} + ] + }`) + + var result bytes.Buffer + err := Apply(rule, data, &result) + if err != nil { + t.Fatal(err) + } + + expected := `[ + {"age":"18", "name": "John"}, + {"age":20, "name":"Luke"}, + {"age":18, "name": "Mark"}, + {"age":11, "name":"Mickey"}, + {"age":2, "name":"Mario"}, + {"age":"a", "name":"Mario"}, + {"age":"c", "name":"Princess"} + ]` + + assert.JSONEq(t, expected, result.String()) +} + func TestSomeWithLists(t *testing.T) { rule := strings.NewReader(`{ "some": [