From d93dd9a2d7d22037f0c301f8d91ac79f582477c4 Mon Sep 17 00:00:00 2001 From: Samuel Berthe Date: Thu, 4 Jul 2024 08:32:21 +0200 Subject: [PATCH 1/3] fix: Dockerfile to reduce vulnerabilities (#487) The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-DEBIAN12-PYTHON311-3325304 - https://snyk.io/vuln/SNYK-DEBIAN12-PYTHON311-3325304 - https://snyk.io/vuln/SNYK-DEBIAN12-PYTHON311-5853785 - https://snyk.io/vuln/SNYK-DEBIAN12-SYSTEMD-6277507 - https://snyk.io/vuln/SNYK-DEBIAN12-SYSTEMD-6277507 Co-authored-by: snyk-bot --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9fd99660..5eab431a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ -FROM golang:1.21.10 +FROM golang:1.21.12 WORKDIR /go/src/github.com/samber/lo From 34537d5291fae806e9f19a7ebeff46ebcc7ac7a8 Mon Sep 17 00:00:00 2001 From: Timur Polukeev Date: Sat, 13 Jul 2024 16:41:15 +0200 Subject: [PATCH 2/3] feat: adding EarliestBy and LatestBy functions (#489) --- README.md | 36 ++++++++++++++++++++++++++++++++++++ find.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ find_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) 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) From 9e343973a4629da234a8eca0aad725a7e888328c Mon Sep 17 00:00:00 2001 From: Mihir Gandhi Date: Mon, 15 Jul 2024 22:46:03 +0530 Subject: [PATCH 3/3] fix: chunk memory leak (#491) --- slice.go | 2 +- slice_test.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/slice.go b/slice.go index bf69d3b0..38b50e82 100644 --- a/slice.go +++ b/slice.go @@ -182,7 +182,7 @@ func Chunk[T any, Slice ~[]T](collection Slice, size int) []Slice { if last > len(collection) { last = len(collection) } - result = append(result, collection[i*size:last]) + result = append(result, collection[i*size:last:last]) } return result diff --git a/slice_test.go b/slice_test.go index 0917e171..f13e129c 100644 --- a/slice_test.go +++ b/slice_test.go @@ -235,6 +235,12 @@ func TestChunk(t *testing.T) { allStrings := myStrings{"", "foo", "bar"} nonempty := Chunk(allStrings, 2) is.IsType(nonempty[0], allStrings, "type preserved") + + // appending to a chunk should not affect original array + originalArray := []int{0, 1, 2, 3, 4, 5} + result5 := Chunk(originalArray, 2) + result5[0] = append(result5[0], 6) + is.Equal(originalArray, []int{0, 1, 2, 3, 4, 5}) } func TestPartitionBy(t *testing.T) {