Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keys+Values: accept multiple maps (vaargs) - Adding UniqKeys+UniqValues #503

Merged
merged 24 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 55 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,11 @@ Supported helpers for slices:
Supported helpers for maps:

- [Keys](#keys)
- [HasKey](#HasKey)
- [UniqKeys](#uniqKeys)
- [HasKey](#hasKey)
samber marked this conversation as resolved.
Show resolved Hide resolved
- [ValueOr](#valueor)
- [Values](#values)
- [UniqValues](#uniqValues)
samber marked this conversation as resolved.
Show resolved Hide resolved
- [PickBy](#pickby)
- [PickByKeys](#pickbykeys)
- [PickByValues](#pickbyvalues)
Expand Down Expand Up @@ -1038,15 +1040,39 @@ result = lo.Splice([]string{"a", "b"}, 42, "1", "2")

### Keys
samber marked this conversation as resolved.
Show resolved Hide resolved

Creates an array of the map keys.
Creates a slice of the map keys.
> [!NOTE]
> The order of the keys isn't guaranteed to be sorted for performance reason, so sort the output slice if needed.

```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 the unique map keys.
> [!NOTE]
> The order of the keys isn't guaranteed to be sorted for performance reason, so sort the output slice if needed.

```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/RyIE6Pb5dVS)]

### HasKey

Returns whether the given key exists.
Expand All @@ -1064,14 +1090,41 @@ exists := lo.HasKey(map[string]int{"foo": 1, "bar": 2}, "baz")
### Values

Creates an array of the map values.
> [!NOTE]
> The order of the values isn't guaranteed to be sorted for performance reason, so sort the output slice if needed.

```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 the unique map values.
> [!NOTE]
> The order of the values isn't guaranteed to be sorted for performance reason, so sort the output slice if needed.

```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}
```

[[play](https://go.dev/play/p/RxsJbnaJn3I)]

### ValueOr

Returns the value of the given key or the fallback value if the key is not present.
Expand Down
57 changes: 50 additions & 7 deletions map.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
samber marked this conversation as resolved.
Show resolved Hide resolved
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)
samber marked this conversation as resolved.
Show resolved Hide resolved
}
}

return result
}

// UniqKeys creates an array of unique keys in the map.
// Play: https://go.dev/play/p/RyIE6Pb5dVS
func UniqKeys[K comparable, V any](in ...map[K]V) []K {
Copy link
Contributor

@d-enk d-enk Aug 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may be more effective

seen := make(map[K]struct{})
	
for i := range in {
  for k := range in[i] {
	seen[k] = struct{}{}
  }
}

result := make([]K, 0, len(seen))

for k := range seen {
	result = append(result, k)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similarly UniqValues

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with this version, the order is not guarantee

IMO, we would need a new helper (UniqKeysUnordered) ?

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
Expand All @@ -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))
samber marked this conversation as resolved.
Show resolved Hide resolved

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/RxsJbnaJn3I
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
Expand Down
32 changes: 28 additions & 4 deletions map_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
Expand Down
76 changes: 76 additions & 0 deletions map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,44 @@ func TestKeys(t *testing.T) {

r1 := Keys(map[string]int{"foo": 1, "bar": 2})
sort.Strings(r1)
samber marked this conversation as resolved.
Show resolved Hide resolved
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) {
Expand All @@ -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(r1)
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(r1)
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) {
Expand Down