diff --git a/README.md b/README.md index efa83fb2..d756cd80 100644 --- a/README.md +++ b/README.md @@ -222,9 +222,11 @@ Supported search helpers: - [Min](#min) - [MinBy](#minby) - [Earliest](#earliest) +- [EarliestBy](#earliestby) - [Max](#max) - [MaxBy](#maxby) - [Latest](#latest) +- [LatestBy](#latestby) - [First](#first) - [FirstOrEmpty](#FirstOrEmpty) - [FirstOr](#FirstOr) @@ -2260,6 +2262,23 @@ earliest := lo.Earliest(time.Now(), time.Time{}) // 0001-01-01 00:00:00 +0000 UTC ``` +### EarliestBy + +Search the minimum time.Time of a collection using the given iteratee function. + +Returns zero value when the collection is empty. + +```go +type foo struct { + bar time.Time +} + +earliest := lo.EarliestBy([]foo{{time.Now()}, {}}, func(i foo) time.Time { + return i.bar +}) +// {bar:{2023-04-01 01:02:03 +0000 UTC}} +``` + ### Max Search the maximum value of a collection. @@ -2308,6 +2327,23 @@ latest := lo.Latest([]time.Time{time.Now(), time.Time{}}) // 2023-04-01 01:02:03 +0000 UTC ``` +### LatestBy + +Search the maximum time.Time of a collection using the given iteratee function. + +Returns zero value when the collection is empty. + +```go +type foo struct { + bar time.Time +} + +latest := lo.LatestBy([]foo{{time.Now()}, {}}, func(i foo) time.Time { + return i.bar +}) +// {bar:{2023-04-01 01:02:03 +0000 UTC}} +``` + ### First Returns the first element of a collection and check for availability of the first element. diff --git a/find.go b/find.go index ff708b5e..ea577ae2 100644 --- a/find.go +++ b/find.go @@ -286,6 +286,30 @@ func Earliest(times ...time.Time) time.Time { return min } +// EarliestBy search the minimum time.Time of a collection using the given iteratee function. +// Returns zero value when the collection is empty. +func EarliestBy[T any](collection []T, iteratee func(item T) time.Time) T { + var earliest T + + if len(collection) == 0 { + return earliest + } + + earliest = collection[0] + earliestTime := iteratee(collection[0]) + + for i := 1; i < len(collection); i++ { + itemTime := iteratee(collection[i]) + + if itemTime.Before(earliestTime) { + earliest = collection[i] + earliestTime = itemTime + } + } + + return earliest +} + // Max searches the maximum value of a collection. // Returns zero value when the collection is empty. func Max[T constraints.Ordered](collection []T) T { @@ -353,6 +377,30 @@ func Latest(times ...time.Time) time.Time { return max } +// LatestBy search the maximum time.Time of a collection using the given iteratee function. +// Returns zero value when the collection is empty. +func LatestBy[T any](collection []T, iteratee func(item T) time.Time) T { + var latest T + + if len(collection) == 0 { + return latest + } + + latest = collection[0] + latestTime := iteratee(collection[0]) + + for i := 1; i < len(collection); i++ { + itemTime := iteratee(collection[i]) + + if itemTime.After(latestTime) { + latest = collection[i] + latestTime = itemTime + } + } + + return latest +} + // First returns the first element of a collection and check for availability of the first element. func First[T any](collection []T) (T, bool) { length := len(collection) diff --git a/find_test.go b/find_test.go index c5adafa4..b1533997 100644 --- a/find_test.go +++ b/find_test.go @@ -336,6 +336,32 @@ func TestEarliest(t *testing.T) { is.Equal(result2, time.Time{}) } +func TestEarliestBy(t *testing.T) { + t.Parallel() + is := assert.New(t) + + type foo struct { + bar time.Time + } + + t1 := time.Now() + t2 := t1.Add(time.Hour) + t3 := t1.Add(-time.Hour) + result1 := EarliestBy([]foo{{t1}, {t2}, {t3}}, func(i foo) time.Time { + return i.bar + }) + result2 := EarliestBy([]foo{{t1}}, func(i foo) time.Time { + return i.bar + }) + result3 := EarliestBy([]foo{}, func(i foo) time.Time { + return i.bar + }) + + is.Equal(result1, foo{t3}) + is.Equal(result2, foo{t1}) + is.Equal(result3, foo{}) +} + func TestMax(t *testing.T) { t.Parallel() is := assert.New(t) @@ -383,6 +409,32 @@ func TestLatest(t *testing.T) { is.Equal(result2, time.Time{}) } +func TestLatestBy(t *testing.T) { + t.Parallel() + is := assert.New(t) + + type foo struct { + bar time.Time + } + + t1 := time.Now() + t2 := t1.Add(time.Hour) + t3 := t1.Add(-time.Hour) + result1 := LatestBy([]foo{{t1}, {t2}, {t3}}, func(i foo) time.Time { + return i.bar + }) + result2 := LatestBy([]foo{{t1}}, func(i foo) time.Time { + return i.bar + }) + result3 := LatestBy([]foo{}, func(i foo) time.Time { + return i.bar + }) + + is.Equal(result1, foo{t2}) + is.Equal(result2, foo{t1}) + is.Equal(result3, foo{}) +} + func TestFirst(t *testing.T) { t.Parallel() is := assert.New(t)