Skip to content

Commit

Permalink
Add ReplaceKey (#37)
Browse files Browse the repository at this point in the history
ReplaceKey replaces an existing key with a new key while preserving order of
the value. This function will return true if the operation was successful, or
false if 'originalKey' is not found OR 'newKey' already exists (which would be an overwrite).
  • Loading branch information
drshriveer authored May 24, 2024
1 parent 8a25936 commit f5ea9d1
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 5 deletions.
15 changes: 15 additions & 0 deletions v2/orderedmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ func (m *OrderedMap[K, V]) Set(key K, value V) bool {
return true
}

// ReplaceKey replaces an existing key with a new key while preserving order of
// the value. This function will return true if the operation was successful, or
// false if 'originalKey' is not found OR 'newKey' already exists (which would be an overwrite).
func (m *OrderedMap[K, V]) ReplaceKey(originalKey, newKey K) bool {
element, originalExists := m.kv[originalKey]
_, newKeyExists := m.kv[newKey]
if originalExists && !newKeyExists {
delete(m.kv, originalKey)
m.kv[newKey] = element
element.Key = newKey
return true
}
return false
}

// GetOrDefault returns the value for a key. If the key does not exist, returns
// the default value instead.
func (m *OrderedMap[K, V]) GetOrDefault(key K, defaultValue V) V {
Expand Down
78 changes: 73 additions & 5 deletions v2/orderedmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/elliotchance/orderedmap/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestNewOrderedMap(t *testing.T) {
Expand Down Expand Up @@ -97,6 +98,68 @@ func TestSet(t *testing.T) {
})
}

func TestReplaceKey(t *testing.T) {
t.Run("ReturnsFalseIfOriginalKeyDoesntExist", func(t *testing.T) {
m := orderedmap.NewOrderedMap[string, string]()
assert.False(t, m.ReplaceKey("foo", "bar"))
})

t.Run("ReturnsFalseIfNewKeyAlreadyExists", func(t *testing.T) {
m := orderedmap.NewOrderedMap[string, string]()
m.Set("foo", "bar")
m.Set("baz", "qux")
assert.False(t, m.ReplaceKey("foo", "baz"))
assert.Equal(t, []string{"foo", "baz"}, m.Keys())
})

t.Run("ReturnsTrueIfOnlyOriginalKeyExists", func(t *testing.T) {
m := orderedmap.NewOrderedMap[string, string]()
m.Set("foo", "bar")
assert.True(t, m.ReplaceKey("foo", "baz"))

// Now validate the "replacement" was a success.
el := m.GetElement("baz")
require.NotNil(t, el)
assert.Equal(t, "bar", el.Value)
assert.Equal(t, "baz", el.Key)

v, ok := m.Get("baz")
assert.True(t, ok)
assert.Equal(t, "bar", v)
assert.Equal(t, []string{"baz"}, m.Keys())
assert.Equal(t, 1, m.Len())

_, ok = m.Get("foo") // original key
assert.False(t, ok)
})

t.Run("KeyMaintainsOrderWhenReplaced", func(t *testing.T) {
count := 100
// Build a larger map to help validate that the order is not coincidental.
m := orderedmap.NewOrderedMap[int, int]()
for i := 0; i < count; i++ {
m.Set(i, i)
}
// Rename the middle 50-60 elements to 100+ current
for i := 50; i < 60; i++ {
assert.True(t, m.ReplaceKey(i, i+100))
}

// ensure length is maintained
assert.Equal(t, count, m.Len())

// Validate the order is maintained.
keys := m.Keys()
for i, key := range keys {
if i >= 50 && i < 60 {
assert.Equal(t, i+100, key)
} else {
assert.Equal(t, i, key)
}
}
})
}

func TestLen(t *testing.T) {
t.Run("EmptyMapIsZeroLen", func(t *testing.T) {
m := orderedmap.NewOrderedMap[string, string]()
Expand Down Expand Up @@ -909,25 +972,30 @@ func BenchmarkAll(b *testing.B) {
b.Run("BenchmarkBigOrderedMap_Set", BenchmarkBigOrderedMap_Set)
b.Run("BenchmarkBigMap_Get", BenchmarkBigMap_Get)
b.Run("BenchmarkBigOrderedMap_Get", BenchmarkBigOrderedMap_Get)
b.Run("BenchmarkBigOrderedMap_GetElement", BenchmarkBigOrderedMap_GetElement)
b.Run("BenchmarkBigOrderedMap_GetElement",
BenchmarkBigOrderedMap_GetElement)
b.Run("BenchmarkBigOrderedMap_Iterate", BenchmarkBigOrderedMap_Iterate)
b.Run("BenchmarkBigMap_Iterate", BenchmarkBigMap_Iterate)

b.Run("BenchmarkOrderedMapString_Set", BenchmarkOrderedMapString_Set)
b.Run("BenchmarkMapString_Set", BenchmarkMapString_Set)
b.Run("BenchmarkOrderedMapString_Get", BenchmarkOrderedMapString_Get)
b.Run("BenchmarkMapString_Get", BenchmarkMapString_Get)
b.Run("BenchmarkOrderedMapString_GetElement", BenchmarkOrderedMapString_GetElement)
b.Run("BenchmarkOrderedMapString_GetElement",
BenchmarkOrderedMapString_GetElement)
b.Run("BenchmarkOrderedMapString_Delete", BenchmarkOrderedMapString_Delete)
b.Run("BenchmarkMapString_Delete", BenchmarkMapString_Delete)
b.Run("BenchmarkOrderedMapString_Iterate", BenchmarkOrderedMapString_Iterate)
b.Run("BenchmarkOrderedMapString_Iterate",
BenchmarkOrderedMapString_Iterate)
b.Run("BenchmarkMapString_Iterate", BenchmarkMapString_Iterate)

b.Run("BenchmarkBigMapString_Set", BenchmarkBigMapString_Set)
b.Run("BenchmarkBigOrderedMapString_Set", BenchmarkBigOrderedMapString_Set)
b.Run("BenchmarkBigMapString_Get", BenchmarkBigMapString_Get)
b.Run("BenchmarkBigOrderedMapString_Get", BenchmarkBigOrderedMapString_Get)
b.Run("BenchmarkBigOrderedMapString_GetElement", BenchmarkBigOrderedMapString_GetElement)
b.Run("BenchmarkBigOrderedMapString_Iterate", BenchmarkBigOrderedMapString_Iterate)
b.Run("BenchmarkBigOrderedMapString_GetElement",
BenchmarkBigOrderedMapString_GetElement)
b.Run("BenchmarkBigOrderedMapString_Iterate",
BenchmarkBigOrderedMapString_Iterate)
b.Run("BenchmarkBigMapString_Iterate", BenchmarkBigMapString_Iterate)
}

0 comments on commit f5ea9d1

Please sign in to comment.