-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
proposal: x/exp/maps: add Merge #52806
Comments
It seems like Merge is a variadic repeated Copy into a new map? This doesn't seem like it comes up enough to put into the standard library. |
This proposal has been added to the active column of the proposals project |
Anecdotally, I've had to implement this in the vast majority of go projects I've worked on - it comes up pretty often. In terms of data, I cobbled together this sourcegraph search, which has a couple of false positives, but indicates that a function like this is pretty fairly implemented. In terms of variadicity (is that a word?) almost all of the time i'll just implement merge on two maps, eg func Merge[M ~map[K]V, K comparable, V any](this, that M) M {
merged := make(M, len(this) + len(that))
for _, m := range maps {
for key, val := range m {
merged[key] = val
}
}
return merged
} but i figured in this case that adding the variadicity was a nice usability improvement, and made the method more usable in a stdlib use case. I know that we don't implement the Go Standard Library based on what other languages are doing, but it's a point of reference that methods like this are useful enough to go in the stdlibs of a lot of other languages (see: Ruby's |
I feel like a map merge, as opposed to a set merge, would need a way to define the way of resolving key conflicts. That is, if maps (As an aside, a few of the maps in the Sourcegraph search seem to be |
Given the prevalence of the package maps
func Merge[M ~map[K]V, K comparable, V any](maps ...M) M {
fullCap := 0
for _, m := range maps {
fullCap += len(m)
}
merged := make(M, fullCap)
for _, m := range maps {
for key, val := range m {
merged[key] = val
}
}
return merged
}
func MergeFunc[M ~map[K]V, K comparable, V any](conflictFunc func(V, V) V, maps ...M) M {
fullCap := 0
for _, m := range maps {
fullCap += len(m)
}
merged := make(M, fullCap)
for _, m := range maps {
for key, val := range m {
if v, ok := merged[key]; ok {
merged[key] = conflictFunc(v, val)
continue
}
merged[key] = val
}
}
return merged
} This way, we could have something like: m1 := map[string]int{"a": 1, "b": 2, "c": 3}
m2 := map[string]int{"b": 2, "e": 0}
MergeFunc(func(a, b int) int { return a + b }, m1, m2) // => map[string]int{"a": 1, "b": 4, "c": 3, "e": 0} Admittedly this sort of breaks the established pattern of having the funcs be the last argument, as the maps argument is variadic, but i also don't want to nail down an implementation prematurely, I'm more interested in establishing if other people actually think that this is a good idea |
It's still unclear why we need to add Merge when we have Copy. Instead of Merge(m1, m2) you can do
I didn't look super closely at the sourcegraph results but skimming through the first one that jumped out at me was just Copy (MergeInto), not Merge. |
oh shivers - i had I'm a dummy, i'm gonna close this issue. |
This proposal has been declined as retracted. |
The new
x/exp/maps
package covers most of the things i really wanted generics for with maps (🙏Keys
andValues
), but the other thing i find myself doing with generic maps a lot is merging two (or more) maps into one larger map. IMO it's a common enough task to get integrated into themaps
package.My proposed implentation is:
Happy for this implementation to be changed, it's what i came up with in about 5 minutes, i'm sure it can be improved 😅
It'd be probably worth calling out in the docs, like we do for
Copy
, that this is only a shallow merge and that it won't traverse deeper map map elements to try to merge them.The text was updated successfully, but these errors were encountered: