mirror of
https://github.com/duke-git/lancet.git
synced 2026-03-01 00:35:28 +08:00
Compare commits
8
Commits
f9e5ec9096
...
7e85a0ed7d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e85a0ed7d | ||
|
|
2268a0312f | ||
|
|
da84d95aa3 | ||
|
|
48244d6711 | ||
|
|
5e3337a52e | ||
|
|
c3372e18b1 | ||
|
|
90e5a0bfb2 | ||
|
|
93be25920f |
+57
-4
@@ -30,6 +30,7 @@ package datetime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -382,11 +383,63 @@ func TimestampNano(timezone ...string) int64 {
|
||||
return t.UnixNano()
|
||||
}
|
||||
|
||||
// TraceFuncTime: trace the func costed time,just call it at top of the func like `defer TraceFuncTime()()`
|
||||
func TraceFuncTime() func() {
|
||||
pre := time.Now()
|
||||
// TrackFuncTime track the time of function execution.
|
||||
// call it at top of the func like `defer TrackFuncTime(time.Now())()`
|
||||
// Play: todo
|
||||
func TrackFuncTime(pre time.Time) func() {
|
||||
callerName := getCallerName()
|
||||
return func() {
|
||||
elapsed := time.Since(pre)
|
||||
fmt.Println("Costs Time:\t", elapsed)
|
||||
fmt.Printf("Function %s execution time:\t %v", callerName, elapsed)
|
||||
}
|
||||
}
|
||||
|
||||
func getCallerName() string {
|
||||
pc, _, _, ok := runtime.Caller(2)
|
||||
if !ok {
|
||||
return "Unknown"
|
||||
}
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn == nil {
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
fullName := fn.Name()
|
||||
if lastDot := strings.LastIndex(fullName, "."); lastDot != -1 {
|
||||
return fullName[lastDot+1:]
|
||||
}
|
||||
|
||||
return fullName
|
||||
}
|
||||
|
||||
// DaysBetween returns the number of days between two times.
|
||||
// Play: todo
|
||||
func DaysBetween(start, end time.Time) int {
|
||||
duration := end.Sub(start)
|
||||
days := int(duration.Hours() / 24)
|
||||
|
||||
return days
|
||||
}
|
||||
|
||||
// GenerateDatetimesBetween returns a slice of strings between two times.
|
||||
// layout: the format of the datetime string
|
||||
// interval: the interval between two datetimes
|
||||
// Play: todo
|
||||
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error) {
|
||||
var result []string
|
||||
|
||||
if start.After(end) {
|
||||
start, end = end, start
|
||||
}
|
||||
|
||||
duration, err := time.ParseDuration(interval)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for current := start; !current.After(end); current = current.Add(duration) {
|
||||
result = append(result, current.Format(layout))
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -408,3 +408,32 @@ func ExampleIsWeekend() {
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
func ExampleDaysBetween() {
|
||||
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
|
||||
end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
result := DaysBetween(start, end)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 9
|
||||
}
|
||||
|
||||
func ExampleGenerateDatetimesBetween() {
|
||||
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
|
||||
end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC)
|
||||
|
||||
layout := "2006-01-02 15:04:05"
|
||||
interval := "1h"
|
||||
|
||||
result, err := GenerateDatetimesBetween(start, end, layout, interval)
|
||||
|
||||
fmt.Println(result)
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00]
|
||||
// <nil>
|
||||
}
|
||||
|
||||
@@ -410,3 +410,114 @@ func TestTimestamp(t *testing.T) {
|
||||
ts4 := TimestampNano()
|
||||
t.Log(ts4)
|
||||
}
|
||||
|
||||
func TestTrackFuncTime(t *testing.T) {
|
||||
defer TrackFuncTime(time.Now())()
|
||||
|
||||
var n int
|
||||
for i := 0; i < 5000000; i++ {
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
func TestDaysBetween(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDaysBetween")
|
||||
|
||||
tests := []struct {
|
||||
start time.Time
|
||||
end time.Time
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||
end: time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC),
|
||||
expected: 9,
|
||||
},
|
||||
{
|
||||
start: time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC),
|
||||
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||
expected: -9,
|
||||
},
|
||||
{
|
||||
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
start: time.Date(2024, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||
end: time.Date(2024, time.December, 31, 0, 0, 0, 0, time.UTC),
|
||||
expected: 365,
|
||||
},
|
||||
{
|
||||
start: time.Date(2024, time.March, 1, 0, 0, 0, 0, time.UTC),
|
||||
end: time.Date(2024, time.March, 31, 0, 0, 0, 0, time.UTC),
|
||||
expected: 30,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
result := DaysBetween(tt.start, tt.end)
|
||||
assert.Equal(tt.expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateDatetimesBetween(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestGenerateDatetimesBetween")
|
||||
|
||||
tests := []struct {
|
||||
start time.Time
|
||||
end time.Time
|
||||
layout string
|
||||
interval string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||
end: time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC),
|
||||
layout: "2006-01-02 15:04:05",
|
||||
interval: "30m",
|
||||
expected: []string{
|
||||
"2024-09-01 00:00:00",
|
||||
"2024-09-01 00:30:00",
|
||||
"2024-09-01 01:00:00",
|
||||
"2024-09-01 01:30:00",
|
||||
"2024-09-01 02:00:00",
|
||||
},
|
||||
},
|
||||
{
|
||||
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||
end: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||
layout: "2006-01-02 15:04:05",
|
||||
interval: "1h",
|
||||
expected: []string{"2024-09-01 00:00:00"},
|
||||
},
|
||||
{
|
||||
start: time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC),
|
||||
end: time.Date(2024, time.September, 1, 3, 0, 0, 0, time.UTC),
|
||||
layout: "2006-01-02 15:04:05",
|
||||
interval: "2h",
|
||||
expected: []string{
|
||||
"2024-09-01 00:00:00",
|
||||
"2024-09-01 02:00:00",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
result, err := GenerateDatetimesBetween(tt.start, tt.end, tt.layout, tt.interval)
|
||||
|
||||
assert.Equal(tt.expected, result)
|
||||
assert.IsNil(err)
|
||||
}
|
||||
|
||||
t.Run("Invalid interval", func(t *testing.T) {
|
||||
_, err := GenerateDatetimesBetween(time.Now(), time.Now(), "2006-01-02 15:04:05", "invalid")
|
||||
if err == nil {
|
||||
t.Fatal("Expected error, got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -64,6 +64,9 @@ import (
|
||||
- [TimestampMilli](#TimestampMilli)
|
||||
- [TimestampMicro](#TimestampMicro)
|
||||
- [TimestampNano](#TimestampNano)
|
||||
- [TrackFuncTime](#TrackFuncTime)
|
||||
- [DaysBetween](#DaysBetween)
|
||||
- [GenerateDatetimesBetween](#GenerateDatetimesBetween)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -1334,7 +1337,7 @@ import (
|
||||
func main() {
|
||||
result1 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss")
|
||||
|
||||
result2 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss", "EST")
|
||||
result2 := datetime.NowDateOrTime("yyyy-mm-dd hh:mm:ss", "EST")
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
@@ -1464,4 +1467,107 @@ func main() {
|
||||
// Output:
|
||||
// 1690363051331788000
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="TrackFuncTime">TrackFuncTime</span>
|
||||
|
||||
<p>测试函数执行时间。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func TrackFuncTime(pre time.Time) func()
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/datetime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer datetime.TrackFuncTime(time.Now())()
|
||||
|
||||
var n int
|
||||
for i := 0; i < 5000000; i++ {
|
||||
n++
|
||||
}
|
||||
|
||||
fmt.Println(1) // Function main execution time: 1.460287ms
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DaysBetween">DaysBetween</span>
|
||||
|
||||
<p>返回两个日期之间的天数差。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func DaysBetween(start, end time.Time) int
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/datetime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
|
||||
end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
result := datetime.DaysBetween(start, end)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 9
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GenerateDatetimesBetween">GenerateDatetimesBetween</span>
|
||||
|
||||
<p>生成从start到end的所有日期时间的字符串列表。layout参数表示时间格式,例如"2006-01-02 15:04:05",interval参数表示时间间隔,例如"1h"表示1小时,"30m"表示30分钟。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error)
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/datetime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
|
||||
end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC)
|
||||
|
||||
layout := "2006-01-02 15:04:05"
|
||||
interval := "1h"
|
||||
|
||||
result, err := datetime.GenerateDatetimesBetween(start, end, layout, interval)
|
||||
|
||||
fmt.Println(result)
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00]
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
@@ -7,6 +7,8 @@ maputil 包包括一些操作 map 的函数。
|
||||
## 源码:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/maputil/map.go](https://github.com/duke-git/lancet/blob/main/maputil/map.go)
|
||||
- [https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go](https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go)
|
||||
- [https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go](https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ import (
|
||||
- [Break](#Break)
|
||||
- [RightPadding](#RightPadding)
|
||||
- [LeftPadding](#LeftPadding)
|
||||
- [Frequency](#Frequency)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -2902,7 +2903,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
<span id="RightPadding">RightPadding</span>
|
||||
### <span id="RightPadding">RightPadding</span>
|
||||
|
||||
<p>在切片的右部添加元素。</p>
|
||||
|
||||
@@ -2921,7 +2922,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
padded := slice.RightPadding(nums, 0, 3)
|
||||
fmt.Println(padded)
|
||||
// Output:
|
||||
@@ -2929,7 +2930,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
<span id="LeftPadding">LeftPadding</span>
|
||||
### <span id="LeftPadding">LeftPadding</span>
|
||||
|
||||
<p>在切片的左部添加元素。</p>
|
||||
|
||||
@@ -2948,10 +2949,39 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
padded := slice.LeftPadding(nums, 0, 3)
|
||||
fmt.Println(padded)
|
||||
// Output:
|
||||
// [0 0 0 1 2 3 4 5]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Frequency">Frequency</span>
|
||||
|
||||
<p>计算切片中每个元素出现的频率。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Frequency[T comparable](slice []T) map[T]int
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
strs := []string{"a", "b", "b", "c", "c", "c"}
|
||||
result := slice.Frequency(strs)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2 c:3]
|
||||
}
|
||||
```
|
||||
@@ -66,6 +66,9 @@ import (
|
||||
- [Ellipsis](#Ellipsis)
|
||||
- [Shuffle](#Shuffle)
|
||||
- [Rotate](#Rotate)
|
||||
- [TemplateReplace](#TemplateReplace)
|
||||
- [RegexMatchAllGroups](#RegexMatchAllGroups)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -1658,4 +1661,71 @@ func main() {
|
||||
// oHell
|
||||
// loHel
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="TemplateReplace">TemplateReplace</span>
|
||||
|
||||
<p>将模板字符串中的占位符替换为数据映射中的相应值。占位符括在花括号中,例如 {key}。例如,模板字符串为“Hello, {name}!”,数据映射为{"name": "world"},结果将为“Hello, world!”。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func TemplateReplace(template string, data map[string]string) string
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行]()</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
template := `Hello, my name is {name}, I'm {age} years old.`
|
||||
data := map[string]string{
|
||||
"name": "Bob",
|
||||
"age": "20",
|
||||
}
|
||||
|
||||
result := strutil.TemplateReplace(template, data)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// Hello, my name is Bob, I'm 20 years old.
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RegexMatchAllGroups">RegexMatchAllGroups</span>
|
||||
|
||||
<p>使用正则表达式匹配字符串中的所有子组并返回结果。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func RegexMatchAllGroups(pattern, str string) [][]string
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[Run]()</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
pattern := `(\w+\.+\w+)@(\w+)\.(\w+)`
|
||||
str := "Emails: john.doe@example.com and jane.doe@example.com"
|
||||
|
||||
result := strutil.RegexMatchAllGroups(pattern, str)
|
||||
|
||||
fmt.Println(result[0])
|
||||
fmt.Println(result[1])
|
||||
|
||||
// Output:
|
||||
// [john.doe@example.com john.doe example com]
|
||||
// [jane.doe@example.com jane.doe example com]
|
||||
}
|
||||
```
|
||||
@@ -65,6 +65,9 @@ import (
|
||||
- [TimestampMilli](#TimestampMilli)
|
||||
- [TimestampMicro](#TimestampMicro)
|
||||
- [TimestampNano](#TimestampNano)
|
||||
- [TrackFuncTime](#TrackFuncTime)
|
||||
- [GenerateDatetimesBetween](#GenerateDatetimesBetween)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -1465,3 +1468,106 @@ func main() {
|
||||
// 1690363051331788000
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="TrackFuncTime">TrackFuncTime</span>
|
||||
|
||||
<p>Tracks function execution time.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func TrackFuncTime(pre time.Time) func()
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/datetime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer datetime.TrackFuncTime(time.Now())()
|
||||
|
||||
var n int
|
||||
for i := 0; i < 5000000; i++ {
|
||||
n++
|
||||
}
|
||||
|
||||
fmt.Println(1) // Function main execution time: 1.460287ms
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DaysBetween">DaysBetween</span>
|
||||
|
||||
<p>Returns the number of days between two times.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func DaysBetween(start, end time.Time) int
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/datetime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
|
||||
end := time.Date(2024, time.September, 10, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
result := datetime.DaysBetween(start, end)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// 9
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="GenerateDatetimesBetween">GenerateDatetimesBetween</span>
|
||||
|
||||
<p>Returns a slice of strings between two times. `layout`: the format of the datetime string.`interval`: the interval between two datetimes.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func GenerateDatetimesBetween(start, end time.Time, layout string, interval string) ([]string, error)
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/datetime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Date(2024, time.September, 1, 0, 0, 0, 0, time.UTC)
|
||||
end := time.Date(2024, time.September, 1, 2, 0, 0, 0, time.UTC)
|
||||
|
||||
layout := "2006-01-02 15:04:05"
|
||||
interval := "1h"
|
||||
|
||||
result, err := datetime.GenerateDatetimesBetween(start, end, layout, interval)
|
||||
|
||||
fmt.Println(result)
|
||||
fmt.Println(err)
|
||||
|
||||
// Output:
|
||||
// [2024-09-01 00:00:00 2024-09-01 01:00:00 2024-09-01 02:00:00]
|
||||
// <nil>
|
||||
}
|
||||
```
|
||||
@@ -7,6 +7,9 @@ Package maputil includes some functions to manipulate map.
|
||||
## Source:
|
||||
|
||||
- [https://github.com/duke-git/lancet/blob/main/maputil/map.go](https://github.com/duke-git/lancet/blob/main/maputil/map.go)
|
||||
- [https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go](https://github.com/duke-git/lancet/blob/main/maputil/concurrentmap.go)
|
||||
- [https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go](https://github.com/duke-git/lancet/blob/main/maputil/orderedmap.go)
|
||||
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ import (
|
||||
- [Break](#Break)
|
||||
- [RightPadding](#RightPadding)
|
||||
- [LeftPadding](#LeftPadding)
|
||||
- [Frequency](#Frequency)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -2897,7 +2898,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
<span id="c">RightPadding</span>
|
||||
### <span id="RightPadding">RightPadding</span>
|
||||
|
||||
<p>RightPadding adds padding to the right end of a slice.</p>
|
||||
|
||||
@@ -2916,7 +2917,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
padded := slice.RightPadding(nums, 0, 3)
|
||||
fmt.Println(padded)
|
||||
// Output:
|
||||
@@ -2924,7 +2925,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
<span id="LeftPadding">LeftPadding</span>
|
||||
### <span id="LeftPadding">LeftPadding</span>
|
||||
|
||||
<p>LeftPadding adds padding to the left begin of a slice.</p>
|
||||
|
||||
@@ -2943,10 +2944,39 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
padded := slice.LeftPadding(nums, 0, 3)
|
||||
fmt.Println(padded)
|
||||
// Output:
|
||||
// [0 0 0 1 2 3 4 5]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Frequency">Frequency</span>
|
||||
|
||||
<p>Counts the frequency of each element in the slice.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Frequency[T comparable](slice []T) map[T]int
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run]()</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
strs := []string{"a", "b", "b", "c", "c", "c"}
|
||||
result := slice.Frequency(strs)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2 c:3]
|
||||
}
|
||||
```
|
||||
@@ -66,6 +66,8 @@ import (
|
||||
- [Ellipsis](#Ellipsis)
|
||||
- [Shuffle](#Shuffle)
|
||||
- [Rotate](#Rotate)
|
||||
- [TemplateReplace](#TemplateReplace)
|
||||
- [RegexMatchAllGroups](#RegexMatchAllGroups)
|
||||
|
||||
<div STYLE="page-break-after: always;"></div>
|
||||
|
||||
@@ -1659,4 +1661,71 @@ func main() {
|
||||
// Hello
|
||||
// oHell
|
||||
// loHel
|
||||
}
|
||||
}
|
||||
```
|
||||
### <span id="TemplateReplace">TemplateReplace</span>
|
||||
|
||||
<p>Replaces the placeholders in the template string with the corresponding values in the data map.The placeholders are enclosed in curly braces, e.g. {key}. for example, the template string is "Hello, {name}!", and the data map is {"name": "world"}, the result will be "Hello, world!".</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func TemplateReplace(template string, data map[string]string string
|
||||
```
|
||||
|
||||
<b>example:<span style="float:right;display:inline-block;">[Run]()</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
template := `Hello, my name is {name}, I'm {age} years old.`
|
||||
data := map[string]string{
|
||||
"name": "Bob",
|
||||
"age": "20",
|
||||
}
|
||||
|
||||
result := strutil.TemplateReplace(template, data)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// Hello, my name is Bob, I'm 20 years old.
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="RegexMatchAllGroups">RegexMatchAllGroups</span>
|
||||
|
||||
<p>Matches all subgroups in a string using a regular expression and returns the result.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func RegexMatchAllGroups(pattern, str string) [][]string
|
||||
```
|
||||
|
||||
<b>example:<span style="float:right;display:inline-block;">[Run]()</span></b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
pattern := `(\w+\.+\w+)@(\w+)\.(\w+)`
|
||||
str := "Emails: john.doe@example.com and jane.doe@example.com"
|
||||
|
||||
result := strutil.RegexMatchAllGroups(pattern, str)
|
||||
|
||||
fmt.Println(result[0])
|
||||
fmt.Println(result[1])
|
||||
|
||||
// Output:
|
||||
// [john.doe@example.com john.doe example com]
|
||||
// [jane.doe@example.com jane.doe example com]
|
||||
}
|
||||
```
|
||||
+184
@@ -473,3 +473,187 @@ func SortByKey[K constraints.Ordered, V any](m map[K]V, less func(a, b K) bool)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var mapHandlers = map[reflect.Kind]func(reflect.Value, reflect.Value) error{
|
||||
reflect.String: convertNormal,
|
||||
reflect.Int: convertNormal,
|
||||
reflect.Int16: convertNormal,
|
||||
reflect.Int32: convertNormal,
|
||||
reflect.Int64: convertNormal,
|
||||
reflect.Uint: convertNormal,
|
||||
reflect.Uint16: convertNormal,
|
||||
reflect.Uint32: convertNormal,
|
||||
reflect.Uint64: convertNormal,
|
||||
reflect.Float32: convertNormal,
|
||||
reflect.Float64: convertNormal,
|
||||
reflect.Uint8: convertNormal,
|
||||
reflect.Int8: convertNormal,
|
||||
reflect.Struct: convertNormal,
|
||||
reflect.Complex64: convertNormal,
|
||||
reflect.Complex128: convertNormal,
|
||||
}
|
||||
|
||||
var _ = func() struct{} {
|
||||
mapHandlers[reflect.Map] = convertMap
|
||||
mapHandlers[reflect.Array] = convertSlice
|
||||
mapHandlers[reflect.Slice] = convertSlice
|
||||
return struct{}{}
|
||||
}()
|
||||
|
||||
// MapTo try to map any interface to struct or base type
|
||||
/*
|
||||
Eg:
|
||||
v := map[string]interface{}{
|
||||
"service":map[string]interface{}{
|
||||
"ip":"127.0.0.1",
|
||||
"port":1234,
|
||||
},
|
||||
version:"v1.0.01"
|
||||
}
|
||||
|
||||
type Target struct {
|
||||
Service struct {
|
||||
Ip string `json:"ip"`
|
||||
Port int `json:"port"`
|
||||
} `json:"service"`
|
||||
Ver string `json:"version"`
|
||||
}
|
||||
|
||||
var dist Target
|
||||
if err := maputil.MapTo(v,&dist); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(dist)
|
||||
|
||||
*/
|
||||
// Play: https://go.dev/play/p/4K7KBEPgS5M
|
||||
func MapTo(src any, dst any) error {
|
||||
dstRef := reflect.ValueOf(dst)
|
||||
|
||||
if dstRef.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("dst is not ptr")
|
||||
}
|
||||
|
||||
dstElem := dstRef.Type().Elem()
|
||||
if dstElem.Kind() == reflect.Struct {
|
||||
srcMap := src.(map[string]interface{})
|
||||
return MapToStruct(srcMap, dst)
|
||||
}
|
||||
|
||||
dstRef = reflect.Indirect(dstRef)
|
||||
|
||||
srcRef := reflect.ValueOf(src)
|
||||
if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface {
|
||||
srcRef = srcRef.Elem()
|
||||
}
|
||||
|
||||
if f, ok := mapHandlers[srcRef.Kind()]; ok {
|
||||
return f(srcRef, dstRef)
|
||||
}
|
||||
|
||||
return fmt.Errorf("no implemented:%s", srcRef.Type())
|
||||
}
|
||||
|
||||
func convertNormal(src reflect.Value, dst reflect.Value) error {
|
||||
if dst.CanSet() {
|
||||
if src.Type() == dst.Type() {
|
||||
dst.Set(src)
|
||||
} else if src.CanConvert(dst.Type()) {
|
||||
dst.Set(src.Convert(dst.Type()))
|
||||
} else {
|
||||
return fmt.Errorf("can not convert:%s:%s", src.Type().String(), dst.Type().String())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertSlice(src reflect.Value, dst reflect.Value) error {
|
||||
if dst.Kind() != reflect.Array && dst.Kind() != reflect.Slice {
|
||||
return fmt.Errorf("error type:%s", dst.Type().String())
|
||||
}
|
||||
l := src.Len()
|
||||
target := reflect.MakeSlice(dst.Type(), l, l)
|
||||
if dst.CanSet() {
|
||||
dst.Set(target)
|
||||
}
|
||||
for i := 0; i < l; i++ {
|
||||
srcValue := src.Index(i)
|
||||
if srcValue.Kind() == reflect.Ptr || srcValue.Kind() == reflect.Interface {
|
||||
srcValue = srcValue.Elem()
|
||||
}
|
||||
if f, ok := mapHandlers[srcValue.Kind()]; ok {
|
||||
err := f(srcValue, dst.Index(i))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertMap(src reflect.Value, dst reflect.Value) error {
|
||||
if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct {
|
||||
if src.Kind() == reflect.Interface && dst.IsValid() {
|
||||
return convertMap(src.Elem(), dst)
|
||||
} else {
|
||||
return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String())
|
||||
}
|
||||
}
|
||||
dstType := dst.Type()
|
||||
num := dstType.NumField()
|
||||
|
||||
exist := map[string]int{}
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
k := dstType.Field(i).Tag.Get("json")
|
||||
if k == "" {
|
||||
k = dstType.Field(i).Name
|
||||
}
|
||||
if strings.Contains(k, ",") {
|
||||
taglist := strings.Split(k, ",")
|
||||
if taglist[0] == "" {
|
||||
k = dstType.Field(i).Name
|
||||
} else {
|
||||
k = taglist[0]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
exist[k] = i
|
||||
}
|
||||
|
||||
keys := src.MapKeys()
|
||||
|
||||
for _, key := range keys {
|
||||
if index, ok := exist[key.String()]; ok {
|
||||
v := dst.Field(index)
|
||||
|
||||
if v.Kind() == reflect.Struct {
|
||||
err := convertMap(src.MapIndex(key), v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if v.CanSet() {
|
||||
if v.Type() == src.MapIndex(key).Elem().Type() {
|
||||
v.Set(src.MapIndex(key).Elem())
|
||||
} else if src.MapIndex(key).Elem().CanConvert(v.Type()) {
|
||||
v.Set(src.MapIndex(key).Elem().Convert(v.Type()))
|
||||
} else if f, ok := mapHandlers[src.MapIndex(key).Elem().Kind()]; ok && f != nil {
|
||||
err := f(src.MapIndex(key).Elem(), v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("error type:d(%s)s(%s)", v.Type(), src.Type())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -751,3 +751,121 @@ func TestSortByKey(t *testing.T) {
|
||||
|
||||
assert.Equal(expected2, result2)
|
||||
}
|
||||
|
||||
type (
|
||||
Person struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
Phone string `json:"phone"`
|
||||
Address *Address `json:"address"`
|
||||
}
|
||||
|
||||
Address struct {
|
||||
Street string `json:"street"`
|
||||
Number int `json:"number"`
|
||||
}
|
||||
)
|
||||
|
||||
func TestStructType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestStructType")
|
||||
|
||||
src := map[string]interface{}{
|
||||
"name": "Nothin",
|
||||
"age": 28,
|
||||
"phone": "123456789",
|
||||
"address": map[string]interface{}{
|
||||
"street": "test",
|
||||
"number": 1,
|
||||
},
|
||||
}
|
||||
|
||||
var p Person
|
||||
err := MapTo(src, &p)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(src["name"], p.Name)
|
||||
assert.Equal(src["age"], p.Age)
|
||||
assert.Equal(src["phone"], p.Phone)
|
||||
assert.Equal("test", p.Address.Street)
|
||||
assert.Equal(1, p.Address.Number)
|
||||
}
|
||||
|
||||
func TestBaseType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBaseType")
|
||||
|
||||
tc := map[string]interface{}{
|
||||
"i32": -32,
|
||||
"i8": -8,
|
||||
"i16": -16,
|
||||
"i64": -64,
|
||||
"i": -1,
|
||||
"u32": 32,
|
||||
"u8": 8,
|
||||
"u16": 16,
|
||||
"u64": 64,
|
||||
"u": 1,
|
||||
"tf": true,
|
||||
"f32": 1.32,
|
||||
"f64": 1.64,
|
||||
"str": "hello mapto",
|
||||
"complex": 1 + 3i,
|
||||
}
|
||||
|
||||
type BaseType struct {
|
||||
I int `json:"i"`
|
||||
I8 int8 `json:"i8"`
|
||||
I16 int16 `json:"i16"`
|
||||
I32 int32 `json:"i32"`
|
||||
I64 int64 `json:"i64"`
|
||||
U uint `json:"u"`
|
||||
U8 uint8 `json:"u8"`
|
||||
U16 uint16 `json:"u16"`
|
||||
U32 uint32 `json:"u32"`
|
||||
U64 uint64 `json:"u64"`
|
||||
F32 float32 `json:"f32"`
|
||||
F64 float64 `json:"f64"`
|
||||
Tf bool `json:"tf"`
|
||||
Str string `json:"str"`
|
||||
Comp complex128 `json:"complex"`
|
||||
}
|
||||
|
||||
var dist BaseType
|
||||
err := MapTo(tc, &dist)
|
||||
assert.IsNil(err)
|
||||
|
||||
var number float64
|
||||
|
||||
MapTo(tc["i"], &number)
|
||||
assert.EqualValues(-1, number)
|
||||
|
||||
MapTo(tc["i8"], &number)
|
||||
assert.EqualValues(-8, number)
|
||||
|
||||
MapTo(tc["i16"], &number)
|
||||
assert.EqualValues(-16, number)
|
||||
|
||||
MapTo(tc["i32"], &number)
|
||||
assert.EqualValues(-32, number)
|
||||
|
||||
MapTo(tc["i64"], &number)
|
||||
assert.EqualValues(-64, number)
|
||||
|
||||
MapTo(tc["u"], &number)
|
||||
assert.EqualValues(1, number)
|
||||
|
||||
MapTo(tc["u8"], &number)
|
||||
assert.EqualValues(8, number)
|
||||
|
||||
MapTo(tc["u16"], &number)
|
||||
assert.EqualValues(16, number)
|
||||
|
||||
MapTo(tc["u32"], &number)
|
||||
assert.EqualValues(32, number)
|
||||
|
||||
MapTo(tc["u64"], &number)
|
||||
assert.EqualValues(64, number)
|
||||
}
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
package maputil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var mapHandlers = map[reflect.Kind]func(reflect.Value, reflect.Value) error{
|
||||
reflect.String: convertNormal,
|
||||
reflect.Int: convertNormal,
|
||||
reflect.Int16: convertNormal,
|
||||
reflect.Int32: convertNormal,
|
||||
reflect.Int64: convertNormal,
|
||||
reflect.Uint: convertNormal,
|
||||
reflect.Uint16: convertNormal,
|
||||
reflect.Uint32: convertNormal,
|
||||
reflect.Uint64: convertNormal,
|
||||
reflect.Float32: convertNormal,
|
||||
reflect.Float64: convertNormal,
|
||||
reflect.Uint8: convertNormal,
|
||||
reflect.Int8: convertNormal,
|
||||
reflect.Struct: convertNormal,
|
||||
reflect.Complex64: convertNormal,
|
||||
reflect.Complex128: convertNormal,
|
||||
}
|
||||
|
||||
var _ = func() struct{} {
|
||||
mapHandlers[reflect.Map] = convertMap
|
||||
mapHandlers[reflect.Array] = convertSlice
|
||||
mapHandlers[reflect.Slice] = convertSlice
|
||||
return struct{}{}
|
||||
}()
|
||||
|
||||
// MapTo try to map any interface to struct or base type
|
||||
/*
|
||||
Eg:
|
||||
v := map[string]interface{}{
|
||||
"service":map[string]interface{}{
|
||||
"ip":"127.0.0.1",
|
||||
"port":1234,
|
||||
},
|
||||
version:"v1.0.01"
|
||||
}
|
||||
|
||||
type Target struct {
|
||||
Service struct {
|
||||
Ip string `json:"ip"`
|
||||
Port int `json:"port"`
|
||||
} `json:"service"`
|
||||
Ver string `json:"version"`
|
||||
}
|
||||
|
||||
var dist Target
|
||||
if err := maputil.MapTo(v,&dist); err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(dist)
|
||||
|
||||
*/
|
||||
// Play: https://go.dev/play/p/4K7KBEPgS5M
|
||||
func MapTo(src any, dst any) error {
|
||||
dstRef := reflect.ValueOf(dst)
|
||||
|
||||
if dstRef.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("dst is not ptr")
|
||||
}
|
||||
|
||||
dstElem := dstRef.Type().Elem()
|
||||
if dstElem.Kind() == reflect.Struct {
|
||||
srcMap := src.(map[string]interface{})
|
||||
return MapToStruct(srcMap, dst)
|
||||
}
|
||||
|
||||
dstRef = reflect.Indirect(dstRef)
|
||||
|
||||
srcRef := reflect.ValueOf(src)
|
||||
if srcRef.Kind() == reflect.Ptr || srcRef.Kind() == reflect.Interface {
|
||||
srcRef = srcRef.Elem()
|
||||
}
|
||||
|
||||
if f, ok := mapHandlers[srcRef.Kind()]; ok {
|
||||
return f(srcRef, dstRef)
|
||||
}
|
||||
|
||||
return fmt.Errorf("no implemented:%s", srcRef.Type())
|
||||
}
|
||||
|
||||
func convertNormal(src reflect.Value, dst reflect.Value) error {
|
||||
if dst.CanSet() {
|
||||
if src.Type() == dst.Type() {
|
||||
dst.Set(src)
|
||||
} else if src.CanConvert(dst.Type()) {
|
||||
dst.Set(src.Convert(dst.Type()))
|
||||
} else {
|
||||
return fmt.Errorf("can not convert:%s:%s", src.Type().String(), dst.Type().String())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertSlice(src reflect.Value, dst reflect.Value) error {
|
||||
if dst.Kind() != reflect.Array && dst.Kind() != reflect.Slice {
|
||||
return fmt.Errorf("error type:%s", dst.Type().String())
|
||||
}
|
||||
l := src.Len()
|
||||
target := reflect.MakeSlice(dst.Type(), l, l)
|
||||
if dst.CanSet() {
|
||||
dst.Set(target)
|
||||
}
|
||||
for i := 0; i < l; i++ {
|
||||
srcValue := src.Index(i)
|
||||
if srcValue.Kind() == reflect.Ptr || srcValue.Kind() == reflect.Interface {
|
||||
srcValue = srcValue.Elem()
|
||||
}
|
||||
if f, ok := mapHandlers[srcValue.Kind()]; ok {
|
||||
err := f(srcValue, dst.Index(i))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertMap(src reflect.Value, dst reflect.Value) error {
|
||||
if src.Kind() != reflect.Map || dst.Kind() != reflect.Struct {
|
||||
if src.Kind() == reflect.Interface && dst.IsValid() {
|
||||
return convertMap(src.Elem(), dst)
|
||||
} else {
|
||||
return fmt.Errorf("src or dst type error,%s,%s", src.Type().String(), dst.Type().String())
|
||||
}
|
||||
}
|
||||
dstType := dst.Type()
|
||||
num := dstType.NumField()
|
||||
|
||||
exist := map[string]int{}
|
||||
|
||||
for i := 0; i < num; i++ {
|
||||
k := dstType.Field(i).Tag.Get("json")
|
||||
if k == "" {
|
||||
k = dstType.Field(i).Name
|
||||
}
|
||||
if strings.Contains(k, ",") {
|
||||
taglist := strings.Split(k, ",")
|
||||
if taglist[0] == "" {
|
||||
k = dstType.Field(i).Name
|
||||
} else {
|
||||
k = taglist[0]
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
exist[k] = i
|
||||
}
|
||||
|
||||
keys := src.MapKeys()
|
||||
|
||||
for _, key := range keys {
|
||||
if index, ok := exist[key.String()]; ok {
|
||||
v := dst.Field(index)
|
||||
|
||||
if v.Kind() == reflect.Struct {
|
||||
err := convertMap(src.MapIndex(key), v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if v.CanSet() {
|
||||
if v.Type() == src.MapIndex(key).Elem().Type() {
|
||||
v.Set(src.MapIndex(key).Elem())
|
||||
} else if src.MapIndex(key).Elem().CanConvert(v.Type()) {
|
||||
v.Set(src.MapIndex(key).Elem().Convert(v.Type()))
|
||||
} else if f, ok := mapHandlers[src.MapIndex(key).Elem().Kind()]; ok && f != nil {
|
||||
err := f(src.MapIndex(key).Elem(), v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("error type:d(%s)s(%s)", v.Type(), src.Type())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package maputil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/duke-git/lancet/v2/internal"
|
||||
)
|
||||
|
||||
type (
|
||||
Person struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
Phone string `json:"phone"`
|
||||
Address *Address `json:"address"`
|
||||
}
|
||||
|
||||
Address struct {
|
||||
Street string `json:"street"`
|
||||
Number int `json:"number"`
|
||||
}
|
||||
)
|
||||
|
||||
func TestStructType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestStructType")
|
||||
|
||||
src := map[string]interface{}{
|
||||
"name": "Nothin",
|
||||
"age": 28,
|
||||
"phone": "123456789",
|
||||
"address": map[string]interface{}{
|
||||
"street": "test",
|
||||
"number": 1,
|
||||
},
|
||||
}
|
||||
|
||||
var p Person
|
||||
err := MapTo(src, &p)
|
||||
assert.IsNil(err)
|
||||
|
||||
assert.Equal(src["name"], p.Name)
|
||||
assert.Equal(src["age"], p.Age)
|
||||
assert.Equal(src["phone"], p.Phone)
|
||||
assert.Equal("test", p.Address.Street)
|
||||
assert.Equal(1, p.Address.Number)
|
||||
}
|
||||
|
||||
func TestBaseType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestBaseType")
|
||||
|
||||
tc := map[string]interface{}{
|
||||
"i32": -32,
|
||||
"i8": -8,
|
||||
"i16": -16,
|
||||
"i64": -64,
|
||||
"i": -1,
|
||||
"u32": 32,
|
||||
"u8": 8,
|
||||
"u16": 16,
|
||||
"u64": 64,
|
||||
"u": 1,
|
||||
"tf": true,
|
||||
"f32": 1.32,
|
||||
"f64": 1.64,
|
||||
"str": "hello mapto",
|
||||
"complex": 1 + 3i,
|
||||
}
|
||||
|
||||
type BaseType struct {
|
||||
I int `json:"i"`
|
||||
I8 int8 `json:"i8"`
|
||||
I16 int16 `json:"i16"`
|
||||
I32 int32 `json:"i32"`
|
||||
I64 int64 `json:"i64"`
|
||||
U uint `json:"u"`
|
||||
U8 uint8 `json:"u8"`
|
||||
U16 uint16 `json:"u16"`
|
||||
U32 uint32 `json:"u32"`
|
||||
U64 uint64 `json:"u64"`
|
||||
F32 float32 `json:"f32"`
|
||||
F64 float64 `json:"f64"`
|
||||
Tf bool `json:"tf"`
|
||||
Str string `json:"str"`
|
||||
Comp complex128 `json:"complex"`
|
||||
}
|
||||
|
||||
var dist BaseType
|
||||
err := MapTo(tc, &dist)
|
||||
assert.IsNil(err)
|
||||
|
||||
var number float64
|
||||
|
||||
MapTo(tc["i"], &number)
|
||||
assert.EqualValues(-1, number)
|
||||
|
||||
MapTo(tc["i8"], &number)
|
||||
assert.EqualValues(-8, number)
|
||||
|
||||
MapTo(tc["i16"], &number)
|
||||
assert.EqualValues(-16, number)
|
||||
|
||||
MapTo(tc["i32"], &number)
|
||||
assert.EqualValues(-32, number)
|
||||
|
||||
MapTo(tc["i64"], &number)
|
||||
assert.EqualValues(-64, number)
|
||||
|
||||
MapTo(tc["u"], &number)
|
||||
assert.EqualValues(1, number)
|
||||
|
||||
MapTo(tc["u8"], &number)
|
||||
assert.EqualValues(8, number)
|
||||
|
||||
MapTo(tc["u16"], &number)
|
||||
assert.EqualValues(16, number)
|
||||
|
||||
MapTo(tc["u32"], &number)
|
||||
assert.EqualValues(32, number)
|
||||
|
||||
MapTo(tc["u64"], &number)
|
||||
assert.EqualValues(64, number)
|
||||
}
|
||||
@@ -1386,3 +1386,15 @@ func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
|
||||
|
||||
return paddedSlice
|
||||
}
|
||||
|
||||
// Frequency counts the frequency of each element in the slice.
|
||||
// Play: todo
|
||||
func Frequency[T comparable](slice []T) map[T]int {
|
||||
result := make(map[T]int)
|
||||
|
||||
for _, v := range slice {
|
||||
result[v]++
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -1251,3 +1251,13 @@ func ExampleMapConcurrent() {
|
||||
// Output:
|
||||
// [1 4 9 16 25 36]
|
||||
}
|
||||
|
||||
func ExampleFrequency() {
|
||||
strs := []string{"a", "b", "b", "c", "c", "c"}
|
||||
result := Frequency(strs)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// map[a:1 b:2 c:3]
|
||||
}
|
||||
|
||||
@@ -1764,5 +1764,55 @@ func TestFilterConcurrent(t *testing.T) {
|
||||
actual := FilterConcurrent(nums, func(_, n int) bool { return n > 3 }, 4)
|
||||
assert.Equal(expected, actual)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFrequency(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestFrequency")
|
||||
|
||||
t.Run("empty slice", func(t *testing.T) {
|
||||
result := Frequency([]int{})
|
||||
assert.Equal(map[int]int{}, result)
|
||||
})
|
||||
|
||||
t.Run("int slice", func(t *testing.T) {
|
||||
nums := []int{1, 2, 2, 3, 3, 3}
|
||||
expected := map[int]int{1: 1, 2: 2, 3: 3}
|
||||
result := Frequency(nums)
|
||||
|
||||
assert.Equal(expected, result)
|
||||
})
|
||||
|
||||
t.Run("string slice", func(t *testing.T) {
|
||||
strs := []string{"a", "b", "b", "c", "c", "c"}
|
||||
expected := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||
result := Frequency(strs)
|
||||
|
||||
assert.Equal(expected, result)
|
||||
})
|
||||
|
||||
t.Run("struct slice", func(t *testing.T) {
|
||||
type student struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
students := []student{
|
||||
{Name: "a", Age: 11},
|
||||
{Name: "b", Age: 12},
|
||||
{Name: "a", Age: 13},
|
||||
{Name: "c", Age: 14},
|
||||
}
|
||||
|
||||
expected := map[student]int{
|
||||
{Name: "a", Age: 11}: 1,
|
||||
{Name: "a", Age: 13}: 1,
|
||||
{Name: "b", Age: 12}: 1,
|
||||
{Name: "c", Age: 14}: 1,
|
||||
}
|
||||
result := Frequency(students)
|
||||
|
||||
assert.Equal(expected, result)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -704,3 +704,34 @@ func Rotate(str string, shift int) string {
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// TemplateReplace replaces the placeholders in the template string with the corresponding values in the data map.
|
||||
// The placeholders are enclosed in curly braces, e.g. {key}.
|
||||
// for example, the template string is "Hello, {name}!", and the data map is {"name": "world"},
|
||||
// the result will be "Hello, world!".
|
||||
// Play: todo
|
||||
func TemplateReplace(template string, data map[string]string) string {
|
||||
re := regexp.MustCompile(`\{(\w+)\}`)
|
||||
|
||||
result := re.ReplaceAllStringFunc(template, func(s string) string {
|
||||
key := strings.Trim(s, "{}")
|
||||
if val, ok := data[key]; ok {
|
||||
return val
|
||||
}
|
||||
|
||||
return s
|
||||
})
|
||||
|
||||
result = strings.ReplaceAll(result, "{{", "{")
|
||||
result = strings.ReplaceAll(result, "}}", "}")
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// RegexMatchAllGroups Matches all subgroups in a string using a regular expression and returns the result.
|
||||
// Play: todo
|
||||
func RegexMatchAllGroups(pattern, str string) [][]string {
|
||||
re := regexp.MustCompile(pattern)
|
||||
matches := re.FindAllStringSubmatch(str, -1)
|
||||
return matches
|
||||
}
|
||||
|
||||
@@ -724,3 +724,32 @@ func ExampleRotate() {
|
||||
// oHell
|
||||
// loHel
|
||||
}
|
||||
|
||||
func ExampleTemplateReplace() {
|
||||
template := `Hello, my name is {name}, I'm {age} years old.`
|
||||
data := map[string]string{
|
||||
"name": "Bob",
|
||||
"age": "20",
|
||||
}
|
||||
|
||||
result := TemplateReplace(template, data)
|
||||
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// Hello, my name is Bob, I'm 20 years old.
|
||||
}
|
||||
|
||||
func ExampleRegexMatchAllGroups() {
|
||||
pattern := `(\w+\.+\w+)@(\w+)\.(\w+)`
|
||||
str := "Emails: john.doe@example.com and jane.doe@example.com"
|
||||
|
||||
result := RegexMatchAllGroups(pattern, str)
|
||||
|
||||
fmt.Println(result[0])
|
||||
fmt.Println(result[1])
|
||||
|
||||
// Output:
|
||||
// [john.doe@example.com john.doe example com]
|
||||
// [jane.doe@example.com jane.doe example com]
|
||||
}
|
||||
|
||||
@@ -766,3 +766,90 @@ func TestRotate(t *testing.T) {
|
||||
assert.Equal(tt.expected, Rotate(tt.input, tt.shift))
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateReplace(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestTemplateReplace")
|
||||
|
||||
t.Run("basic", func(t *testing.T) {
|
||||
template := `Hello, my name is {name}, I'm {age} years old.`
|
||||
data := map[string]string{
|
||||
"name": "Bob",
|
||||
"age": "20",
|
||||
}
|
||||
|
||||
expected := `Hello, my name is Bob, I'm 20 years old.`
|
||||
result := TemplateReplace(template, data)
|
||||
|
||||
assert.Equal(expected, result)
|
||||
})
|
||||
|
||||
t.Run("not found", func(t *testing.T) {
|
||||
template := `Hello, my name is {name}, I'm {age} years old.`
|
||||
data := map[string]string{
|
||||
"name": "Bob",
|
||||
}
|
||||
|
||||
expected := `Hello, my name is Bob, I'm {age} years old.`
|
||||
result := TemplateReplace(template, data)
|
||||
|
||||
assert.Equal(expected, result)
|
||||
})
|
||||
|
||||
t.Run("brackets", func(t *testing.T) {
|
||||
template := `Hello, my name is {name}, I'm {{age}} years old.`
|
||||
data := map[string]string{
|
||||
"name": "Bob",
|
||||
"age": "20",
|
||||
}
|
||||
|
||||
expected := `Hello, my name is Bob, I'm {20} years old.`
|
||||
result := TemplateReplace(template, data)
|
||||
|
||||
assert.Equal(expected, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRegexMatchAllGroups(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestRegexMatchAllGroups")
|
||||
|
||||
tests := []struct {
|
||||
pattern string
|
||||
str string
|
||||
expected [][]string
|
||||
}{
|
||||
{
|
||||
pattern: `(\w+\.+\w+)@(\w+)\.(\w+)`,
|
||||
str: "Emails: john.doe@example.com and jane.doe@example.com",
|
||||
expected: [][]string{{"john.doe@example.com", "john.doe", "example", "com"}, {"jane.doe@example.com", "jane.doe", "example", "com"}},
|
||||
},
|
||||
{
|
||||
pattern: `(\d+)`,
|
||||
str: "No numbers here!",
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
pattern: `(\d{3})-(\d{2})-(\d{4})`,
|
||||
str: "My number is 123-45-6789",
|
||||
expected: [][]string{{"123-45-6789", "123", "45", "6789"}},
|
||||
},
|
||||
{
|
||||
pattern: `(\w+)\s(\d+)`,
|
||||
str: "Item A 123, Item B 456",
|
||||
expected: [][]string{{"A 123", "A", "123"}, {"B 456", "B", "456"}},
|
||||
},
|
||||
{
|
||||
pattern: `(\d{2})-(\d{2})-(\d{4})`,
|
||||
str: "Dates: 01-01-2020, 12-31-1999",
|
||||
expected: [][]string{{"01-01-2020", "01", "01", "2020"}, {"12-31-1999", "12", "31", "1999"}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
result := RegexMatchAllGroups(tt.pattern, tt.str)
|
||||
assert.Equal(tt.expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user