diff --git a/container/gmap/gmap_hash_any_any_map.go b/container/gmap/gmap_hash_any_any_map.go index fdfe9aa63c9..37da5173162 100644 --- a/container/gmap/gmap_hash_any_any_map.go +++ b/container/gmap/gmap_hash_any_any_map.go @@ -13,6 +13,7 @@ import ( "github.com/gogf/gf/v2/internal/json" "github.com/gogf/gf/v2/internal/rwmutex" "github.com/gogf/gf/v2/util/gconv" + "reflect" ) // AnyAnyMap wraps map type `map[interface{}]interface{}` and provides more map features. @@ -535,3 +536,28 @@ func (m *AnyAnyMap) IsSubOf(other *AnyAnyMap) bool { } return true } + +// Diff compares current map `m` with map `other` and returns their different keys. +// The returned `addedKeys` are the keys that are in map `m` but not in map `other`. +// The returned `removedKeys` are the keys that are in map `other` but not in map `m`. +// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`). +func (m *AnyAnyMap) Diff(other *AnyAnyMap) (addedKeys, removedKeys, updatedKeys []interface{}) { + m.mu.RLock() + defer m.mu.RUnlock() + other.mu.RLock() + defer other.mu.RUnlock() + + for key := range m.data { + if _, ok := other.data[key]; !ok { + removedKeys = append(removedKeys, key) + } else if !reflect.DeepEqual(m.data[key], other.data[key]) { + updatedKeys = append(updatedKeys, key) + } + } + for key := range other.data { + if _, ok := m.data[key]; !ok { + addedKeys = append(addedKeys, key) + } + } + return +} diff --git a/container/gmap/gmap_hash_int_any_map.go b/container/gmap/gmap_hash_int_any_map.go index 1faed272e21..64d6877565c 100644 --- a/container/gmap/gmap_hash_int_any_map.go +++ b/container/gmap/gmap_hash_int_any_map.go @@ -14,6 +14,7 @@ import ( "github.com/gogf/gf/v2/internal/json" "github.com/gogf/gf/v2/internal/rwmutex" "github.com/gogf/gf/v2/util/gconv" + "reflect" ) // IntAnyMap implements map[int]interface{} with RWMutex that has switch. @@ -536,3 +537,28 @@ func (m *IntAnyMap) IsSubOf(other *IntAnyMap) bool { } return true } + +// Diff compares current map `m` with map `other` and returns their different keys. +// The returned `addedKeys` are the keys that are in map `m` but not in map `other`. +// The returned `removedKeys` are the keys that are in map `other` but not in map `m`. +// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`). +func (m *IntAnyMap) Diff(other *IntAnyMap) (addedKeys, removedKeys, updatedKeys []int) { + m.mu.RLock() + defer m.mu.RUnlock() + other.mu.RLock() + defer other.mu.RUnlock() + + for key := range m.data { + if _, ok := other.data[key]; !ok { + removedKeys = append(removedKeys, key) + } else if !reflect.DeepEqual(m.data[key], other.data[key]) { + updatedKeys = append(updatedKeys, key) + } + } + for key := range other.data { + if _, ok := m.data[key]; !ok { + addedKeys = append(addedKeys, key) + } + } + return +} diff --git a/container/gmap/gmap_hash_int_int_map.go b/container/gmap/gmap_hash_int_int_map.go index f63c1f0675c..5fb8c440ab3 100644 --- a/container/gmap/gmap_hash_int_int_map.go +++ b/container/gmap/gmap_hash_int_int_map.go @@ -506,3 +506,28 @@ func (m *IntIntMap) IsSubOf(other *IntIntMap) bool { } return true } + +// Diff compares current map `m` with map `other` and returns their different keys. +// The returned `addedKeys` are the keys that are in map `m` but not in map `other`. +// The returned `removedKeys` are the keys that are in map `other` but not in map `m`. +// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`). +func (m *IntIntMap) Diff(other *IntIntMap) (addedKeys, removedKeys, updatedKeys []int) { + m.mu.RLock() + defer m.mu.RUnlock() + other.mu.RLock() + defer other.mu.RUnlock() + + for key := range m.data { + if _, ok := other.data[key]; !ok { + removedKeys = append(removedKeys, key) + } else if m.data[key] != other.data[key] { + updatedKeys = append(updatedKeys, key) + } + } + for key := range other.data { + if _, ok := m.data[key]; !ok { + addedKeys = append(addedKeys, key) + } + } + return +} diff --git a/container/gmap/gmap_hash_int_str_map.go b/container/gmap/gmap_hash_int_str_map.go index 943f6b79b2f..ffba090b732 100644 --- a/container/gmap/gmap_hash_int_str_map.go +++ b/container/gmap/gmap_hash_int_str_map.go @@ -506,3 +506,28 @@ func (m *IntStrMap) IsSubOf(other *IntStrMap) bool { } return true } + +// Diff compares current map `m` with map `other` and returns their different keys. +// The returned `addedKeys` are the keys that are in map `m` but not in map `other`. +// The returned `removedKeys` are the keys that are in map `other` but not in map `m`. +// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`). +func (m *IntStrMap) Diff(other *IntStrMap) (addedKeys, removedKeys, updatedKeys []int) { + m.mu.RLock() + defer m.mu.RUnlock() + other.mu.RLock() + defer other.mu.RUnlock() + + for key := range m.data { + if _, ok := other.data[key]; !ok { + removedKeys = append(removedKeys, key) + } else if m.data[key] != other.data[key] { + updatedKeys = append(updatedKeys, key) + } + } + for key := range other.data { + if _, ok := m.data[key]; !ok { + addedKeys = append(addedKeys, key) + } + } + return +} diff --git a/container/gmap/gmap_hash_str_any_map.go b/container/gmap/gmap_hash_str_any_map.go index b05be65a288..c3749c31492 100644 --- a/container/gmap/gmap_hash_str_any_map.go +++ b/container/gmap/gmap_hash_str_any_map.go @@ -14,6 +14,7 @@ import ( "github.com/gogf/gf/v2/internal/json" "github.com/gogf/gf/v2/internal/rwmutex" "github.com/gogf/gf/v2/util/gconv" + "reflect" ) // StrAnyMap implements map[string]interface{} with RWMutex that has switch. @@ -522,3 +523,28 @@ func (m *StrAnyMap) IsSubOf(other *StrAnyMap) bool { } return true } + +// Diff compares current map `m` with map `other` and returns their different keys. +// The returned `addedKeys` are the keys that are in map `m` but not in map `other`. +// The returned `removedKeys` are the keys that are in map `other` but not in map `m`. +// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`). +func (m *StrAnyMap) Diff(other *StrAnyMap) (addedKeys, removedKeys, updatedKeys []string) { + m.mu.RLock() + defer m.mu.RUnlock() + other.mu.RLock() + defer other.mu.RUnlock() + + for key := range m.data { + if _, ok := other.data[key]; !ok { + removedKeys = append(removedKeys, key) + } else if !reflect.DeepEqual(m.data[key], other.data[key]) { + updatedKeys = append(updatedKeys, key) + } + } + for key := range other.data { + if _, ok := m.data[key]; !ok { + addedKeys = append(addedKeys, key) + } + } + return +} diff --git a/container/gmap/gmap_hash_str_int_map.go b/container/gmap/gmap_hash_str_int_map.go index e0b330f22dc..55582efa799 100644 --- a/container/gmap/gmap_hash_str_int_map.go +++ b/container/gmap/gmap_hash_str_int_map.go @@ -510,3 +510,28 @@ func (m *StrIntMap) IsSubOf(other *StrIntMap) bool { } return true } + +// Diff compares current map `m` with map `other` and returns their different keys. +// The returned `addedKeys` are the keys that are in map `m` but not in map `other`. +// The returned `removedKeys` are the keys that are in map `other` but not in map `m`. +// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`). +func (m *StrIntMap) Diff(other *StrIntMap) (addedKeys, removedKeys, updatedKeys []string) { + m.mu.RLock() + defer m.mu.RUnlock() + other.mu.RLock() + defer other.mu.RUnlock() + + for key := range m.data { + if _, ok := other.data[key]; !ok { + removedKeys = append(removedKeys, key) + } else if m.data[key] != other.data[key] { + updatedKeys = append(updatedKeys, key) + } + } + for key := range other.data { + if _, ok := m.data[key]; !ok { + addedKeys = append(addedKeys, key) + } + } + return +} diff --git a/container/gmap/gmap_hash_str_str_map.go b/container/gmap/gmap_hash_str_str_map.go index e73628f8754..066107a714e 100644 --- a/container/gmap/gmap_hash_str_str_map.go +++ b/container/gmap/gmap_hash_str_str_map.go @@ -499,3 +499,28 @@ func (m *StrStrMap) IsSubOf(other *StrStrMap) bool { } return true } + +// Diff compares current map `m` with map `other` and returns their different keys. +// The returned `addedKeys` are the keys that are in map `m` but not in map `other`. +// The returned `removedKeys` are the keys that are in map `other` but not in map `m`. +// The returned `updatedKeys` are the keys that are both in map `m` and `other` but their values and not equal (`!=`). +func (m *StrStrMap) Diff(other *StrStrMap) (addedKeys, removedKeys, updatedKeys []string) { + m.mu.RLock() + defer m.mu.RUnlock() + other.mu.RLock() + defer other.mu.RUnlock() + + for key := range m.data { + if _, ok := other.data[key]; !ok { + removedKeys = append(removedKeys, key) + } else if m.data[key] != other.data[key] { + updatedKeys = append(updatedKeys, key) + } + } + for key := range other.data { + if _, ok := m.data[key]; !ok { + addedKeys = append(addedKeys, key) + } + } + return +} diff --git a/container/gmap/gmap_z_unit_hash_any_any_test.go b/container/gmap/gmap_z_unit_hash_any_any_test.go index 3853218bfc0..afeb749da43 100644 --- a/container/gmap/gmap_z_unit_hash_any_any_test.go +++ b/container/gmap/gmap_z_unit_hash_any_any_test.go @@ -406,3 +406,24 @@ func Test_AnyAnyMap_IsSubOf(t *testing.T) { t.Assert(m2.IsSubOf(m2), true) }) } + +func Test_AnyAnyMap_Diff(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + m1 := gmap.NewAnyAnyMapFrom(g.MapAnyAny{ + "0": "v0", + "1": "v1", + 2: "v2", + 3: 3, + }) + m2 := gmap.NewAnyAnyMapFrom(g.MapAnyAny{ + "0": "v0", + 2: "v2", + 3: "v3", + 4: "v4", + }) + addedKeys, removedKeys, updatedKeys := m1.Diff(m2) + t.Assert(addedKeys, []interface{}{4}) + t.Assert(removedKeys, []interface{}{"1"}) + t.Assert(updatedKeys, []interface{}{3}) + }) +} diff --git a/container/gmap/gmap_z_unit_hash_int_any_test.go b/container/gmap/gmap_z_unit_hash_int_any_test.go index 57034872392..702bf4c8662 100644 --- a/container/gmap/gmap_z_unit_hash_int_any_test.go +++ b/container/gmap/gmap_z_unit_hash_int_any_test.go @@ -390,3 +390,24 @@ func Test_IntAnyMap_IsSubOf(t *testing.T) { t.Assert(m2.IsSubOf(m2), true) }) } + +func Test_IntAnyMap_Diff(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + m1 := gmap.NewIntAnyMapFrom(g.MapIntAny{ + 0: "v0", + 1: "v1", + 2: "v2", + 3: 3, + }) + m2 := gmap.NewIntAnyMapFrom(g.MapIntAny{ + 0: "v0", + 2: "v2", + 3: "v3", + 4: "v4", + }) + addedKeys, removedKeys, updatedKeys := m1.Diff(m2) + t.Assert(addedKeys, []int{4}) + t.Assert(removedKeys, []int{1}) + t.Assert(updatedKeys, []int{3}) + }) +} diff --git a/container/gmap/gmap_z_unit_hash_int_int_test.go b/container/gmap/gmap_z_unit_hash_int_int_test.go index b2e7dd8f5f6..d620975b2db 100644 --- a/container/gmap/gmap_z_unit_hash_int_int_test.go +++ b/container/gmap/gmap_z_unit_hash_int_int_test.go @@ -398,3 +398,24 @@ func Test_IntIntMap_IsSubOf(t *testing.T) { t.Assert(m2.IsSubOf(m2), true) }) } + +func Test_IntIntMap_Diff(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + m1 := gmap.NewIntIntMapFrom(g.MapIntInt{ + 0: 0, + 1: 1, + 2: 2, + 3: 3, + }) + m2 := gmap.NewIntIntMapFrom(g.MapIntInt{ + 0: 0, + 2: 2, + 3: 31, + 4: 4, + }) + addedKeys, removedKeys, updatedKeys := m1.Diff(m2) + t.Assert(addedKeys, []int{4}) + t.Assert(removedKeys, []int{1}) + t.Assert(updatedKeys, []int{3}) + }) +} diff --git a/container/gmap/gmap_z_unit_hash_int_str_test.go b/container/gmap/gmap_z_unit_hash_int_str_test.go index 7a2bcc5ab1d..9c24d55e639 100644 --- a/container/gmap/gmap_z_unit_hash_int_str_test.go +++ b/container/gmap/gmap_z_unit_hash_int_str_test.go @@ -462,3 +462,24 @@ func Test_IntStrMap_IsSubOf(t *testing.T) { t.Assert(m2.IsSubOf(m2), true) }) } + +func Test_IntStrMap_Diff(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + m1 := gmap.NewIntStrMapFrom(g.MapIntStr{ + 0: "0", + 1: "1", + 2: "2", + 3: "3", + }) + m2 := gmap.NewIntStrMapFrom(g.MapIntStr{ + 0: "0", + 2: "2", + 3: "31", + 4: "4", + }) + addedKeys, removedKeys, updatedKeys := m1.Diff(m2) + t.Assert(addedKeys, []int{4}) + t.Assert(removedKeys, []int{1}) + t.Assert(updatedKeys, []int{3}) + }) +} diff --git a/container/gmap/gmap_z_unit_hash_str_any_test.go b/container/gmap/gmap_z_unit_hash_str_any_test.go index 83fc513f6bd..0113d91c55c 100644 --- a/container/gmap/gmap_z_unit_hash_str_any_test.go +++ b/container/gmap/gmap_z_unit_hash_str_any_test.go @@ -396,3 +396,24 @@ func Test_StrAnyMap_IsSubOf(t *testing.T) { t.Assert(m2.IsSubOf(m2), true) }) } + +func Test_StrAnyMap_Diff(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + m1 := gmap.NewStrAnyMapFrom(g.MapStrAny{ + "0": "v0", + "1": "v1", + "2": "v2", + "3": 3, + }) + m2 := gmap.NewStrAnyMapFrom(g.MapStrAny{ + "0": "v0", + "2": "v2", + "3": "v3", + "4": "v4", + }) + addedKeys, removedKeys, updatedKeys := m1.Diff(m2) + t.Assert(addedKeys, []string{"4"}) + t.Assert(removedKeys, []string{"1"}) + t.Assert(updatedKeys, []string{"3"}) + }) +} diff --git a/container/gmap/gmap_z_unit_hash_str_int_test.go b/container/gmap/gmap_z_unit_hash_str_int_test.go index 6616ebf9042..6fe756289dd 100644 --- a/container/gmap/gmap_z_unit_hash_str_int_test.go +++ b/container/gmap/gmap_z_unit_hash_str_int_test.go @@ -404,3 +404,24 @@ func Test_StrIntMap_IsSubOf(t *testing.T) { t.Assert(m2.IsSubOf(m2), true) }) } + +func Test_StrIntMap_Diff(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + m1 := gmap.NewStrIntMapFrom(g.MapStrInt{ + "0": 0, + "1": 1, + "2": 2, + "3": 3, + }) + m2 := gmap.NewStrIntMapFrom(g.MapStrInt{ + "0": 0, + "2": 2, + "3": 31, + "4": 4, + }) + addedKeys, removedKeys, updatedKeys := m1.Diff(m2) + t.Assert(addedKeys, []string{"4"}) + t.Assert(removedKeys, []string{"1"}) + t.Assert(updatedKeys, []string{"3"}) + }) +} diff --git a/container/gmap/gmap_z_unit_hash_str_str_test.go b/container/gmap/gmap_z_unit_hash_str_str_test.go index 41c0966a1c7..81936df5222 100644 --- a/container/gmap/gmap_z_unit_hash_str_str_test.go +++ b/container/gmap/gmap_z_unit_hash_str_str_test.go @@ -403,3 +403,24 @@ func Test_StrStrMap_IsSubOf(t *testing.T) { t.Assert(m2.IsSubOf(m2), true) }) } + +func Test_StrStrMap_Diff(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + m1 := gmap.NewStrStrMapFrom(g.MapStrStr{ + "0": "0", + "1": "1", + "2": "2", + "3": "3", + }) + m2 := gmap.NewStrStrMapFrom(g.MapStrStr{ + "0": "0", + "2": "2", + "3": "31", + "4": "4", + }) + addedKeys, removedKeys, updatedKeys := m1.Diff(m2) + t.Assert(addedKeys, []string{"4"}) + t.Assert(removedKeys, []string{"1"}) + t.Assert(updatedKeys, []string{"3"}) + }) +}