From 4ad508c04d9261d95a310359dcd9a0aae485bc37 Mon Sep 17 00:00:00 2001 From: Lonely <641008175@QQ.COM> Date: Thu, 20 Jul 2023 20:07:43 +0800 Subject: [PATCH] feat: add Diff feature to gmap (#2774) --- container/gmap/gmap_hash_any_any_map.go | 26 +++++++++++++++++++ container/gmap/gmap_hash_int_any_map.go | 26 +++++++++++++++++++ container/gmap/gmap_hash_int_int_map.go | 25 ++++++++++++++++++ container/gmap/gmap_hash_int_str_map.go | 25 ++++++++++++++++++ container/gmap/gmap_hash_str_any_map.go | 26 +++++++++++++++++++ container/gmap/gmap_hash_str_int_map.go | 25 ++++++++++++++++++ container/gmap/gmap_hash_str_str_map.go | 25 ++++++++++++++++++ .../gmap/gmap_z_unit_hash_any_any_test.go | 21 +++++++++++++++ .../gmap/gmap_z_unit_hash_int_any_test.go | 21 +++++++++++++++ .../gmap/gmap_z_unit_hash_int_int_test.go | 21 +++++++++++++++ .../gmap/gmap_z_unit_hash_int_str_test.go | 21 +++++++++++++++ .../gmap/gmap_z_unit_hash_str_any_test.go | 21 +++++++++++++++ .../gmap/gmap_z_unit_hash_str_int_test.go | 21 +++++++++++++++ .../gmap/gmap_z_unit_hash_str_str_test.go | 21 +++++++++++++++ 14 files changed, 325 insertions(+) diff --git a/container/gmap/gmap_hash_any_any_map.go b/container/gmap/gmap_hash_any_any_map.go index fdfe9aa63..37da51731 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 1faed272e..64d687756 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 f63c1f067..5fb8c440a 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 943f6b79b..ffba090b7 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 b05be65a2..c3749c314 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 e0b330f22..55582efa7 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 e73628f87..066107a71 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 3853218bf..afeb749da 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 570348723..702bf4c86 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 b2e7dd8f5..d620975b2 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 7a2bcc5ab..9c24d55e6 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 83fc513f6..0113d91c5 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 6616ebf90..6fe756289 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 41c0966a1..81936df52 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"}) + }) +}