From d8e5707487b544d8e574621faa0c9cadc3b7b91f Mon Sep 17 00:00:00 2001 From: shivam-bhalla <88187021+shivamrazorpay@users.noreply.github.com> Date: Mon, 29 Jul 2024 09:07:41 +0530 Subject: [PATCH] Keys+Values: accept multiple maps (vaargs) - Adding UniqKeys+UniqValues (#503) * added Keys: accept multiple maps (vaargs) * added test:review comment fix * added sort before asserting :review comment fix * added sort before asserting :review comment fix * output unique keys * added map functions" * added map_test.go and README.md * added anchor in README.md * added breakline in README.md * fix README.md * fix README.md and added test * fix comments * fix comments * fix comments * fix comments * fix comments * Update README.md * small typo * Update README.md * Update README.md * Update map.go * Update map_test.go --------- Co-authored-by: Samuel Berthe --- README.md | 53 +++++++++++++++++++++++++++++-- map.go | 57 +++++++++++++++++++++++++++++----- map_example_test.go | 32 ++++++++++++++++--- map_test.go | 76 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 205 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 3751b339..c5b44297 100644 --- a/README.md +++ b/README.md @@ -125,9 +125,11 @@ Supported helpers for slices: Supported helpers for maps: - [Keys](#keys) -- [HasKey](#HasKey) +- [UniqKeys](#uniqkeys) +- [HasKey](#haskey) - [ValueOr](#valueor) - [Values](#values) +- [UniqValues](#uniqvalues) - [PickBy](#pickby) - [PickByKeys](#pickbykeys) - [PickByValues](#pickbyvalues) @@ -1040,15 +1042,37 @@ result = lo.Splice([]string{"a", "b"}, 42, "1", "2") ### Keys -Creates an array of the map keys. +Creates a slice of the map keys. + +Use the UniqKeys variant to deduplicate common keys. ```go keys := lo.Keys(map[string]int{"foo": 1, "bar": 2}) // []string{"foo", "bar"} + +keys := lo.Keys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3}) +// []string{"foo", "bar", "baz"} + +keys := lo.Keys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"bar": 3}) +// []string{"foo", "bar", "bar"} ``` [[play](https://go.dev/play/p/Uu11fHASqrU)] +### UniqKeys + +Creates an array of unique map keys. + +```go +keys := lo.Keys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3}) +// []string{"foo", "bar", "baz"} + +keys := lo.Keys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"bar": 3}) +// []string{"foo", "bar"} +``` + +[[play](https://go.dev/play/p/TPKAb6ILdHk)] + ### HasKey Returns whether the given key exists. @@ -1067,13 +1091,38 @@ exists := lo.HasKey(map[string]int{"foo": 1, "bar": 2}, "baz") Creates an array of the map values. +Use the UniqValues variant to deduplicate common values. + ```go values := lo.Values(map[string]int{"foo": 1, "bar": 2}) // []int{1, 2} + +values := lo.Values(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3}) +// []int{1, 2, 3} + +values := lo.Values(map[string]int{"foo": 1, "bar": 2}, map[string]int{"bar": 2}) +// []int{1, 2, 2} ``` [[play](https://go.dev/play/p/nnRTQkzQfF6)] +### UniqValues + +Creates an array of unique map values. + +```go +values := lo.UniqValues(map[string]int{"foo": 1, "bar": 2}) +// []int{1, 2} + +values := lo.UniqValues(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3}) +// []int{1, 2, 3} + +values := lo.UniqValues(map[string]int{"foo": 1, "bar": 2}, map[string]int{"bar": 2}) +// []int{1, 2} +``` + +[[play](https://go.dev/play/p/nf6bXMh7rM3)] + ### ValueOr Returns the value of the given key or the fallback value if the key is not present. diff --git a/map.go b/map.go index 59343bc1..7d53b94e 100644 --- a/map.go +++ b/map.go @@ -2,11 +2,32 @@ package lo // Keys creates an array of the map keys. // Play: https://go.dev/play/p/Uu11fHASqrU -func Keys[K comparable, V any](in map[K]V) []K { - result := make([]K, 0, len(in)) +func Keys[K comparable, V any](in ...map[K]V) []K { + result := make([]K, 0) - for k := range in { - result = append(result, k) + for i := range in { + for k := range in[i] { + result = append(result, k) + } + } + + return result +} + +// UniqKeys creates an array of unique keys in the map. +// Play: https://go.dev/play/p/TPKAb6ILdHk +func UniqKeys[K comparable, V any](in ...map[K]V) []K { + seen := make(map[K]struct{}) + result := make([]K, 0) + + for i := range in { + for k := range in[i] { + if _, exists := seen[k]; exists { + continue + } + seen[k] = struct{}{} + result = append(result, k) + } } return result @@ -21,11 +42,33 @@ func HasKey[K comparable, V any](in map[K]V, key K) bool { // Values creates an array of the map values. // Play: https://go.dev/play/p/nnRTQkzQfF6 -func Values[K comparable, V any](in map[K]V) []V { +func Values[K comparable, V any](in ...map[K]V) []V { result := make([]V, 0, len(in)) - for k := range in { - result = append(result, in[k]) + for i := range in { + for k := range in[i] { + result = append(result, in[i][k]) + } + } + + return result +} + +// UniqValues creates an array of unique values in the map. +// Play: https://go.dev/play/p/nf6bXMh7rM3 +func UniqValues[K comparable, V comparable](in ...map[K]V) []V { + seen := make(map[V]struct{}) + result := make([]V, 0) + + for i := range in { + for k := range in[i] { + val := in[i][k] + if _, exists := seen[val]; exists { + continue + } + seen[val] = struct{}{} + result = append(result, val) + } } return result diff --git a/map_example_test.go b/map_example_test.go index aebca8fd..3a9b66ab 100644 --- a/map_example_test.go +++ b/map_example_test.go @@ -9,20 +9,44 @@ import ( func ExampleKeys() { kv := map[string]int{"foo": 1, "bar": 2} + kv2 := map[string]int{"baz": 3} - result := Keys(kv) + result := Keys(kv, kv2) + sort.Strings(result) + fmt.Printf("%v", result) + // Output: [bar baz foo] - sort.StringSlice(result).Sort() +} + +func ExampleUniqKeys() { + kv := map[string]int{"foo": 1, "bar": 2} + kv2 := map[string]int{"bar": 3} + + result := UniqKeys(kv, kv2) + sort.Strings(result) fmt.Printf("%v", result) // Output: [bar foo] + } func ExampleValues() { kv := map[string]int{"foo": 1, "bar": 2} + kv2 := map[string]int{"baz": 3} + + result := Values(kv, kv2) + + sort.Ints(result) + fmt.Printf("%v", result) + // Output: [1 2 3] +} + +func ExampleUniqValues() { + kv := map[string]int{"foo": 1, "bar": 2} + kv2 := map[string]int{"baz": 2} - result := Values(kv) + result := UniqValues(kv, kv2) - sort.IntSlice(result).Sort() + sort.Ints(result) fmt.Printf("%v", result) // Output: [1 2] } diff --git a/map_test.go b/map_test.go index c683cbe1..25319811 100644 --- a/map_test.go +++ b/map_test.go @@ -15,8 +15,44 @@ func TestKeys(t *testing.T) { r1 := Keys(map[string]int{"foo": 1, "bar": 2}) sort.Strings(r1) + is.Equal(r1, []string{"bar", "foo"}) + + r2 := Keys(map[string]int{}) + is.Empty(r2) + + r3 := Keys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3}) + sort.Strings(r3) + is.Equal(r3, []string{"bar", "baz", "foo"}) + + r4 := Keys[string, int]() + is.Equal(r4, []string{}) + r5 := Keys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"bar": 3}) + sort.Strings(r5) + is.Equal(r5, []string{"bar", "bar", "foo"}) +} + +func TestUniqKeys(t *testing.T) { + t.Parallel() + is := assert.New(t) + + r1 := UniqKeys(map[string]int{"foo": 1, "bar": 2}) + sort.Strings(r1) is.Equal(r1, []string{"bar", "foo"}) + + r2 := UniqKeys(map[string]int{}) + is.Empty(r2) + + r3 := UniqKeys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3}) + sort.Strings(r3) + is.Equal(r3, []string{"bar", "baz", "foo"}) + + r4 := UniqKeys[string, int]() + is.Equal(r4, []string{}) + + r5 := UniqKeys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"foo": 1, "bar": 3}) + sort.Strings(r5) + is.Equal(r5, []string{"bar", "foo"}) } func TestHasKey(t *testing.T) { @@ -36,8 +72,48 @@ func TestValues(t *testing.T) { r1 := Values(map[string]int{"foo": 1, "bar": 2}) sort.Ints(r1) + is.Equal(r1, []int{1, 2}) + r2 := Values(map[string]int{}) + is.Empty(r2) + + r3 := Values(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3}) + sort.Ints(r3) + is.Equal(r3, []int{1, 2, 3}) + + r4 := Values[string, int]() + is.Equal(r4, []int{}) + + r5 := Values(map[string]int{"foo": 1, "bar": 2}, map[string]int{"foo": 1, "bar": 3}) + sort.Ints(r5) + is.Equal(r5, []int{1, 1, 2, 3}) +} + +func TestUniqValues(t *testing.T) { + t.Parallel() + is := assert.New(t) + + r1 := UniqValues(map[string]int{"foo": 1, "bar": 2}) + sort.Ints(r1) is.Equal(r1, []int{1, 2}) + + r2 := UniqValues(map[string]int{}) + is.Empty(r2) + + r3 := UniqValues(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3}) + sort.Ints(r3) + is.Equal(r3, []int{1, 2, 3}) + + r4 := UniqValues[string, int]() + is.Equal(r4, []int{}) + + r5 := UniqValues(map[string]int{"foo": 1, "bar": 2}, map[string]int{"foo": 1, "bar": 3}) + sort.Ints(r5) + is.Equal(r5, []int{1, 2, 3}) + + r6 := UniqValues(map[string]int{"foo": 1, "bar": 1}, map[string]int{"foo": 1, "bar": 3}) + sort.Ints(r6) + is.Equal(r6, []int{1, 3}) } func TestValueOr(t *testing.T) {