-
Notifications
You must be signed in to change notification settings - Fork 8.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor equals #3958
Refactor equals #3958
Conversation
/hold |
[]string{"2", "1", "3"},
[]string{"1", "2", "3"},
false,
Should be true. That is one of the reasons why we cannot use
reflect.DeepEqual. Also, we cannot assume the lists are ordered.
…On Wed, Apr 3, 2019 at 2:46 AM dongqi ***@***.***> wrote:
why don't we use the equal function like this
func StringSliceEqual(a, b []string) bool {
if len(a) != len(b) {
return false
}
if (a == nil) != (b == nil) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
here is the benchmark test
equal.go
package equal
import "reflect"
func StringSliceReflectEqual(a, b []string) bool {
return reflect.DeepEqual(a, b)
}
func StringSliceEqual(a, b []string) bool {
if len(a) != len(b) {
return false
}
if (a == nil) != (b == nil) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
func StringElementsMatch(listA, listB []string) bool {
if len(listA) != len(listB) {
return false
}
visited := make([]bool, len(listB))
for i := 0; i < len(listA); i++ {
element := listA[i]
found := false
for j := 0; j < len(listB); j++ {
if visited[j] {
continue
}
if listB[j] == element {
visited[j] = true
found = true
break
}
}
if !found {
return false
}
}
return true
}
equal_test.go
package equal
import "testing"
var stringSliceEqualTests = []struct {
a []string
b []string
out bool
}{
{
[]string{"1", "2", "3"},
[]string{"1", "2", "3"},
true,
},
{
[]string{"2", "1", "3"},
[]string{"1", "2", "3"},
false,
},
{
make([]string, 0, 3),
make([]string, 0, 3),
true,
},
{
make([]string, 0, 3),
make([]string, 0, 5),
true,
},
{
[]string{"1", "1", "1"}[0:1],
[]string{"1", "1", "1"}[1:2],
true,
},
{
make([]string, 0, 3),
nil,
false,
},
}
func TestStringSliceReflectEqual(t *testing.T) {
for _, test := range stringSliceEqualTests {
actual := StringSliceReflectEqual(test.a, test.b)
if actual != test.out {
t.Errorf("ReflectEqual(%#v, %#v) = %v; want %v", test.a, test.b, actual, test.out)
}
}
}
func TestStringSliceEqual(t *testing.T) {
for _, test := range stringSliceEqualTests {
actual := StringSliceEqual(test.a, test.b)
if actual != test.out {
t.Errorf("Equal(%#v, %#v) = %v; want %v", test.a, test.b, actual, test.out)
}
}
}
func TestStringElementsMatch(t *testing.T) {
for _, test := range stringSliceEqualTests {
actual := StringElementsMatch(test.a, test.b)
if actual != test.out {
t.Errorf("Equal(%#v, %#v) = %v; want %v", test.a, test.b, actual, test.out)
}
}
}
func BenchmarkStringSliceReflectEqual(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, test := range stringSliceEqualTests {
StringSliceReflectEqual(test.a, test.b)
}
}
}
func BenchmarkStringSliceEqual(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, test := range stringSliceEqualTests {
StringSliceEqual(test.a, test.b)
}
}
}
func BenchmarkStringElementsMatch(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, test := range stringSliceEqualTests {
StringElementsMatch(test.a, test.b)
}
}
}
run go test -bench=. -run=none, we can get the result below:
goos: linux
goarch: amd64
pkg: Learning/equal
BenchmarkStringSliceReflectEqual-4 1000000 1073 ns/op
BenchmarkStringSliceEqual-4 30000000 43.3 ns/op
BenchmarkStringElementsMatch-4 20000000 97.0 ns/op
PASS
ok Learning/equal 4.473s
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#3958 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAJ3Iwz2Yn3NtdQKiezsn-RvhKB_qcQcks5vdEBPgaJpZM4cYHhh>
.
|
internal/sets/match_test.go
Outdated
} | ||
|
||
sameResult := StringElementsMatch(testCase.listB, testCase.listA) | ||
if result != testCase.expected { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/s/result/sameResult
can we not make this general enough so that we don't repeat the same logic for different types? maybe something like following in sets package?
with this you can still have type specific helper functions, but based on this common function. that way we would not be repeating the same logic in multiple functions. |
I started doing just that but it implies using reflection
If you are ok with that I can change the code Edit: not sure if some of the equalFunc would require type casting or not |
/lgtm |
@ElvinEfendi this is the working implementation https://gist.github.com/aledbf/5ee02ca3959cb640ae83760145aef4ca |
6dde93f
to
51de018
Compare
/lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: aledbf, ElvinEfendi The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
||
func isIterable(obj reflect.Value) bool { | ||
switch reflect.TypeOf(obj).Kind() { | ||
case reflect.Struct, reflect.Slice, reflect.Array: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aledbf why the type struct is iterative?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dongqi1990 fixed by #3980
What this PR does / why we need it:
Which issue this PR fixes (optional, in
fixes #<issue number>(, fixes #<issue_number>, ...)
format, will close that issue when PR gets merged): fixes #3956Special notes for your reviewer: