mirror of
https://github.com/duke-git/lancet.git
synced 2026-03-01 00:35:28 +08:00
Compare commits
11 Commits
a06bb8ee6a
...
v2.2.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d4fc981b6 | ||
|
|
4c21fe700c | ||
|
|
b7370e8ef8 | ||
|
|
38920e3be6 | ||
|
|
a630a7cda9 | ||
|
|
66dfd9c4fd | ||
|
|
be62aaac9b | ||
|
|
e0c9ccbce3 | ||
|
|
d2d1e5a055 | ||
|
|
bbc58c7e46 | ||
|
|
ac2ecceaec |
25
README.md
25
README.md
@@ -4,7 +4,7 @@
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -706,6 +706,8 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
||||
[[play](https://go.dev/play/p/OExTkhGEd3_u)]
|
||||
- **<big>WriteCsvFile</big>** : write content to target csv file.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteCsvFile)]
|
||||
- **<big>WriteMapsToCsv</big>** : write slice of map to csv file.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteMapsToCsv)]
|
||||
[[play](https://go.dev/play/p/dAXm58Q5U1o)]
|
||||
- **<big>WriteBytesToFile</big>** : write bytes to target file.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteBytesToFile)]
|
||||
@@ -1178,9 +1180,10 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>DifferenceWith</big>** : accepts comparator which is invoked to compare elements of slice to values.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DifferenceWith)]
|
||||
[[play](https://go.dev/play/p/v2U2deugKuV)]
|
||||
- **<big>DeleteAt</big>** : delete the element of slice from specific start index to end index - 1.
|
||||
- **<big>DeleteAt</big>** : delete the element of slice at index.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DeleteAt)]
|
||||
[[play](https://go.dev/play/p/pJ-d6MUWcvK)]
|
||||
- **<big>DeleteRange</big>** : delete the element of slice from start index to end index(exclude).
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#DeleteRange)]
|
||||
- **<big>Drop</big>** : drop n elements from the start of a slice.
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/slice.md#Drop)]
|
||||
[[play](https://go.dev/play/p/jnPO2yQsT8H)]
|
||||
@@ -1942,22 +1945,6 @@ import "github.com/duke-git/lancet/v2/xerror"
|
||||
|
||||
#### [Contributing Guide](./CONTRIBUTING.en-US.md)
|
||||
|
||||
|
||||
## Sponsor
|
||||
|
||||
Hello, I am a software developer and have been engaged in development work for 13 years. Love open source software. And be willing to put in the energy for it. I am the creator of project lancet. Since Lancet was released as open source two years ago, it has been used by more than 1,000 internal and external projects. lancet will always be free for all users. Your support is a powerful encouragement for me to continue my struggle. Thanks! You can use WeChat to scans the following QR code or clicks the following sponsor button to initiate sponsorship.
|
||||
|
||||
<div style="position: relative;display: inline-block;">
|
||||
<img src="./docs/public/wechat_pay.png" width="260" height="260"/>
|
||||
<a href="https://en.liberapay.com/Duke_Du/donate" target="\_blank"><img src="./docs/public/sponsor_btn.png" width="220" height="60"/></a>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
*Donated funds will be used to maintain [lancet](https://www.golancet.cn/en/) website and pay for cloud server costs. Or just buy me a cup of ☕️ when I'm sleepy writing code.*
|
||||
|
||||
|
||||
## Contributors
|
||||
Thank you to all the people who contributed to lancet!
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<br/>
|
||||
|
||||

|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://github.com/duke-git/lancet/releases)
|
||||
[](https://pkg.go.dev/github.com/duke-git/lancet/v2)
|
||||
[](https://goreportcard.com/report/github.com/duke-git/lancet/v2)
|
||||
[](https://github.com/duke-git/lancet/actions/workflows/codecov.yml)
|
||||
@@ -705,8 +705,10 @@ import "github.com/duke-git/lancet/v2/fileutil"
|
||||
- **<big>ReadCsvFile</big>** : 读取 csv 文件内容到切片。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadCsvFile)]
|
||||
[[play](https://go.dev/play/p/OExTkhGEd3_u)]
|
||||
- **<big>WriteCsvFile</big>** : 向 csv 文件写入内容。
|
||||
- **<big>WriteCsvFile</big>** : 向csv文件写入切片数据。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteCsvFile)]
|
||||
- **<big>WriteMapsToCsv</big>** : 将map切片写入csv文件中。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteMapsToCsv)]
|
||||
[[play](https://go.dev/play/p/dAXm58Q5U1o)]
|
||||
- **<big>WriteBytesToFile</big>** : 将 bytes 写入文件。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteBytesToFile)]
|
||||
@@ -1177,9 +1179,10 @@ import "github.com/duke-git/lancet/v2/slice"
|
||||
- **<big>DifferenceWith</big>** : 接受比较器函数,该比较器被调用以将切片的元素与值进行比较。 结果值的顺序和引用由第一个切片确定。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DifferenceWith)]
|
||||
[[play](https://go.dev/play/p/v2U2deugKuV)]
|
||||
- **<big>DeleteAt</big>** : 删除切片中指定开始索引到结束索引的元素。
|
||||
- **<big>DeleteAt</big>** : 删除切片中指定索引到的元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DeleteAt)]
|
||||
[[play](https://go.dev/play/p/pJ-d6MUWcvK)]
|
||||
- **<big>DeleteRange</big>** : 删除切片中指定开始索引到结束索引的元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#DeleteRange)]
|
||||
- **<big>Drop</big>** : 从切片头部删除 n 个元素。
|
||||
[[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/slice.md#Drop)]
|
||||
[[play](https://go.dev/play/p/jnPO2yQsT8H)]
|
||||
@@ -1943,23 +1946,9 @@ import "github.com/duke-git/lancet/v2/xerror"
|
||||
|
||||
#### [贡献代码指南](./CONTRIBUTING.zh-CN.md)
|
||||
|
||||
## 赞助
|
||||
|
||||
您好,我是一名软件开发者,从事开发工作 13 年。热爱软件开源。并愿意为此付出精力。是开源项目 lancet 的作者。Lancet 自两年前开源发布以来,已有超过 1000 个内外部项目使用。lancet 一直会对所有用户免费。您的支持是对我继续奋斗的有力鼓励。谢谢! 微信扫描以下二维码或点击以下赞助按钮发起赞助。
|
||||
|
||||
<div style="position: relative;display: inline-block;">
|
||||
<img src="./docs/public/wechat_pay.png" width="260" height="260"/>
|
||||
<a href="https://liberapay.com/Duke_Du/donate" target="\_blank"><img src="./docs/public/sponsor_btn.png" width="220" height="60"/></a>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
_捐赠的资金将用于后续[lancet](https://www.golancet.cn/)官网的维护和云服务器的费用支付。或者当我写代码困倦时,给我买杯 ☕️。_
|
||||
|
||||
## 贡献者
|
||||
|
||||
感谢所有为 lancet 贡献过代码的人!
|
||||
感谢所有为lancet贡献过代码的人!
|
||||
|
||||
<a href="https://github.com/duke-git/lancet/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=duke-git/lancet" />
|
||||
|
||||
@@ -45,6 +45,7 @@ import (
|
||||
- [Sha](#Sha)
|
||||
- [ReadCsvFile](#ReadCsvFile)
|
||||
- [WriteCsvFile](#WriteCsvFile)
|
||||
- [WriteMapsToCsv](#WriteMapsToCsv)
|
||||
- [WriteStringToFile](#WriteStringToFile)
|
||||
- [WriteBytesToFile](#WriteBytesToFile)
|
||||
- [ReadFile](#ReadFile)
|
||||
@@ -750,7 +751,12 @@ func main() {
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func WriteMapsToCsv(filepath string, records []map[string]string, append_to_existing_file bool, delimiter ...rune) error
|
||||
// filepath: CSV文件路径。
|
||||
// records: 写入文件的map切片。map值必须为基本类型。会以map键的字母顺序写入。
|
||||
// appendToExistingFile: 是否为追加写模式。
|
||||
// delimiter: CSV文件分割符。
|
||||
// headers: CSV文件表头顺序(需要与map key保持一致),不指定时按字母排序。
|
||||
func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune, headers ...[]string) error
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
@@ -770,23 +776,24 @@ func main() {
|
||||
f, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0777)
|
||||
defer f.Close()
|
||||
|
||||
records := []map[string]string{
|
||||
{"Name": "Lili", "Age": "22", "gender": "female"},
|
||||
{"Name": "Jim", "Age": "21", "gender": "male"},
|
||||
records := []map[string]any{
|
||||
{"Name": "Lili", "Age": "22", "Gender": "female"},
|
||||
{"Name": "Jim", "Age": "21", "Gender": "male"},
|
||||
}
|
||||
|
||||
err := WriteMapsToCsv(csvFilePath, records, false, ';')
|
||||
headers := []string{"Name", "Age", "Gender"}
|
||||
err := WriteMapsToCsv(csvFilePath, records, false, ';', headers)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
content, err := ReadCsvFile(csvFilePath, ';')
|
||||
content, err := fileutil.ReadCsvFile(csvFilePath, ';')
|
||||
|
||||
fmt.Println(content)
|
||||
|
||||
// Output:
|
||||
// [[Name Age gender] [Lili 22 female] [Jim 21 male]]
|
||||
// [[Name Age Gender] [Lili 22 female] [Jim 21 male]]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
- [DifferenceBy](#DifferenceBy)
|
||||
- [DifferenceWith](#DifferenceWith)
|
||||
- [DeleteAt](#DeleteAt)
|
||||
- [DeleteRange](#DeleteRange)
|
||||
- [Drop](#Drop)
|
||||
- [DropRight](#DropRight)
|
||||
- [DropWhile](#DropWhile)
|
||||
@@ -516,12 +517,12 @@ func main() {
|
||||
|
||||
### <span id="DeleteAt">DeleteAt</span>
|
||||
|
||||
<p>删除切片中指定开始索引到结束索引的元素</p>
|
||||
<p>删除切片中指定索引的元素(不修改原切片)。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func DeleteAt[T any](slice []T, start int, end ...int)
|
||||
func DeleteAt[T any](slice []T, index int) []T
|
||||
```
|
||||
|
||||
<b>示例:<span style="float:right;display:inline-block;">[运行](https://go.dev/play/p/pJ-d6MUWcvK)</span></b>
|
||||
@@ -533,18 +534,66 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := slice.DeleteAt([]string{"a", "b", "c"}, -1)
|
||||
result2 := slice.DeleteAt([]string{"a", "b", "c"}, 0)
|
||||
result3 := slice.DeleteAt([]string{"a", "b", "c"}, 0, 2)
|
||||
chars := []string{"a", "b", "c", "d", "e"}
|
||||
|
||||
result1 := slice.DeleteAt(chars, 0)
|
||||
result2 := slice.DeleteAt(chars, 4)
|
||||
result3 := slice.DeleteAt(chars, 5)
|
||||
result4 := slice.DeleteAt(chars, 6)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [b c]
|
||||
// [c]
|
||||
// [b c d e]
|
||||
// [a b c d]
|
||||
// [a b c d]
|
||||
// [a b c d]
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DeleteRange">DeleteRange</span>
|
||||
|
||||
<p>删除切片中指定索引范围的元素(不修改原切片)。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func DeleteRange[T any](slice []T, start, end int) []T
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
chars := []string{"a", "b", "c", "d", "e"}
|
||||
|
||||
result1 := DeleteRange(chars, 0, 0)
|
||||
result2 := DeleteRange(chars, 0, 1)
|
||||
result3 := DeleteRange(chars, 0, 3)
|
||||
result4 := DeleteRange(chars, 0, 4)
|
||||
result5 := DeleteRange(chars, 0, 5)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
|
||||
// Output:
|
||||
// [a b c d e]
|
||||
// [b c d e]
|
||||
// [d e]
|
||||
// [e]
|
||||
// []
|
||||
|
||||
}
|
||||
```
|
||||
@@ -2475,18 +2524,18 @@ import (
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
|
||||
result1 := slice.Partition(nums)
|
||||
result2 := slice.Partition(nums, func(n int) bool { return n%2 == 0 })
|
||||
result3 := slice.Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 })
|
||||
result1 := slice.Partition(nums)
|
||||
result2 := slice.Partition(nums, func(n int) bool { return n%2 == 0 })
|
||||
result3 := slice.Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 })
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// [[1 2 3 4 5]]
|
||||
// [[2 4] [1 3 5]]
|
||||
// [[1 2] [3 4] [5]]
|
||||
// Output:
|
||||
// [[1 2 3 4 5]]
|
||||
// [[2 4] [1 3 5]]
|
||||
// [[1 2] [3 4] [5]]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2510,13 +2559,13 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
|
||||
val, idx := slice.Random(nums)
|
||||
if idx >= 0 && idx < len(nums) && slice.Contain(nums, val) {
|
||||
fmt.Println("okk")
|
||||
}
|
||||
// Output:
|
||||
// okk
|
||||
val, idx := slice.Random(nums)
|
||||
if idx >= 0 && idx < len(nums) && slice.Contain(nums, val) {
|
||||
fmt.Println("okk")
|
||||
}
|
||||
// Output:
|
||||
// okk
|
||||
}
|
||||
```
|
||||
@@ -45,6 +45,7 @@ import (
|
||||
- [Sha](#Sha)
|
||||
- [ReadCsvFile](#ReadCsvFile)
|
||||
- [WriteCsvFile](#WriteCsvFile)
|
||||
- [WriteCsvFile](#WriteCsvFile)
|
||||
- [WriteMapsToCsv](#WriteMapsToCsv)
|
||||
- [WriteStringToFile](#WriteStringToFile)
|
||||
- [WriteBytesToFile](#WriteBytesToFile)
|
||||
@@ -751,7 +752,12 @@ func main() {
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func WriteMapsToCsv(filepath string, records []map[string]string, append_to_existing_file bool, delimiter ...rune) error
|
||||
// filepath: path of the CSV file.
|
||||
// records: slice of maps to be written. the value of map should be basic type. The maps will be sorted by key in alphabeta order, then be written into csv file.
|
||||
// appendToExistingFile: If true, data will be appended to the file if it exists.
|
||||
// delimiter: Delimiter to use in the CSV file.
|
||||
// headers: order of the csv column headers, needs to be consistent with the key of the map.
|
||||
func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune, headers ...[]string) error
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
@@ -771,23 +777,24 @@ func main() {
|
||||
f, _ := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0777)
|
||||
defer f.Close()
|
||||
|
||||
records := []map[string]string{
|
||||
{"Name": "Lili", "Age": "22", "gender": "female"},
|
||||
{"Name": "Jim", "Age": "21", "gender": "male"},
|
||||
records := []map[string]any{
|
||||
{"Name": "Lili", "Age": "22", "Gender": "female"},
|
||||
{"Name": "Jim", "Age": "21", "Gender": "male"},
|
||||
}
|
||||
|
||||
err := WriteMapsToCsv(csvFilePath, records, false, ';')
|
||||
headers := []string{"Name", "Age", "Gender"}
|
||||
err := fileutil.WriteMapsToCsv(csvFilePath, records, false, ';', headers)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
content, err := ReadCsvFile(csvFilePath, ';')
|
||||
content, err := fileutil.ReadCsvFile(csvFilePath, ';')
|
||||
|
||||
fmt.Println(content)
|
||||
|
||||
// Output:
|
||||
// [[Name Age gender] [Lili 22 female] [Jim 21 male]]
|
||||
// [[Name Age Gender] [Lili 22 female] [Jim 21 male]]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
- [DifferenceBy](#DifferenceBy)
|
||||
- [DifferenceWith](#DifferenceWith)
|
||||
- [DeleteAt](#DeleteAt)
|
||||
- [DeleteRange](#DeleteRange)
|
||||
- [Drop](#Drop)
|
||||
- [DropRight](#DropRight)
|
||||
- [DropWhile](#DropWhile)
|
||||
@@ -515,12 +516,12 @@ func main() {
|
||||
|
||||
### <span id="DeleteAt">DeleteAt</span>
|
||||
|
||||
<p>Delete the element of slice from start index to end index - 1.</p>
|
||||
<p>Delete delete the element of slice at index.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func DeleteAt[T any](slice []T, start int, end ...int)
|
||||
func DeleteAt[T any](slice []T, index int)
|
||||
```
|
||||
|
||||
<b>Example:<span style="float:right;display:inline-block;">[Run](https://go.dev/play/p/pJ-d6MUWcvK)</span></b>
|
||||
@@ -532,18 +533,66 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
result1 := slice.DeleteAt([]string{"a", "b", "c"}, -1)
|
||||
result2 := slice.DeleteAt([]string{"a", "b", "c"}, 0)
|
||||
result3 := slice.DeleteAt([]string{"a", "b", "c"}, 0, 2)
|
||||
chars := []string{"a", "b", "c", "d", "e"}
|
||||
|
||||
result1 := slice.DeleteAt(chars, 0)
|
||||
result2 := slice.DeleteAt(chars, 4)
|
||||
result3 := slice.DeleteAt(chars, 5)
|
||||
result4 := slice.DeleteAt(chars, 6)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [b c]
|
||||
// [c]
|
||||
// [b c d e]
|
||||
// [a b c d]
|
||||
// [a b c d]
|
||||
// [a b c d]
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="DeleteRange">DeleteRange</span>
|
||||
|
||||
<p>Delete the element of slice from start index to end index(exclude)</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func DeleteRange[T any](slice []T, start, end int) []T
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/slice"
|
||||
)
|
||||
|
||||
func main() {
|
||||
chars := []string{"a", "b", "c", "d", "e"}
|
||||
|
||||
result1 := DeleteRange(chars, 0, 0)
|
||||
result2 := DeleteRange(chars, 0, 1)
|
||||
result3 := DeleteRange(chars, 0, 3)
|
||||
result4 := DeleteRange(chars, 0, 4)
|
||||
result5 := DeleteRange(chars, 0, 5)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
|
||||
// Output:
|
||||
// [a b c d e]
|
||||
// [b c d e]
|
||||
// [d e]
|
||||
// [e]
|
||||
// []
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2473,18 +2522,18 @@ import (
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
|
||||
result1 := slice.Partition(nums)
|
||||
result2 := slice.Partition(nums, func(n int) bool { return n%2 == 0 })
|
||||
result3 := slice.Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 })
|
||||
result1 := slice.Partition(nums)
|
||||
result2 := slice.Partition(nums, func(n int) bool { return n%2 == 0 })
|
||||
result3 := slice.Partition(nums, func(n int) bool { return n == 1 || n == 2 }, func(n int) bool { return n == 2 || n == 3 || n == 4 })
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
|
||||
// Output:
|
||||
// [[1 2 3 4 5]]
|
||||
// [[2 4] [1 3 5]]
|
||||
// [[1 2] [3 4] [5]]
|
||||
// Output:
|
||||
// [[1 2 3 4 5]]
|
||||
// [[2 4] [1 3 5]]
|
||||
// [[1 2] [3 4] [5]]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2507,13 +2556,13 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
nums := []int{1, 2, 3, 4, 5}
|
||||
|
||||
val, idx := slice.Random(nums)
|
||||
if idx >= 0 && idx < len(nums) && slice.Contain(nums, val) {
|
||||
fmt.Println("okk")
|
||||
}
|
||||
// Output:
|
||||
// okk
|
||||
val, idx := slice.Random(nums)
|
||||
if idx >= 0 && idx < len(nums) && slice.Contain(nums, val) {
|
||||
fmt.Println("okk")
|
||||
}
|
||||
// Output:
|
||||
// okk
|
||||
}
|
||||
```
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
@@ -754,33 +755,55 @@ func escapeCSVField(field string, delimiter rune) string {
|
||||
|
||||
// WriteMapsToCsv write slice of map to csv file.
|
||||
// Play: todo
|
||||
func WriteMapsToCsv(filepath string, records []map[string]string, append_to_existing_file bool, delimiter ...rune) error {
|
||||
var datas_to_write [][]string
|
||||
// 标题(列名)
|
||||
var headers []string
|
||||
if len(records) > 0 {
|
||||
for key := range records[0] {
|
||||
headers = append(headers, key)
|
||||
// filepath: Path to the CSV file.
|
||||
// records: Slice of maps to be written. the value of map should be basic type.
|
||||
// the maps will be sorted by key in alphabeta order, then be written into csv file.
|
||||
// appendToExistingFile: If true, data will be appended to the file if it exists.
|
||||
// delimiter: Delimiter to use in the CSV file.
|
||||
// headers: order of the csv column headers, needs to be consistent with the key of the map.
|
||||
func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune,
|
||||
headers ...[]string) error {
|
||||
for _, record := range records {
|
||||
for _, value := range record {
|
||||
if !isCsvSupportedType(value) {
|
||||
return errors.New("unsupported value type detected; only basic types are supported: \nbool, rune, string, int, int64, float32, float64, uint, byte, complex128, complex64, uintptr")
|
||||
}
|
||||
}
|
||||
}
|
||||
// 追加模式不重复写字段名
|
||||
if !append_to_existing_file {
|
||||
datas_to_write = append(datas_to_write, headers)
|
||||
|
||||
var columnHeaders []string
|
||||
if len(headers) > 0 {
|
||||
columnHeaders = headers[0]
|
||||
} else {
|
||||
for key := range records[0] {
|
||||
columnHeaders = append(columnHeaders, key)
|
||||
}
|
||||
// sort keys in alphabeta order
|
||||
sort.Strings(columnHeaders)
|
||||
}
|
||||
// 写入数据行
|
||||
|
||||
var datasToWrite [][]string
|
||||
if !appendToExistingFile {
|
||||
datasToWrite = append(datasToWrite, columnHeaders)
|
||||
}
|
||||
|
||||
for _, record := range records {
|
||||
var row []string
|
||||
for _, header := range headers {
|
||||
row = append(row, record[header])
|
||||
for _, h := range columnHeaders {
|
||||
row = append(row, fmt.Sprintf("%v", record[h]))
|
||||
}
|
||||
datas_to_write = append(datas_to_write, row)
|
||||
datasToWrite = append(datasToWrite, row)
|
||||
}
|
||||
|
||||
return WriteCsvFile(filepath, datasToWrite, appendToExistingFile, delimiter)
|
||||
}
|
||||
|
||||
// check if the value of map which to be written into csv is basic type.
|
||||
func isCsvSupportedType(v interface{}) bool {
|
||||
switch v.(type) {
|
||||
case bool, rune, string, int, int64, float32, float64, uint, byte, complex128, complex64, uintptr:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
// 提取自定义分隔符
|
||||
var sep rune
|
||||
if len(delimiter) > 0 {
|
||||
sep = delimiter[0]
|
||||
} else {
|
||||
sep = ','
|
||||
}
|
||||
return WriteCsvFile(filepath, datas_to_write, append_to_existing_file, sep)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package fileutil
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -331,26 +332,27 @@ func ExampleWriteCsvFile() {
|
||||
// [[Lili 22 female] [Jim 21 male]]
|
||||
}
|
||||
|
||||
// func ExampleWriteMapsToCsv() {
|
||||
// csvFilePath := "./testdata/test3.csv"
|
||||
// records := []map[string]string{
|
||||
// {"Name": "Lili", "Age": "22", "gender": "female"},
|
||||
// {"Name": "Jim", "Age": "21", "gender": "male"},
|
||||
// }
|
||||
func ExampleWriteMapsToCsv() {
|
||||
csvFilePath := "./testdata/test3.csv"
|
||||
records := []map[string]any{
|
||||
{"Name": "Lili", "Age": "22", "Gender": "female"},
|
||||
{"Name": "Jim", "Age": "21", "Gender": "male"},
|
||||
}
|
||||
|
||||
// err := WriteMapsToCsv(csvFilePath, records, false, ';')
|
||||
headers := []string{"Name", "Age", "Gender"}
|
||||
err := WriteMapsToCsv(csvFilePath, records, false, ';', headers)
|
||||
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// content, err := ReadCsvFile(csvFilePath, ';')
|
||||
content, err := ReadCsvFile(csvFilePath, ';')
|
||||
|
||||
// fmt.Println(content) //顺序不固定
|
||||
fmt.Println(content)
|
||||
|
||||
// // Output:
|
||||
// // [[Name Age gender] [Lili 22 female] [Jim 21 male]]
|
||||
// }
|
||||
// Output:
|
||||
// [[Name Age Gender] [Lili 22 female] [Jim 21 male]]
|
||||
}
|
||||
|
||||
func ExampleWriteStringToFile() {
|
||||
filepath := "./test.txt"
|
||||
|
||||
@@ -392,12 +392,13 @@ func TestWriteMapsToCsv(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestWriteMapsToCSV")
|
||||
|
||||
csvFilePath := "./testdata/test4.csv"
|
||||
records := []map[string]string{
|
||||
{"Name": "Lili", "Age": "22", "gender": "female"},
|
||||
{"Name": "Jim", "Age": "21", "gender": "male"},
|
||||
records := []map[string]any{
|
||||
{"Name": "Lili", "Age": "22", "Gender": "female"},
|
||||
{"Name": "Jim", "Age": "21", "Gender": "male"},
|
||||
}
|
||||
|
||||
err := WriteMapsToCsv(csvFilePath, records, false, ';')
|
||||
headers := []string{"Name", "Age", "Gender"}
|
||||
err := WriteMapsToCsv(csvFilePath, records, false, ';', headers)
|
||||
|
||||
assert.IsNil(err)
|
||||
|
||||
@@ -407,7 +408,9 @@ func TestWriteMapsToCsv(t *testing.T) {
|
||||
|
||||
assert.Equal(3, len(content))
|
||||
assert.Equal(3, len(content[0]))
|
||||
// assert.Equal("Lili", content[1][0])
|
||||
assert.Equal("Lili", content[1][0])
|
||||
assert.Equal("22", content[1][1])
|
||||
assert.Equal("female", content[1][2])
|
||||
}
|
||||
|
||||
func TestWriteStringToFile(t *testing.T) {
|
||||
|
||||
6
fileutil/testdata/test3.csv
vendored
6
fileutil/testdata/test3.csv
vendored
@@ -1,3 +1,3 @@
|
||||
Age;gender;Name
|
||||
22;female;Lili
|
||||
21;male;Jim
|
||||
Name;Age;Gender
|
||||
Lili;22;female
|
||||
Jim;21;male
|
||||
|
||||
|
2
fileutil/testdata/test4.csv
vendored
2
fileutil/testdata/test4.csv
vendored
@@ -1,3 +1,3 @@
|
||||
Name;Age;gender
|
||||
Name;Age;Gender
|
||||
Lili;22;female
|
||||
Jim;21;male
|
||||
|
||||
|
0
fileutil/testdata/text.txt
vendored
0
fileutil/testdata/text.txt
vendored
@@ -6,12 +6,11 @@ package formatter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/duke-git/lancet/v2/convertor"
|
||||
"github.com/duke-git/lancet/v2/strutil"
|
||||
"github.com/duke-git/lancet/v2/validator"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
@@ -20,31 +19,24 @@ import (
|
||||
// Comma("12345", "$") => "$12,345", Comma(12345, "$") => "$12,345"
|
||||
// Play: https://go.dev/play/p/eRD5k2vzUVX
|
||||
func Comma[T constraints.Float | constraints.Integer | string](value T, symbol string) string {
|
||||
if validator.IsInt(value) {
|
||||
v, err := convertor.ToInt(value)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return symbol + commaInt(v)
|
||||
}
|
||||
numString := convertor.ToString(value)
|
||||
|
||||
if validator.IsFloat(value) {
|
||||
v, err := convertor.ToFloat(value)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return symbol + commaFloat(v)
|
||||
}
|
||||
|
||||
if strutil.IsString(value) {
|
||||
v := fmt.Sprintf("%v", value)
|
||||
if validator.IsNumberStr(v) {
|
||||
return symbol + commaStr(v)
|
||||
}
|
||||
_, err := strconv.ParseFloat(numString, 64)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return ""
|
||||
index := strings.Index(numString, ".")
|
||||
if index == -1 {
|
||||
index = len(numString)
|
||||
}
|
||||
|
||||
for index > 3 {
|
||||
index = index - 3
|
||||
numString = numString[:index] + "," + numString[index:]
|
||||
}
|
||||
|
||||
return symbol + numString
|
||||
}
|
||||
|
||||
// Pretty data to JSON string.
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// see https://github.com/dustin/go-humanize/blob/master/comma.go
|
||||
func commaInt(v int64) string {
|
||||
sign := ""
|
||||
|
||||
// Min int64 can't be negated to a usable value, so it has to be special cased.
|
||||
if v == math.MinInt64 {
|
||||
return "-9,223,372,036,854,775,808"
|
||||
}
|
||||
|
||||
if v < 0 {
|
||||
sign = "-"
|
||||
v = 0 - v
|
||||
}
|
||||
|
||||
parts := []string{"", "", "", "", "", "", ""}
|
||||
j := len(parts) - 1
|
||||
|
||||
for v > 999 {
|
||||
parts[j] = strconv.FormatInt(v%1000, 10)
|
||||
switch len(parts[j]) {
|
||||
case 2:
|
||||
parts[j] = "0" + parts[j]
|
||||
case 1:
|
||||
parts[j] = "00" + parts[j]
|
||||
}
|
||||
v = v / 1000
|
||||
j--
|
||||
}
|
||||
parts[j] = strconv.Itoa(int(v))
|
||||
return sign + strings.Join(parts[j:], ",")
|
||||
}
|
||||
|
||||
func commaFloat(v float64) string {
|
||||
buf := &bytes.Buffer{}
|
||||
if v < 0 {
|
||||
buf.Write([]byte{'-'})
|
||||
v = 0 - v
|
||||
}
|
||||
|
||||
comma := []byte{','}
|
||||
|
||||
parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".")
|
||||
pos := 0
|
||||
if len(parts[0])%3 != 0 {
|
||||
pos += len(parts[0]) % 3
|
||||
buf.WriteString(parts[0][:pos])
|
||||
buf.Write(comma)
|
||||
}
|
||||
for ; pos < len(parts[0]); pos += 3 {
|
||||
buf.WriteString(parts[0][pos : pos+3])
|
||||
buf.Write(comma)
|
||||
}
|
||||
buf.Truncate(buf.Len() - 1)
|
||||
|
||||
if len(parts) > 1 {
|
||||
buf.Write([]byte{'.'})
|
||||
buf.WriteString(parts[1])
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func commaStr(s string) string {
|
||||
dotIndex := strings.Index(s, ".")
|
||||
if dotIndex != -1 {
|
||||
return commaStrRecursive(s[:dotIndex]) + s[dotIndex:]
|
||||
}
|
||||
|
||||
return commaStrRecursive(s)
|
||||
}
|
||||
|
||||
func commaStrRecursive(s string) string {
|
||||
if len(s) <= 3 {
|
||||
return s
|
||||
}
|
||||
return commaStrRecursive(s[:len(s)-3]) + "," + commaStrRecursive(s[len(s)-3:])
|
||||
}
|
||||
@@ -4,7 +4,9 @@
|
||||
// Package pointer contains some util functions to operate go pointer.
|
||||
package pointer
|
||||
|
||||
import "reflect"
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Of returns a pointer to the value `v`.
|
||||
// Play: https://go.dev/play/p/HFd70x4DrMj
|
||||
@@ -47,5 +49,10 @@ func ExtractPointer(value any) any {
|
||||
if t.Kind() != reflect.Pointer {
|
||||
return value
|
||||
}
|
||||
return ExtractPointer(v.Elem().Interface())
|
||||
|
||||
if v.Elem().IsValid() {
|
||||
return ExtractPointer(v.Elem().Interface())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,20 +8,25 @@ import (
|
||||
crand "crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/duke-git/lancet/v2/mathutil"
|
||||
)
|
||||
|
||||
const (
|
||||
Numeral = "0123456789"
|
||||
LowwerLetters = "abcdefghijklmnopqrstuvwxyz"
|
||||
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
SymbolChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?"
|
||||
MaximumCapacity = 1 << 31
|
||||
Numeral = "0123456789"
|
||||
LowwerLetters = "abcdefghijklmnopqrstuvwxyz"
|
||||
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
Letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
SymbolChars = "!@#$%^&*()_+-=[]{}|;':\",./<>?"
|
||||
)
|
||||
|
||||
var rn = rand.NewSource(time.Now().UnixNano())
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
@@ -108,18 +113,61 @@ func RandSymbolChar(length int) string {
|
||||
return random(SymbolChars, length)
|
||||
}
|
||||
|
||||
// nearestPowerOfTwo 返回一个大于等于cap的最近的2的整数次幂,参考java8的hashmap的tableSizeFor函数
|
||||
func nearestPowerOfTwo(cap int) int {
|
||||
n := cap - 1
|
||||
n |= n >> 1
|
||||
n |= n >> 2
|
||||
n |= n >> 4
|
||||
n |= n >> 8
|
||||
n |= n >> 16
|
||||
if n < 0 {
|
||||
return 1
|
||||
} else if n >= MaximumCapacity {
|
||||
return MaximumCapacity
|
||||
}
|
||||
return n + 1
|
||||
}
|
||||
|
||||
// random generate a random string based on given string range.
|
||||
func random(s string, length int) string {
|
||||
b := make([]byte, length)
|
||||
|
||||
// fix: https://github.com/duke-git/lancet/issues/75
|
||||
// r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
for i := range b {
|
||||
b[i] = s[rand.Int63()%int64(len(s))]
|
||||
// 仿照strings.Builder
|
||||
// 创建一个长度为 length 的字节切片
|
||||
bytes := make([]byte, length)
|
||||
strLength := len(s)
|
||||
if strLength <= 0 {
|
||||
return ""
|
||||
} else if strLength == 1 {
|
||||
for i := 0; i < length; i++ {
|
||||
bytes[i] = s[0]
|
||||
}
|
||||
return *(*string)(unsafe.Pointer(&bytes))
|
||||
}
|
||||
|
||||
return string(b)
|
||||
// s的字符需要使用多少个比特位数才能表示完
|
||||
// letterIdBits := int(math.Ceil(math.Log2(strLength))),下面比上面的代码快
|
||||
letterIdBits := int(math.Log2(float64(nearestPowerOfTwo(strLength))))
|
||||
// 最大的字母id掩码
|
||||
var letterIdMask int64 = 1<<letterIdBits - 1
|
||||
// 可用次数的最大值
|
||||
letterIdMax := 63 / letterIdBits
|
||||
// 循环生成随机字符串
|
||||
for i, cache, remain := length-1, rn.Int63(), letterIdMax; i >= 0; {
|
||||
// 检查随机数生成器是否用尽所有随机数
|
||||
if remain == 0 {
|
||||
cache, remain = rn.Int63(), letterIdMax
|
||||
}
|
||||
// 从可用字符的字符串中随机选择一个字符
|
||||
if idx := int(cache & letterIdMask); idx < len(s) {
|
||||
bytes[i] = s[idx]
|
||||
i--
|
||||
}
|
||||
// 右移比特位数,为下次选择字符做准备
|
||||
cache >>= letterIdBits
|
||||
remain--
|
||||
}
|
||||
// 仿照strings.Builder用unsafe包返回一个字符串,避免拷贝
|
||||
// 将字节切片转换为字符串并返回
|
||||
return *(*string)(unsafe.Pointer(&bytes))
|
||||
}
|
||||
|
||||
// UUIdV4 generate a random UUID of version 4 according to RFC 4122.
|
||||
|
||||
@@ -498,20 +498,13 @@ func FlatMap[T any, U any](slice []T, iteratee func(index int, item T) []U) []U
|
||||
// Reduce creates an slice of values by running each element of slice thru iteratee function.
|
||||
// Play: https://go.dev/play/p/_RfXJJWIsIm
|
||||
func Reduce[T any](slice []T, iteratee func(index int, item1, item2 T) T, initial T) T {
|
||||
if len(slice) == 0 {
|
||||
return initial
|
||||
accumulator := initial
|
||||
|
||||
for i, v := range slice {
|
||||
accumulator = iteratee(i, v, accumulator)
|
||||
}
|
||||
|
||||
result := iteratee(0, initial, slice[0])
|
||||
|
||||
tmp := make([]T, 2)
|
||||
for i := 1; i < len(slice); i++ {
|
||||
tmp[0] = result
|
||||
tmp[1] = slice[i]
|
||||
result = iteratee(i, tmp[0], tmp[1])
|
||||
}
|
||||
|
||||
return result
|
||||
return accumulator
|
||||
}
|
||||
|
||||
// ReduceBy produces a value from slice by accumulating the result of each element as passed through the reducer function.
|
||||
@@ -625,35 +618,34 @@ func IntSlice(slice any) []int {
|
||||
return result
|
||||
}
|
||||
|
||||
// DeleteAt delete the element of slice from start index to end index - 1.
|
||||
// DeleteAt delete the element of slice at index.
|
||||
// Play: https://go.dev/play/p/pJ-d6MUWcvK
|
||||
func DeleteAt[T any](slice []T, start int, end ...int) []T {
|
||||
size := len(slice)
|
||||
|
||||
if start < 0 || start >= size {
|
||||
return slice
|
||||
func DeleteAt[T any](slice []T, index int) []T {
|
||||
if index >= len(slice) {
|
||||
index = len(slice) - 1
|
||||
}
|
||||
|
||||
if len(end) > 0 {
|
||||
end := end[0]
|
||||
if end <= start {
|
||||
return slice
|
||||
}
|
||||
if end > size {
|
||||
end = size
|
||||
}
|
||||
result := make([]T, len(slice)-1)
|
||||
copy(result, slice[:index])
|
||||
copy(result[index:], slice[index+1:])
|
||||
|
||||
slice = append(slice[:start], slice[end:]...)
|
||||
return slice
|
||||
return result
|
||||
}
|
||||
|
||||
// DeleteRange delete the element of slice from start index to end index(exclude).
|
||||
// Play: todo
|
||||
func DeleteRange[T any](slice []T, start, end int) []T {
|
||||
result := make([]T, 0, len(slice)-(end-start))
|
||||
|
||||
for i := 0; i < start; i++ {
|
||||
result = append(result, slice[i])
|
||||
}
|
||||
|
||||
if start == size-1 {
|
||||
slice = slice[:start]
|
||||
} else {
|
||||
slice = append(slice[:start], slice[start+1:]...)
|
||||
for i := end; i < len(slice); i++ {
|
||||
result = append(result, slice[i])
|
||||
}
|
||||
|
||||
return slice
|
||||
return result
|
||||
}
|
||||
|
||||
// Drop drop n elements from the start of a slice.
|
||||
|
||||
@@ -594,18 +594,46 @@ func ExampleIntSlice() {
|
||||
}
|
||||
|
||||
func ExampleDeleteAt() {
|
||||
result1 := DeleteAt([]string{"a", "b", "c"}, -1)
|
||||
result2 := DeleteAt([]string{"a", "b", "c"}, 0)
|
||||
result3 := DeleteAt([]string{"a", "b", "c"}, 0, 2)
|
||||
chars := []string{"a", "b", "c", "d", "e"}
|
||||
|
||||
result1 := DeleteAt(chars, 0)
|
||||
result2 := DeleteAt(chars, 4)
|
||||
result3 := DeleteAt(chars, 5)
|
||||
result4 := DeleteAt(chars, 6)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
|
||||
// Output:
|
||||
// [a b c]
|
||||
// [b c]
|
||||
// [c]
|
||||
// [b c d e]
|
||||
// [a b c d]
|
||||
// [a b c d]
|
||||
// [a b c d]
|
||||
}
|
||||
|
||||
func ExampleDeleteRange() {
|
||||
chars := []string{"a", "b", "c", "d", "e"}
|
||||
|
||||
result1 := DeleteRange(chars, 0, 0)
|
||||
result2 := DeleteRange(chars, 0, 1)
|
||||
result3 := DeleteRange(chars, 0, 3)
|
||||
result4 := DeleteRange(chars, 0, 4)
|
||||
result5 := DeleteRange(chars, 0, 5)
|
||||
|
||||
fmt.Println(result1)
|
||||
fmt.Println(result2)
|
||||
fmt.Println(result3)
|
||||
fmt.Println(result4)
|
||||
fmt.Println(result5)
|
||||
|
||||
// Output:
|
||||
// [a b c d e]
|
||||
// [b c d e]
|
||||
// [d e]
|
||||
// [e]
|
||||
// []
|
||||
}
|
||||
|
||||
func ExampleDrop() {
|
||||
|
||||
@@ -572,19 +572,27 @@ func TestDeleteAt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDeleteAt")
|
||||
arr := []int{1, 2, 3, 4, 5}
|
||||
|
||||
assert.Equal([]string{"a", "b", "c"}, DeleteAt([]string{"a", "b", "c"}, -1))
|
||||
assert.Equal([]string{"a", "b", "c"}, DeleteAt([]string{"a", "b", "c"}, 3))
|
||||
assert.Equal([]string{"b", "c"}, DeleteAt([]string{"a", "b", "c"}, 0))
|
||||
assert.Equal([]string{"a", "c"}, DeleteAt([]string{"a", "b", "c"}, 1))
|
||||
assert.Equal([]string{"a", "b"}, DeleteAt([]string{"a", "b", "c"}, 2))
|
||||
assert.Equal([]int{2, 3, 4, 5}, DeleteAt(arr, 0))
|
||||
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 4))
|
||||
|
||||
assert.Equal([]string{"b", "c"}, DeleteAt([]string{"a", "b", "c"}, 0, 1))
|
||||
assert.Equal([]string{"c"}, DeleteAt([]string{"a", "b", "c"}, 0, 2))
|
||||
assert.Equal([]string{}, DeleteAt([]string{"a", "b", "c"}, 0, 3))
|
||||
assert.Equal([]string{}, DeleteAt([]string{"a", "b", "c"}, 0, 4))
|
||||
assert.Equal([]string{"a"}, DeleteAt([]string{"a", "b", "c"}, 1, 3))
|
||||
assert.Equal([]string{"a"}, DeleteAt([]string{"a", "b", "c"}, 1, 4))
|
||||
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 5))
|
||||
assert.Equal([]int{1, 2, 3, 4}, DeleteAt(arr, 6))
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4, 5}, arr)
|
||||
}
|
||||
|
||||
func TestDeleteRange(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
assert := internal.NewAssert(t, "TestDeleteRange")
|
||||
|
||||
arr := []int{1, 2, 3, 4, 5}
|
||||
|
||||
assert.Equal([]int{1, 2, 3, 4, 5}, DeleteRange(arr, 0, 0))
|
||||
assert.Equal([]int{2, 3, 4, 5}, DeleteRange(arr, 0, 1))
|
||||
assert.Equal([]int{4, 5}, DeleteRange(arr, 0, 3))
|
||||
}
|
||||
|
||||
func TestDrop(t *testing.T) {
|
||||
|
||||
@@ -52,6 +52,16 @@ func (f *Field) IsZero() bool {
|
||||
return reflect.DeepEqual(z, v)
|
||||
}
|
||||
|
||||
// IsNil returns true if the given field is nil value.
|
||||
func (f *Field) IsNil() bool {
|
||||
v := f.Value()
|
||||
if v == nil || (reflect.ValueOf(v)).Kind() == reflect.Ptr && reflect.ValueOf(v).IsNil() {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Name returns the name of the given field
|
||||
func (f *Field) Name() string {
|
||||
return f.field.Name
|
||||
@@ -111,7 +121,9 @@ func (f *Field) mapValue(value any) any {
|
||||
ret = v.Interface()
|
||||
}
|
||||
default:
|
||||
ret = v.Interface()
|
||||
if v.Kind().String() != "invalid" {
|
||||
ret = v.Interface()
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/duke-git/lancet/v2/pointer"
|
||||
@@ -61,7 +62,7 @@ func New(value any, tagName ...string) *Struct {
|
||||
// ToMap convert the exported fields of a struct to map.
|
||||
func (s *Struct) ToMap() (map[string]any, error) {
|
||||
if !s.IsStruct() {
|
||||
return nil, errInvalidStruct(s)
|
||||
return nil, fmt.Errorf("invalid struct %v", s)
|
||||
}
|
||||
|
||||
result := make(map[string]any)
|
||||
@@ -70,9 +71,15 @@ func (s *Struct) ToMap() (map[string]any, error) {
|
||||
if !f.IsExported() || f.tag.IsEmpty() || f.tag.Name == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
if f.IsZero() && f.tag.HasOption("omitempty") {
|
||||
continue
|
||||
}
|
||||
|
||||
if f.IsNil() {
|
||||
continue
|
||||
}
|
||||
|
||||
result[f.tag.Name] = f.mapValue(f.Value())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package structs
|
||||
|
||||
import "fmt"
|
||||
|
||||
func errInvalidStruct(v any) error {
|
||||
return fmt.Errorf("invalid struct %v", v)
|
||||
}
|
||||
@@ -65,6 +65,45 @@ func TestStruct_ToMap(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ToMap2(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert := internal.NewAssert(t, "TestStruct_ToMap")
|
||||
|
||||
type M struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type S struct {
|
||||
ID int `json:"id"`
|
||||
M *M `json:"m"`
|
||||
}
|
||||
|
||||
s1 := &S{
|
||||
ID: 1,
|
||||
}
|
||||
var expect1 = map[string]any{"id": 1}
|
||||
map1, err := ToMap(s1)
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal(expect1, map1)
|
||||
|
||||
s2 := &S{
|
||||
ID: 1,
|
||||
M: &M{
|
||||
Name: "test",
|
||||
},
|
||||
}
|
||||
var expect2 = map[string]any{
|
||||
"id": 1,
|
||||
"m": map[string]any{
|
||||
"name": "test",
|
||||
}}
|
||||
map2, err := ToMap(s2)
|
||||
|
||||
assert.IsNil(err)
|
||||
assert.Equal(expect2, map2)
|
||||
}
|
||||
|
||||
func TestStruct_Fields(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
package strutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
@@ -380,10 +379,7 @@ func RemoveNonPrintable(str string) string {
|
||||
// StringToBytes converts a string to byte slice without a memory allocation.
|
||||
// Play: https://go.dev/play/p/7OyFBrf9AxA
|
||||
func StringToBytes(str string) (b []byte) {
|
||||
sh := *(*reflect.StringHeader)(unsafe.Pointer(&str))
|
||||
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||
bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
|
||||
return b
|
||||
return *(*[]byte)(unsafe.Pointer(&str))
|
||||
}
|
||||
|
||||
// BytesToString converts a byte slice to string without a memory allocation.
|
||||
|
||||
Reference in New Issue
Block a user