From 1d50016bca0d332d4fe45058d6979c24c6a9beff Mon Sep 17 00:00:00 2001 From: Aria Date: Fri, 24 Jan 2025 17:28:01 +0330 Subject: [PATCH] feat: add (Min|Max)Index(By) (#569) --- README.md | 78 +++++++++++++++++++++++++++++++++++++ find.go | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++ find_test.go | 92 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 276 insertions(+) diff --git a/README.md b/README.md index 019de08a..da553420 100644 --- a/README.md +++ b/README.md @@ -224,11 +224,15 @@ Supported search helpers: - [FindDuplicates](#findduplicates) - [FindDuplicatesBy](#findduplicatesby) - [Min](#min) +- [MinIndex](#minindex) - [MinBy](#minby) +- [MinIndexBy](#minindexby) - [Earliest](#earliest) - [EarliestBy](#earliestby) - [Max](#max) +- [MaxIndex](#maxindex) - [MaxBy](#maxby) +- [MaxIndexBy](#maxindexby) - [Latest](#latest) - [LatestBy](#latestby) - [First](#first) @@ -2299,6 +2303,23 @@ min := lo.Min([]time.Duration{time.Second, time.Hour}) // 1s ``` +### MinIndex + +Search the minimum value of a collection and the index of the minimum value. + +Returns (zero value, -1) when the collection is empty. + +```go +min, index := lo.MinIndex([]int{1, 2, 3}) +// 1, 0 + +min, index := lo.MinIndex([]int{}) +// 0, -1 + +min, index := lo.MinIndex([]time.Duration{time.Second, time.Hour}) +// 1s, 0 +``` + ### MinBy Search the minimum value of a collection using the given comparison function. @@ -2319,6 +2340,26 @@ min := lo.MinBy([]string{}, func(item string, min string) bool { // "" ``` +### MinIndexBy + +Search the minimum value of a collection using the given comparison function and the index of the minimum value. + +If several values of the collection are equal to the smallest value, returns the first such value. + +Returns (zero value, -1) when the collection is empty. + +```go +min, index := lo.MinIndexBy([]string{"s1", "string2", "s3"}, func(item string, min string) bool { + return len(item) < len(min) +}) +// "s1", 0 + +min, index := lo.MinIndexBy([]string{}, func(item string, min string) bool { + return len(item) < len(min) +}) +// "", -1 +``` + ### Earliest Search the minimum time.Time of a collection. @@ -2364,6 +2405,23 @@ max := lo.Max([]time.Duration{time.Second, time.Hour}) // 1h ``` +### MaxIndex + +Search the maximum value of a collection and the index of the maximum value. + +Returns (zero value, -1) when the collection is empty. + +```go +max, index := lo.MaxIndex([]int{1, 2, 3}) +// 3, 2 + +max, index := lo.MaxIndex([]int{}) +// 0, -1 + +max, index := lo.MaxIndex([]time.Duration{time.Second, time.Hour}) +// 1h, 1 +``` + ### MaxBy Search the maximum value of a collection using the given comparison function. @@ -2384,6 +2442,26 @@ max := lo.MaxBy([]string{}, func(item string, max string) bool { // "" ``` +### MaxIndexBy + +Search the maximum value of a collection using the given comparison function and the index of the maximum value. + +If several values of the collection are equal to the greatest value, returns the first such value. + +Returns (zero value, -1) when the collection is empty. + +```go +max, index := lo.MaxIndexBy([]string{"string1", "s2", "string3"}, func(item string, max string) bool { + return len(item) > len(max) +}) +// "string1", 0 + +max, index := lo.MaxIndexBy([]string{}, func(item string, max string) bool { + return len(item) > len(max) +}) +// "", -1 +``` + ### Latest Search the maximum time.Time of a collection. diff --git a/find.go b/find.go index 59c23460..d251f4ad 100644 --- a/find.go +++ b/find.go @@ -241,6 +241,32 @@ func Min[T constraints.Ordered](collection []T) T { return min } +// MinIndex search the minimum value of a collection and the index of the minimum value. +// Returns (zero value, -1) when the collection is empty. +func MinIndex[T constraints.Ordered](collection []T) (T, int) { + var ( + min T + index int + ) + + if len(collection) == 0 { + return min, -1 + } + + min = collection[0] + + for i := 1; i < len(collection); i++ { + item := collection[i] + + if item < min { + min = item + index = i + } + } + + return min, index +} + // MinBy search the minimum value of a collection using the given comparison function. // If several values of the collection are equal to the smallest value, returns the first such value. // Returns zero value when the collection is empty. @@ -264,6 +290,33 @@ func MinBy[T any](collection []T, comparison func(a T, b T) bool) T { return min } +// MinIndexBy search the minimum value of a collection using the given comparison function and the index of the minimum value. +// If several values of the collection are equal to the smallest value, returns the first such value. +// Returns (zero value, -1) when the collection is empty. +func MinIndexBy[T any](collection []T, comparison func(a T, b T) bool) (T, int) { + var ( + min T + index int + ) + + if len(collection) == 0 { + return min, -1 + } + + min = collection[0] + + for i := 1; i < len(collection); i++ { + item := collection[i] + + if comparison(item, min) { + min = item + index = i + } + } + + return min, index +} + // Earliest search the minimum time.Time of a collection. // Returns zero value when the collection is empty. func Earliest(times ...time.Time) time.Time { @@ -332,6 +385,32 @@ func Max[T constraints.Ordered](collection []T) T { return max } +// MaxIndex searches the maximum value of a collection and the index of the maximum value. +// Returns (zero value, -1) when the collection is empty. +func MaxIndex[T constraints.Ordered](collection []T) (T, int) { + var ( + max T + index int + ) + + if len(collection) == 0 { + return max, -1 + } + + max = collection[0] + + for i := 1; i < len(collection); i++ { + item := collection[i] + + if item > max { + max = item + index = i + } + } + + return max, index +} + // MaxBy search the maximum value of a collection using the given comparison function. // If several values of the collection are equal to the greatest value, returns the first such value. // Returns zero value when the collection is empty. @@ -355,6 +434,33 @@ func MaxBy[T any](collection []T, comparison func(a T, b T) bool) T { return max } +// MaxIndexBy search the maximum value of a collection using the given comparison function and the index of the maximum value. +// If several values of the collection are equal to the greatest value, returns the first such value. +// Returns (zero value, -1) when the collection is empty. +func MaxIndexBy[T any](collection []T, comparison func(a T, b T) bool) (T, int) { + var ( + max T + index int + ) + + if len(collection) == 0 { + return max, -1 + } + + max = collection[0] + + for i := 1; i < len(collection); i++ { + item := collection[i] + + if comparison(item, max) { + max = item + index = i + } + } + + return max, index +} + // Latest search the maximum time.Time of a collection. // Returns zero value when the collection is empty. func Latest(times ...time.Time) time.Time { diff --git a/find_test.go b/find_test.go index b1533997..6482cc98 100644 --- a/find_test.go +++ b/find_test.go @@ -304,6 +304,28 @@ func TestMin(t *testing.T) { is.Equal(result4, 0) } +func TestMinIndex(t *testing.T) { + t.Parallel() + is := assert.New(t) + + result1, index1 := MinIndex([]int{1, 2, 3}) + result2, index2 := MinIndex([]int{3, 2, 1}) + result3, index3 := MinIndex([]time.Duration{time.Second, time.Minute, time.Hour}) + result4, index4 := MinIndex([]int{}) + + is.Equal(result1, 1) + is.Equal(index1, 0) + + is.Equal(result2, 1) + is.Equal(index2, 2) + + is.Equal(result3, time.Second) + is.Equal(index3, 0) + + is.Equal(result4, 0) + is.Equal(index4, -1) +} + func TestMinBy(t *testing.T) { t.Parallel() is := assert.New(t) @@ -323,6 +345,30 @@ func TestMinBy(t *testing.T) { is.Equal(result3, "") } +func TestMinIndexBy(t *testing.T) { + t.Parallel() + is := assert.New(t) + + result1, index1 := MinIndexBy([]string{"s1", "string2", "s3"}, func(item string, min string) bool { + return len(item) < len(min) + }) + result2, index2 := MinIndexBy([]string{"string1", "string2", "s3"}, func(item string, min string) bool { + return len(item) < len(min) + }) + result3, index3 := MinIndexBy([]string{}, func(item string, min string) bool { + return len(item) < len(min) + }) + + is.Equal(result1, "s1") + is.Equal(index1, 0) + + is.Equal(result2, "s3") + is.Equal(index2, 2) + + is.Equal(result3, "") + is.Equal(index3, -1) +} + func TestEarliest(t *testing.T) { t.Parallel() is := assert.New(t) @@ -377,6 +423,28 @@ func TestMax(t *testing.T) { is.Equal(result4, 0) } +func TestMaxIndex(t *testing.T) { + t.Parallel() + is := assert.New(t) + + result1, index1 := MaxIndex([]int{1, 2, 3}) + result2, index2 := MaxIndex([]int{3, 2, 1}) + result3, index3 := MaxIndex([]time.Duration{time.Second, time.Minute, time.Hour}) + result4, index4 := MaxIndex([]int{}) + + is.Equal(result1, 3) + is.Equal(index1, 2) + + is.Equal(result2, 3) + is.Equal(index2, 0) + + is.Equal(result3, time.Hour) + is.Equal(index3, 2) + + is.Equal(result4, 0) + is.Equal(index4, -1) +} + func TestMaxBy(t *testing.T) { t.Parallel() is := assert.New(t) @@ -396,6 +464,30 @@ func TestMaxBy(t *testing.T) { is.Equal(result3, "") } +func TestMaxIndexBy(t *testing.T) { + t.Parallel() + is := assert.New(t) + + result1, index1 := MaxIndexBy([]string{"s1", "string2", "s3"}, func(item string, max string) bool { + return len(item) > len(max) + }) + result2, index2 := MaxIndexBy([]string{"string1", "string2", "s3"}, func(item string, max string) bool { + return len(item) > len(max) + }) + result3, index3 := MaxIndexBy([]string{}, func(item string, max string) bool { + return len(item) > len(max) + }) + + is.Equal(result1, "string2") + is.Equal(index1, 1) + + is.Equal(result2, "string1") + is.Equal(index2, 0) + + is.Equal(result3, "") + is.Equal(index3, -1) +} + func TestLatest(t *testing.T) { t.Parallel() is := assert.New(t)