From 9b9bb2dc2c7f1530f5d0f75062c82e73bcd8c8c9 Mon Sep 17 00:00:00 2001 From: nuxen Date: Sun, 25 Aug 2024 18:00:11 +0200 Subject: [PATCH] perf(utils): optimize slice functions (#132) * perf(utils): optimize slice functions * test(utils): added tests for SimplifyHDRSlice --- internal/release/release.go | 8 +-- internal/utils/slices.go | 43 ++++++------ internal/utils/slices_test.go | 126 ++++++++++++++++++++++++++++++++-- 3 files changed, 149 insertions(+), 28 deletions(-) diff --git a/internal/release/release.go b/internal/release/release.go index ac8abdd..cca5d51 100644 --- a/internal/release/release.go +++ b/internal/release/release.go @@ -36,17 +36,17 @@ func compareReleases(r1, r2 rls.Release, fuzzyMatching domain.FuzzyMatching) int return 203 } - if !utils.CompareStringSlices(r1.Cut, r2.Cut) { + if !utils.EqualElements(r1.Cut, r2.Cut) { return 204 } - if !utils.CompareStringSlices(r1.Edition, r2.Edition) { + if !utils.EqualElements(r1.Edition, r2.Edition) { return 205 } // skip comparing repack status when skipRepackCompare is enabled if !fuzzyMatching.SkipRepackCompare { - if !utils.CompareStringSlices(r1.Other, r2.Other) { + if !utils.EqualElements(r1.Other, r2.Other) { return 206 } } @@ -57,7 +57,7 @@ func compareReleases(r1, r2 rls.Release, fuzzyMatching domain.FuzzyMatching) int r2.HDR = utils.SimplifyHDRSlice(r2.HDR) } - if !utils.CompareStringSlices(r1.HDR, r2.HDR) { + if !utils.EqualElements(r1.HDR, r2.HDR) { return 207 } diff --git a/internal/utils/slices.go b/internal/utils/slices.go index 3d792f8..5e90f86 100644 --- a/internal/utils/slices.go +++ b/internal/utils/slices.go @@ -4,43 +4,46 @@ package utils import ( - "slices" "strings" ) func DedupeSlice[T comparable](s []T) []T { - inResult := make(map[T]bool) - var result []T - for _, str := range s { - if _, ok := inResult[str]; !ok { - inResult[str] = true - result = append(result, str) - } + resultSet := make(map[T]struct{}) + for _, i := range s { + resultSet[i] = struct{}{} + } + + result := make([]T, 0, len(resultSet)) + for str := range resultSet { + result = append(result, str) } + return result } -func CompareStringSlices(x, y []string) bool { +func EqualElements[T comparable](x, y []T) bool { if len(x) != len(y) { return false } - sortedX := slices.Clone(x) - sortedY := slices.Clone(y) + freqMap := make(map[T]int) + for _, i := range x { + freqMap[i]++ + } - slices.Sort(sortedX) - slices.Sort(sortedY) + for _, i := range y { + if freqMap[i] == 0 { + return false + } + freqMap[i]-- + } - return slices.Equal(sortedX, sortedY) + return true } func SimplifyHDRSlice(hdrSlice []string) []string { - if len(hdrSlice) == 0 { - return hdrSlice - } - - for i, v := range hdrSlice { - if strings.Contains(v, "HDR") { + for i := range hdrSlice { + if strings.Contains(hdrSlice[i], "HDR") { hdrSlice[i] = "HDR" } } diff --git a/internal/utils/slices_test.go b/internal/utils/slices_test.go index 3ed50a6..c24650d 100644 --- a/internal/utils/slices_test.go +++ b/internal/utils/slices_test.go @@ -33,7 +33,7 @@ func Test_DedupeSlice(t *testing.T) { { name: "string_slice_empty", slice: []string{}, - want: []string(nil), + want: []string{}, }, { name: "int_slice_some_duplicates", @@ -53,19 +53,137 @@ func Test_DedupeSlice(t *testing.T) { { name: "int_slice_empty", slice: []int{}, - want: []int(nil), + want: []int{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { switch v := tt.slice.(type) { case []string: - assert.Equalf(t, tt.want, DedupeSlice(v), "Dedupe(%v)", v) + assert.ElementsMatchf(t, tt.want, DedupeSlice(v), "Dedupe(%v)", v) case []int: - assert.Equalf(t, tt.want, DedupeSlice(v), "Dedupe(%v)", v) + assert.ElementsMatchf(t, tt.want, DedupeSlice(v), "Dedupe(%v)", v) default: t.Errorf("Unsupported slice type in test case: %v", tt.name) } }) } } + +func Test_EqualElements(t *testing.T) { + tests := []struct { + name string + x interface{} + y interface{} + want bool + }{ + { + name: "string_slice_identical_elements", + x: []string{"a", "b", "c"}, + y: []string{"a", "b", "c"}, + want: true, + }, + { + name: "string_slice_different_order", + x: []string{"a", "b", "c"}, + y: []string{"c", "b", "a"}, + want: true, + }, + { + name: "string_slice_different_elements", + x: []string{"a", "b", "c"}, + y: []string{"a", "b", "d"}, + want: false, + }, + { + name: "string_slice_different_lengths", + x: []string{"a", "b", "c"}, + y: []string{"a", "b"}, + want: false, + }, + { + name: "int_slice_identical_elements", + x: []int{1, 2, 3}, + y: []int{1, 2, 3}, + want: true, + }, + { + name: "int_slice_different_order", + x: []int{1, 2, 3}, + y: []int{3, 2, 1}, + want: true, + }, + { + name: "int_slice_different_elements", + x: []int{1, 2, 3}, + y: []int{1, 2, 4}, + want: false, + }, + { + name: "int_slice_different_lengths", + x: []int{1, 2, 3}, + y: []int{1, 2}, + want: false, + }, + { + name: "empty_slices", + x: []int{}, + y: []int{}, + want: true, + }, + { + name: "one_empty_slice", + x: []int{}, + y: []int{1}, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + switch v1 := tt.x.(type) { + case []string: + v2 := tt.y.([]string) + assert.Equalf(t, tt.want, EqualElements(v1, v2), "EqualElements(%v, %v)", v1, v2) + case []int: + v2 := tt.y.([]int) + assert.Equalf(t, tt.want, EqualElements(v1, v2), "EqualElements(%v, %v)", v1, v2) + default: + t.Errorf("Unsupported slice type in test case: %v", tt.name) + } + }) + } +} + +func Test_SimplifyHDRSlice(t *testing.T) { + tests := []struct { + name string + input []string + want []string + }{ + { + name: "contains_HDR", + input: []string{"HDR10", "HDR10+", "HDR"}, + want: []string{"HDR", "HDR", "HDR"}, + }, + { + name: "no_HDR", + input: []string{"SDR", "DV"}, + want: []string{"SDR", "DV"}, + }, + { + name: "empty_slice", + input: []string{}, + want: []string{}, + }, + { + name: "mixed_HDR_and_others", + input: []string{"HDR10", "DV", "SDR", "HDR10+"}, + want: []string{"HDR", "DV", "SDR", "HDR"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, SimplifyHDRSlice(tt.input), "SimplifyHDRSlice(%v)", tt.input) + }) + } +}