From d59259bbe0bed4a80a7d813a2ca34c1f71dc748e Mon Sep 17 00:00:00 2001 From: Mickls <914541185@qq.com> Date: Fri, 9 Dec 2022 11:31:40 +0800 Subject: [PATCH] feat: A more reasonable IndexOf function (#66) --- slice/slice.go | 47 ++++++++++++++++++++++++++++++++++++++++----- slice/slice_test.go | 32 ++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/slice/slice.go b/slice/slice.go index 7117620..bf5f572 100644 --- a/slice/slice.go +++ b/slice/slice.go @@ -14,6 +14,13 @@ import ( "github.com/duke-git/lancet/v2/lancetconstraints" ) +// Create a static variable to store the hash table. +// This variable has the same lifetime as the entire program and can be shared by functions that are called more than once. +var ( + memoryHashMap = make(map[string]map[any]int) + memoryHashCounter = make(map[string]int) +) + // Contain check if the target value is in the slice or not func Contain[T comparable](slice []T, target T) bool { for _, item := range slice { @@ -832,14 +839,44 @@ func Without[T comparable](slice []T, items ...T) []T { return result } -// IndexOf returns the index at which the first occurrence of a item is found in a slice or return -1 if the item cannot be found. -func IndexOf[T comparable](slice []T, item T) int { - for i, v := range slice { - if v == item { - return i +// IndexOf returns the index at which the first occurrence of an item is found in a slice or return -1 if the item cannot be found. +func IndexOf[T comparable](arr []T, val T) int { + limit := 10 + // gets the hash value of the array as the key of the hash table. + key := fmt.Sprintf("%p", arr) + // determines whether the hash table is empty. If so, the hash table is created. + if memoryHashMap[key] == nil { + memoryHashMap[key] = make(map[any]int) + // iterate through the array, adding the value and index of each element to the hash table. + for i := len(arr) - 1; i >= 0; i-- { + memoryHashMap[key][arr[i]] = i } } + // update the hash table counter. + memoryHashCounter[key]++ + // use the hash table to find the specified value. If found, the index is returned. + if index, ok := memoryHashMap[key][val]; ok { + // calculate the memory usage of the hash table. + size := len(memoryHashMap) + // If the memory usage of the hash table exceeds the memory limit, the hash table with the lowest counter is cleared. + if size > limit { + var minKey string + var minVal int + for k, v := range memoryHashCounter { + if k == key { + continue + } + if minVal == 0 || v < minVal { + minKey = k + minVal = v + } + } + delete(memoryHashMap, minKey) + delete(memoryHashCounter, minKey) + } + return index + } return -1 } diff --git a/slice/slice_test.go b/slice/slice_test.go index be493ad..8417ba8 100644 --- a/slice/slice_test.go +++ b/slice/slice_test.go @@ -1,6 +1,7 @@ package slice import ( + "fmt" "math" "testing" @@ -669,8 +670,39 @@ func TestIndexOf(t *testing.T) { assert := internal.NewAssert(t, "TestIndexOf") arr := []string{"a", "a", "b", "c"} + key := fmt.Sprintf("%p", arr) assert.Equal(0, IndexOf(arr, "a")) assert.Equal(-1, IndexOf(arr, "d")) + assert.Equal(2, memoryHashCounter[key]) + + arr1 := []int{1, 2, 3, 4, 5} + key1 := fmt.Sprintf("%p", arr1) + assert.Equal(3, IndexOf(arr1, 4)) + assert.Equal(-1, IndexOf(arr1, 6)) + assert.Equal(2, memoryHashCounter[key1]) + + arr2 := []float64{1.1, 2.2, 3.3, 4.4, 5.5} + key2 := fmt.Sprintf("%p", arr2) + assert.Equal(2, IndexOf(arr2, 3.3)) + assert.Equal(3, IndexOf(arr2, 4.4)) + assert.Equal(-1, IndexOf(arr2, 6.6)) + assert.Equal(3, memoryHashCounter[key2]) + + for i := 0; i < 6; i++ { + a := []string{"a", "b", "c"} + IndexOf(a, "a") + IndexOf(a, "b") + } + minArr := []string{"c", "b", "a"} + minKey := fmt.Sprintf("%p", minArr) + assert.Equal(0, IndexOf(minArr, "c")) + + arr3 := []string{"q", "w", "e"} + key3 := fmt.Sprintf("%p", arr3) + assert.Equal(1, IndexOf(arr3, "w")) + assert.Equal(-1, IndexOf(arr3, "r")) + assert.Equal(2, memoryHashCounter[key3]) + assert.Equal(0, memoryHashCounter[minKey]) } func TestLastIndexOf(t *testing.T) {