Skip to content

Commit

Permalink
add map/reduce methods
Browse files Browse the repository at this point in the history
  • Loading branch information
lxzan committed Dec 25, 2023
1 parent 6e9a952 commit 881984e
Show file tree
Hide file tree
Showing 10 changed files with 339 additions and 117 deletions.
9 changes: 9 additions & 0 deletions algorithm/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ func UniqueBy[T any, K cmp.Ordered, A ~[]T](arr A, getKey func(item T) K) A {
return arr
}

// Reduce 对数组中的每个元素按序执行一个提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,
// 最后将其结果汇总为单个返回值。
func Reduce[T any, S any](initialValue S, arr []T, reducer func(s S, item T) S) S {
for _, item := range arr {
initialValue = reducer(initialValue, item)
}
return initialValue
}

// Reverse 反转数组
func Reverse[T any, A ~[]T](arr A) A {
var n = len(arr)
Expand Down
21 changes: 21 additions & 0 deletions algorithm/helper_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package algorithm

import (
"github.com/lxzan/dao/hashmap"
"github.com/lxzan/dao/internal/utils"
"github.com/stretchr/testify/assert"
"math/rand"
Expand Down Expand Up @@ -182,3 +183,23 @@ func TestIsZero(t *testing.T) {
assert.False(t, IsZero(1))
assert.False(t, IsZero(" "))
}

func TestReduce(t *testing.T) {
t.Run("", func(t *testing.T) {
var arr = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var sum = Reduce(0, arr, func(summarize int, item int) int {
return summarize + item
})
assert.Equal(t, sum, 55)
})

t.Run("", func(t *testing.T) {
var arr = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var m = hashmap.New[int, struct{}](10)
Reduce(m, arr, func(s hashmap.HashMap[int, struct{}], item int) hashmap.HashMap[int, struct{}] {
s.Set(item, struct{}{})
return s
})
assert.ElementsMatch(t, m.Keys(), []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
})
}
58 changes: 58 additions & 0 deletions heap/heap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package heap

import (
"fmt"
"github.com/lxzan/dao/algorithm"
"github.com/lxzan/dao/internal/utils"
"github.com/lxzan/dao/types/cmp"
"github.com/stretchr/testify/assert"
"math/rand"
"sort"
"testing"
"unsafe"
Expand All @@ -14,6 +16,62 @@ func desc[T cmp.Ordered](a, b T) bool {
return a > b
}

func validateHeap[T cmp.Ordered](t *testing.T, h *Heap[T], compare cmp.CompareFunc[T]) {
var n = h.Len()
if n > 0 {
assert.Equal(t, h.data[0], h.Top())
}

var i = 0
h.Range(func(index int, value T) bool {
i++

var base = index << h.bits
var end = algorithm.Min(base+h.ways, n-1)
for j := base + 1; j <= end; j++ {
child := h.data[j]
assert.True(t, compare(value, child) <= 0)
}
return true
})

var keys = make([]T, 0, n)
for h.Len() > 0 {
keys = append(keys, h.Pop())
}
assert.True(t, algorithm.IsSorted(keys, compare))
}

func TestHeap_Random(t *testing.T) {
const count = 10000

var f = func(ways uint32, lessFunc cmp.LessFunc[int], compareFunc cmp.CompareFunc[int]) {
var h = NewWithWays[int](ways, lessFunc)
h.SetCap(count)
for i := 0; i < count; i++ {
flag := rand.Intn(3)
key := rand.Intn(count)
switch flag {
case 0, 1:
h.Push(key)
case 2:
h.Pop()
}
}

validateHeap(t, h, compareFunc)
}

f(2, cmp.Less[int], cmp.Compare[int])
f(2, cmp.Great[int], cmp.CompareDesc[int])
f(4, cmp.Less[int], cmp.Compare[int])
f(4, cmp.Great[int], cmp.CompareDesc[int])
f(8, cmp.Less[int], cmp.Compare[int])
f(8, cmp.Great[int], cmp.CompareDesc[int])
f(16, cmp.Less[int], cmp.Compare[int])
f(16, cmp.Great[int], cmp.CompareDesc[int])
}

func TestNew(t *testing.T) {
const count = 1000
{
Expand Down
139 changes: 60 additions & 79 deletions heap/index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,90 +11,54 @@ import (
"unsafe"
)

func TestIndexedHeap_Sort(t *testing.T) {
var h = NewIndexedHeap[int, struct{}](Quadratic, nil)
for i := 0; i < 1000; i++ {
h.Push(rand.Int(), struct{}{})
}
var arr []int
for h.Len() > 0 {
arr = append(arr, h.Pop().Key())
func validateIndexedHeap[K cmp.Ordered, V any](t *testing.T, h *IndexedHeap[K, V], compare cmp.CompareFunc[K]) {
var n = h.Len()
if n > 0 {
assert.Equal(t, h.data[0].Key(), h.Top().Key())
}
assert.True(t, algorithm.IsSorted(arr, func(a, b int) int {
if a > b {
return 1
} else if a < b {
return -1
} else {
return 0
}
}))
}

func TestHeap_Random(t *testing.T) {
t.Run("asc", func(t *testing.T) {
const count = 10000
var h = NewIndexedHeap[int, struct{}](Quadratic, cmp.Less[int])
h.SetCap(count)
for i := 0; i < count; i++ {
flag := rand.Intn(5)
key := rand.Intn(count)
switch flag {
case 0, 1:
h.Push(key, struct{}{})
case 2:
h.Pop()
case 3:
n := h.Len()
if n > 0 {
index := rand.Intn(n)
h.UpdateKeyByIndex(index, key)
}
case 4:
n := h.Len()
if n > 0 {
index := rand.Intn(n)
h.DeleteByIndex(index)
}
}
}

for i, item := range h.data {
assert.Equal(t, item.Index(), i)
var i = 0
h.Range(func(ele *Element[K, V]) bool {
assert.Equal(t, ele.Index(), i)
i++

if item.Index() == 0 {
item = h.Top()
}
var n = h.Len()
var base = i << h.bits
var end = algorithm.Min(base+h.ways, n-1)
for j := base + 1; j <= end; j++ {
assert.True(t, h.lessFunc(item.Key(), h.GetByIndex(j).Key()))
}
var base = ele.Index() << h.bits
var end = algorithm.Min(base+h.ways, n-1)
for j := base + 1; j <= end; j++ {
child := h.GetByIndex(j)
assert.True(t, compare(ele.Key(), child.Key()) <= 0)
}
return true
})

t.Run("desc", func(t *testing.T) {
const count = 10000
var h = NewIndexedHeap[int, struct{}](Quadratic, func(a, b int) bool {
return a > b
})
var keys = make([]K, 0, n)
for h.Len() > 0 {
keys = append(keys, h.Pop().Key())
}
assert.True(t, algorithm.IsSorted(keys, compare))
}

func TestIndexedHeap_Random(t *testing.T) {
const count = 10000

var f = func(ways uint32, lessFunc cmp.LessFunc[int], compareFunc cmp.CompareFunc[int]) {
var h = NewIndexedHeap[int, struct{}](ways, lessFunc)
h.SetCap(count)
for i := 0; i < count; i++ {
flag := rand.Intn(5)
flag := utils.Alphabet.Intn(6)
key := rand.Intn(count)
switch flag {
case 0, 1:
case 0, 1, 2:
h.Push(key, struct{}{})
case 2:
h.Pop()
case 3:
h.Pop()
case 4:
n := h.Len()
if n > 0 {
index := rand.Intn(n)
h.UpdateKeyByIndex(index, key)
}
case 4:
case 5:
n := h.Len()
if n > 0 {
index := rand.Intn(n)
Expand All @@ -103,20 +67,37 @@ func TestHeap_Random(t *testing.T) {
}
}

for i, item := range h.data {
assert.Equal(t, item.Index(), i)
validateIndexedHeap(t, h, compareFunc)
}

if item.Index() == 0 {
item = h.Top()
}
var n = h.Len()
var base = i << h.bits
var end = algorithm.Min(base+h.ways, n-1)
for j := base + 1; j <= end; j++ {
assert.True(t, h.lessFunc(item.Key(), h.GetByIndex(j).Key()))
}
f(2, cmp.Less[int], cmp.Compare[int])
f(2, cmp.Great[int], cmp.CompareDesc[int])
f(4, cmp.Less[int], cmp.Compare[int])
f(4, cmp.Great[int], cmp.CompareDesc[int])
f(8, cmp.Less[int], cmp.Compare[int])
f(8, cmp.Great[int], cmp.CompareDesc[int])
f(16, cmp.Less[int], cmp.Compare[int])
f(16, cmp.Great[int], cmp.CompareDesc[int])
}

func TestIndexedHeap_Sort(t *testing.T) {
var h = NewIndexedHeap[int, struct{}](Quadratic, nil)
for i := 0; i < 1000; i++ {
h.Push(rand.Int(), struct{}{})
}
var arr []int
for h.Len() > 0 {
arr = append(arr, h.Pop().Key())
}
assert.True(t, algorithm.IsSorted(arr, func(a, b int) int {
if a > b {
return 1
} else if a < b {
return -1
} else {
return 0
}
})
}))
}

func TestIndexedHeap_Range(t *testing.T) {
Expand Down
7 changes: 7 additions & 0 deletions internal/utils/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ func (c *RandomString) Generate(n int) string {
return string(b)
}

func (c *RandomString) Intn(n int) int {
c.mu.Lock()
v := c.rand.Intn(n)
c.mu.Unlock()
return v
}

func IsSameSlice[T comparable](a, b []T) bool {
if len(a) != len(b) {
return false
Expand Down
6 changes: 6 additions & 0 deletions internal/utils/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ func TestRandomString_Generate(t *testing.T) {
assert.Equal(t, len(s), 6)
}

func TestRandomString_Intn(t *testing.T) {
for i := 0; i < 100; i++ {
v := Alphabet.Intn(i + 10)
assert.True(t, v < i+10)
}
}
func TestReverseStrings(t *testing.T) {
{
var arr = []string{"a", "b", "c"}
Expand Down
16 changes: 15 additions & 1 deletion types/cmp/cmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ type (
CompareFunc[T any] func(a, b T) int
)

// Less 比大小函数(升序)
// Less 小于
func Less[T Ordered](x, y T) bool { return x < y }

// Great 大于
func Great[T Ordered](x, y T) bool { return x > y }

// Compare 比较函数(升序)
func Compare[T Ordered](x, y T) int {
if x < y {
Expand All @@ -45,3 +48,14 @@ func Compare[T Ordered](x, y T) int {
}
return 0
}

// CompareDesc 比较函数(降序)
func CompareDesc[T Ordered](x, y T) int {
if x < y {
return +1
}
if x > y {
return -1
}
return 0
}
Loading

0 comments on commit 881984e

Please sign in to comment.