Repository: ecodeclub/ekit

Issue:
mapx: 支持 Merge 方法
**仅限中文**

### 使用场景
在实践中会出现将多个 map 的键值对合并为一个 map 的场景，所以需要考虑提供一个新的方法来支持这种操作。

### 行业分析
> 如果你知道有框架提供了类似功能，可以在这里描述，并且给出文档或者例子

### 可行方案
> 如果你有设计思路或者解决方案，请在这里提供。你可以提供多个方案，并且给出自己的选择

提供一个新的方法：
```go
func Merge[K comparable, V any](ms...map[K][V]) map[K][V] {
// ...
}
```

实现关键点：
- 返回一个全新的 map
- 要计算 ms 中的所有的键值对的数量来预估返回 map 的容量
- 即便 ms 的元素都是 nil，返回空 map 而不是 nil

### 其它
> 任何你觉得有利于解决问题的补充说明

### 你使用的是 ekit 哪个版本?

### 你设置的的 Go 环境?
> 上传 `go env` 的结果


Discussion:

Solution patch:
diff --git a/mapx/map.go b/mapx/map.go
index 78e838f..fc6640d 100644
--- a/mapx/map.go
+++ b/mapx/map.go
@@ -72,3 +72,31 @@ func ToMap[K comparable, V any](keys []K, values []V) (m map[K]V, err error) {
 	}
 	return
 }
+
+// Merge 如果同一个键对应多个值，则会使用最后一个值
+func Merge[K comparable, V any](ms ...map[K]V) map[K]V {
+	fc := func(val1, val2 V) V {
+		return val2
+	}
+	return MergeFunc(fc, ms...)
+}
+
+// MergeFunc 如果同一个键对应多个值，需要指定合并方式
+func MergeFunc[K comparable, V any](mergeFunc func(val1, val2 V) V, ms ...map[K]V) map[K]V {
+	total := 0
+	for _, m := range ms {
+		total += len(m)
+	}
+	merged := make(map[K]V, total)
+
+	for _, m := range ms {
+		for k, v := range m {
+			if value, ok := merged[k]; ok {
+				merged[k] = mergeFunc(value, v)
+			} else {
+				merged[k] = v
+			}
+		}
+	}
+	return merged
+}



Test patch:
diff --git a/mapx/map_example_test.go b/mapx/map_example_test.go
index 2832501..ecf2bba 100644
--- a/mapx/map_example_test.go
+++ b/mapx/map_example_test.go
@@ -62,3 +62,36 @@ func (m MockKey) Equals(key any) bool {
 	}
 	return true
 }
+
+func ExampleMerge() {
+	m1 := map[int]int{1: 1, 2: 2, 3: 3}
+	m2 := map[int]int{4: 4, 5: 5, 6: 6}
+	got := mapx.Merge(m1, m2)
+	fmt.Println(got)
+
+	m3 := map[int]int{1: 1, 2: 2, 3: 3}
+	m4 := map[int]int{1: 5, 2: 6, 3: 7}
+	got = mapx.Merge(m3, m4)
+	fmt.Println(got)
+
+	var m map[int]int
+	got = mapx.Merge(m)
+	fmt.Println(got == nil) // 不会返回 nil map
+
+	// Output:
+	// map[1:1 2:2 3:3 4:4 5:5 6:6]
+	// map[1:5 2:6 3:7]
+	// false
+}
+
+func ExampleMergeFunc() {
+	m1 := map[int]int{1: 1, 2: 2, 3: 3}
+	m2 := map[int]int{1: 2, 2: 3, 3: 4}
+	got := mapx.MergeFunc(func(val1, val2 int) int {
+		return val1 + val2
+	}, m1, m2)
+	fmt.Println(got)
+
+	// Output:
+	// map[1:3 2:5 3:7]
+}
diff --git a/mapx/map_test.go b/mapx/map_test.go
index 0d84701..7afc8cd 100644
--- a/mapx/map_test.go
+++ b/mapx/map_test.go
@@ -208,3 +208,54 @@ func TestToMap(t *testing.T) {
 		assert.Equal(t, c.result, result)
 	}
 }
+
+func TestMerge(t *testing.T) {
+	input1 := []map[int]int{
+		{1: 1, 2: 2},
+		{3: 3, 4: 4},
+	}
+	want1 := map[int]int{
+		1: 1, 2: 2, 3: 3, 4: 4,
+	}
+	got := Merge(input1...)
+	assert.Equal(t, want1, got)
+
+	input2 := []map[int]int{
+		{1: 1, 2: 2},
+		{3: 3, 4: 4},
+		{2: 3, 4: 5},
+	}
+	want2 := map[int]int{
+		1: 1, 2: 3, 3: 3, 4: 5,
+	}
+	got = Merge(input2...)
+	assert.Equal(t, want2, got)
+}
+
+func TestMergeFunc(t *testing.T) {
+	input1 := []map[int]int{
+		{1: 1, 2: 2},
+		{1: 3, 2: 4},
+		{3: 3, 4: 4},
+	}
+	want1 := map[int]int{
+		1: 4, 2: 6, 3: 3, 4: 4,
+	}
+	got := MergeFunc(func(val1, val2 int) int {
+		return val1 + val2
+	}, input1...)
+	assert.Equal(t, want1, got)
+
+	input2 := []map[int]int{
+		{1: 1, 2: 2},
+		{1: 3, 2: 4},
+		{3: 3, 4: 4},
+	}
+	want2 := map[int]int{
+		1: 3, 2: 8, 3: 3, 4: 4,
+	}
+	got = MergeFunc(func(val1, val2 int) int {
+		return val1 * val2
+	}, input2...)
+	assert.Equal(t, want2, got)
+}