This repository has been archived by the owner on Apr 19, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: add intersect and except (#3)
- Loading branch information
Showing
9 changed files
with
994 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package slices | ||
|
||
import ( | ||
"unsafe" | ||
) | ||
|
||
type exceptFn func(sptr, optr unsafe.Pointer) interface{} | ||
|
||
// Except returns a slice with the elements of slice that are not in other. | ||
// When the slices different, slice is returned. | ||
func Except(slice, other interface{}) interface{} { | ||
fn, ok := exceptOf(slice, other) | ||
if fn == nil { | ||
panic("slice is not a supported slice type") | ||
} | ||
if !ok { | ||
panic("other is not the same type as slice") | ||
} | ||
|
||
sptr := noescape(ptrOf(slice)) | ||
optr := noescape(ptrOf(other)) | ||
return fn(sptr, optr) | ||
} | ||
|
||
func exceptOf(slice, other interface{}) (exceptFn, bool) { | ||
switch slice.(type) { | ||
case []string: | ||
_, ok := other.([]string) | ||
return stringExcept, ok | ||
case []int: | ||
_, ok := other.([]int) | ||
return intExcept, ok | ||
case []int8: | ||
_, ok := other.([]int8) | ||
return int8Except, ok | ||
case []int16: | ||
_, ok := other.([]int16) | ||
return int16Except, ok | ||
case []int32: | ||
_, ok := other.([]int32) | ||
return int32Except, ok | ||
case []int64: | ||
_, ok := other.([]int64) | ||
return int64Except, ok | ||
case []uint: | ||
_, ok := other.([]uint) | ||
return uintExcept, ok | ||
case []uint8: | ||
_, ok := other.([]uint8) | ||
return uint8Except, ok | ||
case []uint16: | ||
_, ok := other.([]uint16) | ||
return uint16Except, ok | ||
case []uint32: | ||
_, ok := other.([]uint32) | ||
return uint32Except, ok | ||
case []uint64: | ||
_, ok := other.([]uint64) | ||
return uint64Except, ok | ||
case []float32: | ||
_, ok := other.([]float32) | ||
return float32Except, ok | ||
case []float64: | ||
_, ok := other.([]float64) | ||
return float64Except, ok | ||
} | ||
return nil, false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package slices_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/hamba/slices" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestExcept(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
a interface{} | ||
b interface{} | ||
want interface{} | ||
}{ | ||
{ | ||
name: "string contains", | ||
a: []string{"foo", "bar", "baz", "bat"}, | ||
b: []string{"bar", "baz", "test"}, | ||
want: []string{"foo", "bat"}, | ||
}, | ||
{ | ||
name: "int contains", | ||
a: []int{1, 2, 3, 4}, | ||
b: []int{2, 3, 5}, | ||
want: []int{1, 4}, | ||
}, | ||
{ | ||
name: "int8 contains", | ||
a: []int8{1, 2, 3, 4}, | ||
b: []int8{2, 3, 5}, | ||
want: []int8{1, 4}, | ||
}, | ||
{ | ||
name: "int16 contains", | ||
a: []int16{1, 2, 3, 4}, | ||
b: []int16{2, 3, 5}, | ||
want: []int16{1, 4}, | ||
}, | ||
{ | ||
name: "int32 contains", | ||
a: []int32{1, 2, 3, 4}, | ||
b: []int32{2, 3, 5}, | ||
want: []int32{1, 4}, | ||
}, | ||
{ | ||
name: "int64 contains", | ||
a: []int64{1, 2, 3, 4}, | ||
b: []int64{2, 3, 5}, | ||
want: []int64{1, 4}, | ||
}, | ||
{ | ||
name: "uint contains", | ||
a: []uint{1, 2, 3, 4}, | ||
b: []uint{2, 3, 5}, | ||
want: []uint{1, 4}, | ||
}, | ||
{ | ||
name: "uint8 contains", | ||
a: []uint8{1, 2, 3, 4}, | ||
b: []uint8{2, 3, 5}, | ||
want: []uint8{1, 4}, | ||
}, | ||
{ | ||
name: "uint16 contains", | ||
a: []uint16{1, 2, 3, 4}, | ||
b: []uint16{2, 3, 5}, | ||
want: []uint16{1, 4}, | ||
}, | ||
{ | ||
name: "uint32 contains", | ||
a: []uint32{1, 2, 3, 4}, | ||
b: []uint32{2, 3, 5}, | ||
want: []uint32{1, 4}, | ||
}, | ||
{ | ||
name: "uint64 contains", | ||
a: []uint64{1, 2, 3, 4}, | ||
b: []uint64{2, 3, 5}, | ||
want: []uint64{1, 4}, | ||
}, | ||
{ | ||
name: "float32 contains", | ||
a: []float32{1, 2, 3, 4}, | ||
b: []float32{2, 3, 5}, | ||
want: []float32{1, 4}, | ||
}, | ||
{ | ||
name: "float64 contains", | ||
a: []float64{1, 2, 3, 4}, | ||
b: []float64{2, 3, 5}, | ||
want: []float64{1, 4}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
got := slices.Except(tt.a, tt.b) | ||
|
||
assert.Equal(t, tt.want, got) | ||
}) | ||
} | ||
} | ||
|
||
func TestExcept_ChecksSliceType(t *testing.T) { | ||
assert.Panics(t, func() { | ||
slices.Except("test", "test") | ||
}) | ||
} | ||
|
||
func TestExcept_ChecksValType(t *testing.T) { | ||
assert.Panics(t, func() { | ||
slices.Except([]string{"test"}, 1) | ||
}) | ||
} | ||
|
||
func BenchmarkExcept(b *testing.B) { | ||
a := []string{"foo", "bar", "baz", "bat"} | ||
c := []string{"bar", "baz", "test"} | ||
|
||
b.ReportAllocs() | ||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
slices.Except(a, c) | ||
} | ||
} | ||
|
||
func except(slice, other []string) interface{} { | ||
s := make([]string, len(slice)) | ||
copy(s, slice) | ||
for i := 0; i < len(s); i++ { | ||
for _, v := range other { | ||
if v == s[i] { | ||
s = append(s[:i], s[i+1:]...) | ||
i-- | ||
break | ||
} | ||
} | ||
} | ||
return s | ||
} | ||
|
||
func BenchmarkExceptNative(b *testing.B) { | ||
slice := []string{"foo", "bar", "baz", "bat"} | ||
other := []string{"bar", "baz", "test"} | ||
|
||
b.ReportAllocs() | ||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
except(slice, other) | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.