Skip to content

Commit e5ba472

Browse files
authored
Merge pull request #7 from huttotw/feature/ncontains
feature/ncontains
2 parents aca18a8 + d98376d commit e5ba472

File tree

4 files changed

+97
-8
lines changed

4 files changed

+97
-8
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ BenchmarkGreaterThan-8|200000000|18.1 ns/op|0 B/op|0 allocs/op|
7272
BenchmarkGreaterThanEqual-8|300000000|13.9 ns/op|0 B/op|0 allocs/op|
7373
BenchmarkContains-8|50000000|73.0 ns/op|64 B/op|2 allocs/op|
7474
BenchmarkContainsLong50000-8|100000000|55.6 ns/op|32 B/op|1 allocs/op|
75+
BenchmarkNotContains-8|50000000|75.1 ns/op|64 B/op|2 allocs/op|
76+
BenchmarkNotContainsLong50000-8|100000000|56.2 ns/op|32 B/op|1 allocs/op|
7577
BenchmarkPluckShallow-8|100000000|60.2 ns/op|16 B/op|1 allocs/op|
7678
BenchmarkPluckDeep-8|20000000|242 ns/op|112 B/op|1 allocs/op|
7779

comparators.go

+47
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,53 @@ func containsFloat64(a, b interface{}) bool {
164164
return false
165165
}
166166

167+
// notContains will return true if the b is not contained a. This will also return
168+
// true if a is a slice of different types than b. It will return false if a
169+
// is not a slice.
170+
func notContains(a, b interface{}) bool {
171+
t1 := reflect.TypeOf(a)
172+
t2 := reflect.TypeOf(b)
173+
174+
if t1.Kind() != reflect.Slice {
175+
return false
176+
}
177+
178+
switch t2.Kind() {
179+
case reflect.String:
180+
return notContainsString(a, b)
181+
case reflect.Float64:
182+
return notContainsFloat64(a, b)
183+
default:
184+
return false
185+
}
186+
}
187+
188+
func notContainsString(a, b interface{}) bool {
189+
as, ok := a.([]interface{})
190+
if !ok {
191+
return false
192+
}
193+
for _, elem := range as {
194+
if val, ok := elem.(string); ok && val == b.(string) {
195+
return false
196+
}
197+
}
198+
return true
199+
}
200+
201+
func notContainsFloat64(a, b interface{}) bool {
202+
as, ok := a.([]interface{})
203+
if !ok {
204+
return false
205+
}
206+
for _, elem := range as {
207+
if val, ok := elem.(float64); ok && val == b.(float64) {
208+
return false
209+
}
210+
}
211+
return true
212+
}
213+
167214
// oneOf will return true if b contains a
168215
func oneOf(a, b interface{}) bool {
169216
return contains(b, a)

comparators_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,45 @@ func BenchmarkContainsLong50000(b *testing.B) {
199199
}
200200
}
201201

202+
func TestNotContains(t *testing.T) {
203+
cases := []testCase{
204+
testCase{args: []interface{}{[]interface{}{"a", "b"}, "a"}, expected: false},
205+
testCase{args: []interface{}{[]interface{}{"a", "b"}, "c"}, expected: true},
206+
testCase{args: []interface{}{[]interface{}{"a", "b"}, float64(1)}, expected: true},
207+
testCase{args: []interface{}{[]interface{}{float64(1), float64(2)}, float64(1)}, expected: false},
208+
testCase{args: []interface{}{[]interface{}{float64(1), float64(2)}, float64(3)}, expected: true},
209+
testCase{args: []interface{}{[]interface{}{float64(1.01), float64(1.02)}, float64(1.01)}, expected: false},
210+
}
211+
212+
for i, c := range cases {
213+
res := notContains(c.args[0], c.args[1])
214+
if res != c.expected {
215+
t.Fatalf("expected case %d to be %v, got %v", i, c.expected, res)
216+
}
217+
}
218+
}
219+
220+
func BenchmarkNotContains(b *testing.B) {
221+
for i := 0; i < b.N; i++ {
222+
contains([]string{"1", "2"}, "3")
223+
}
224+
}
225+
226+
func BenchmarkNotContainsLong50000(b *testing.B) {
227+
var list []string
228+
229+
// Simulate a list of postal codes
230+
for i := 0; i < 50000; i++ {
231+
list = append(list, fmt.Sprintf("%d", i))
232+
}
233+
234+
b.ResetTimer()
235+
236+
for i := 0; i < b.N; i++ {
237+
contains(list, "50000")
238+
}
239+
}
240+
202241
func TestOneOf(t *testing.T) {
203242
cases := []testCase{
204243
testCase{args: []interface{}{"a", []interface{}{"a", "b"}}, expected: true},

rule.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ const (
1414
// defaultComparators is a map of all the default comparators that
1515
// a new engine should include
1616
var defaultComparators = map[string]Comparator{
17-
"eq": equal,
18-
"ne": notEqual,
19-
"gt": greaterThan,
20-
"gte": greaterThanEqual,
21-
"lt": lessThan,
22-
"lte": lessThanEqual,
23-
"contains": contains,
24-
"oneof": oneOf,
17+
"eq": equal,
18+
"ne": notEqual,
19+
"gt": greaterThan,
20+
"gte": greaterThanEqual,
21+
"lt": lessThan,
22+
"lte": lessThanEqual,
23+
"contains": contains,
24+
"ncontains": notContains,
25+
"oneof": oneOf,
2526
}
2627

2728
// Rule is a our smallest unit of measure, each rule will be

0 commit comments

Comments
 (0)