mirror of
https://gitee.com/johng/gf
synced 2026-06-09 11:03:59 +08:00
Compare commits
385 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e95058cb5 | |||
| f33753e0cd | |||
| ac71e1d753 | |||
| 8151b6efd6 | |||
| 94de306c93 | |||
| 51f8ea26de | |||
| 29d9bb17cd | |||
| 30501a882d | |||
| f2f98e1d16 | |||
| cbf465eeea | |||
| 79c400e912 | |||
| cc4c49b5b0 | |||
| 96ea2c911d | |||
| 131b11680a | |||
| 3c6ab96283 | |||
| d2c4fa921a | |||
| dbb51a9328 | |||
| 55bfc3fa73 | |||
| d9c5182d1a | |||
| 899fcbf2da | |||
| 6e2c0d8706 | |||
| c4e599ce63 | |||
| 9cad9e4467 | |||
| 48019444ea | |||
| 76d31a7fbb | |||
| facb9d93c0 | |||
| 1d6bd46c5e | |||
| 8646dc1825 | |||
| 2992bdeed7 | |||
| c5601e6c88 | |||
| ef1d9a561c | |||
| 2d3b32c94a | |||
| 269378aa0d | |||
| 6889542801 | |||
| 8e6f1f1740 | |||
| b6ab1a992c | |||
| a5a267567c | |||
| f05f855c07 | |||
| bc5f773ba6 | |||
| 55308cc37c | |||
| 4d9db6edf0 | |||
| 38111a64e3 | |||
| bd8d3fca08 | |||
| 89ccaa3915 | |||
| 7c2cff7d99 | |||
| 788e15dbb6 | |||
| a0172d9d7e | |||
| bc1a7a1644 | |||
| c98234d3e6 | |||
| 45a94d23d5 | |||
| 351de5ee6a | |||
| 3559436d1e | |||
| 189f4c1637 | |||
| caead810e2 | |||
| ebdc5d9c9d | |||
| 4164059211 | |||
| bd27258c46 | |||
| ddf0605bc4 | |||
| 991dbe50e0 | |||
| 8050efb835 | |||
| 9355bc73a2 | |||
| acc2a6a353 | |||
| 09e83e7b8d | |||
| c0df8a3d80 | |||
| 33e890d225 | |||
| 750e5df962 | |||
| 74c65439fc | |||
| f290bd7170 | |||
| d398b749d4 | |||
| 2fd5a1574a | |||
| a7504f0629 | |||
| 3c35bb85ee | |||
| 6621edb04c | |||
| b0c722e297 | |||
| 47a6284274 | |||
| 150b8edb70 | |||
| aa5d3285eb | |||
| 993d6e3076 | |||
| 78ad9d8c2d | |||
| feff3ddce3 | |||
| 80fddad64d | |||
| 1e6dd0be02 | |||
| 22ecf4f1b6 | |||
| 5d21148657 | |||
| edb745ed92 | |||
| 61f4e0da6f | |||
| 767afa3106 | |||
| 16779359ee | |||
| 770653fafa | |||
| 0c021b6da7 | |||
| ec92b08f25 | |||
| 13e2353729 | |||
| 90d19a84ce | |||
| b4c0b95d8a | |||
| b7e8255066 | |||
| 5f8e6ad9ed | |||
| c8d253eb56 | |||
| 4814624cff | |||
| cc67f3d388 | |||
| f7c2a51c9f | |||
| 3db83e1159 | |||
| 3bb002796c | |||
| 45170bc53e | |||
| b79ff84c6f | |||
| 938c46fec9 | |||
| 8a13d94526 | |||
| 1e844d505a | |||
| a123a2c086 | |||
| 1eeeeb853e | |||
| 6e7224e306 | |||
| 9e064e2651 | |||
| 8d9dd17eac | |||
| cf1d3d3d2b | |||
| 9480ffcdc0 | |||
| 5db10add4a | |||
| fa66bf5d9d | |||
| f69da3ace1 | |||
| e01bfa05c3 | |||
| 231238c157 | |||
| 7edec099ab | |||
| 83eb8be064 | |||
| 7af30df494 | |||
| f026686fda | |||
| 35a50b9c6c | |||
| 010e2f951a | |||
| f7f86ad65a | |||
| 1e19f447d1 | |||
| 8cc378331d | |||
| 4721f68fd8 | |||
| 0d11c0a1f8 | |||
| 5076613a8f | |||
| c5a44daa65 | |||
| 520970b71f | |||
| ebfb08ee3f | |||
| 9e38b2cb90 | |||
| 71b1f00dc5 | |||
| 59d6830ab1 | |||
| 8779a3f211 | |||
| c10149baa0 | |||
| 4f87668780 | |||
| 63f33d1d8c | |||
| 734aa5a6fe | |||
| 371aef224d | |||
| 362e380ada | |||
| e995bd8c9a | |||
| 81b211dd1a | |||
| 0515fc94cb | |||
| 46ee070f0a | |||
| b17e3a6804 | |||
| 9160bee1af | |||
| 8e6018cfff | |||
| c08739b5a3 | |||
| 385a503d31 | |||
| ef286b0c15 | |||
| 53aba2d4b8 | |||
| f22da4ba3a | |||
| 23c2f12672 | |||
| 7fd53673ce | |||
| e64fd088b9 | |||
| 15672e7a09 | |||
| 2ea1d2c7b2 | |||
| 90d751f98d | |||
| 25a91c732c | |||
| 01995f5501 | |||
| 68abb3cf3d | |||
| 77cc323d0e | |||
| c7a9c03495 | |||
| f7850e3ed3 | |||
| 82125416a2 | |||
| 2c1e2155e3 | |||
| 2d30a53c3a | |||
| ccceeae29c | |||
| f22b98456f | |||
| e7f1bd692b | |||
| f82f7ab199 | |||
| 4d33b527b6 | |||
| 7bcc596308 | |||
| be2d4b080e | |||
| c96e5f5a9e | |||
| 3c23766674 | |||
| 718089fc11 | |||
| 8ab44dcb44 | |||
| 992522342c | |||
| 040898cdc3 | |||
| 343126ef22 | |||
| 05760d1eac | |||
| c10f73f1f7 | |||
| 7e0fa8e0cd | |||
| 6fe6218505 | |||
| 6059782de8 | |||
| 4844eea0ab | |||
| 8ecd62d3de | |||
| 4c610b4f58 | |||
| 1932c4ec44 | |||
| bd2e51ddca | |||
| ddcb7121c1 | |||
| f1f575fd5c | |||
| 99adb7cdc4 | |||
| 6b7ea97777 | |||
| 495b5758ec | |||
| ba56eb87b1 | |||
| 4258a3bbc9 | |||
| 4912331ddc | |||
| e87b7ecf5d | |||
| 23bce7bde6 | |||
| 926b664615 | |||
| fa8257c85b | |||
| abb9c88c23 | |||
| f89976cad5 | |||
| 0389778725 | |||
| 3c36285126 | |||
| 75054ee109 | |||
| 8447b1a42b | |||
| e5265a1c46 | |||
| 6e8ef8d0b0 | |||
| 75c081afc9 | |||
| 060fd9eaba | |||
| 63e5a60344 | |||
| 75dc1d82c1 | |||
| a5e048eb5f | |||
| 65bc1d5eb8 | |||
| bfa64705b5 | |||
| 17aea8d7d4 | |||
| a6a01fd7f2 | |||
| 41a9e91b4c | |||
| e2c1e11f95 | |||
| c2966817ce | |||
| 16958413bb | |||
| c0a0913d4b | |||
| 6d47810782 | |||
| 7a9ea2e546 | |||
| 7881b2dee4 | |||
| 5f223ef049 | |||
| e57942b374 | |||
| f18e6f078c | |||
| f667cbc2a2 | |||
| 07e65c14a9 | |||
| 0b6d04485e | |||
| 36401a063d | |||
| 849e7370d1 | |||
| f01dca0895 | |||
| 55137b2aa3 | |||
| 22540921b3 | |||
| 6174097a07 | |||
| 52de11b1fe | |||
| 5a646179ad | |||
| 33ae93e050 | |||
| f3d859159d | |||
| 4fb01e68f7 | |||
| 2812a247aa | |||
| 2b46e765c4 | |||
| c7f911cae2 | |||
| 9f0548c03d | |||
| c578df06a3 | |||
| 8ed3cf9c97 | |||
| 2438f565e9 | |||
| 8230c72ec6 | |||
| d716037caa | |||
| 855a4ddb2c | |||
| 74be9fac18 | |||
| e9fba5a166 | |||
| 0a92df691b | |||
| e513cd10ed | |||
| b702d98700 | |||
| 99f1d9d0ed | |||
| 707b08c585 | |||
| f44c19868c | |||
| 13ab139afc | |||
| cacb2f142b | |||
| 11f0317e92 | |||
| 9a667c8803 | |||
| c1cce17934 | |||
| 5a92d7de0d | |||
| 53bf378868 | |||
| cd7c45c00c | |||
| f13a5ad82e | |||
| 2e9be609c8 | |||
| 2f2f6e1ffe | |||
| f3bd2b67f7 | |||
| 645cecdffb | |||
| 22e9965629 | |||
| 24ea9f9245 | |||
| 9dbde6e8f1 | |||
| fe0b34544d | |||
| 5e489d59b4 | |||
| 61f49574a9 | |||
| 2fabcb62a8 | |||
| 3bc3b652c1 | |||
| 042a6f12f5 | |||
| 5acce82e63 | |||
| 4732bf46ad | |||
| 5bed5a1532 | |||
| 5b7576430f | |||
| 356f4cd701 | |||
| c444630d1e | |||
| 8e40cded42 | |||
| 0e52d467d3 | |||
| 6665d62e7e | |||
| 5bdf1a71b8 | |||
| a34ca0ff4b | |||
| 7f0163d958 | |||
| 31f19b0eee | |||
| 93d0760898 | |||
| 4863e7a6ae | |||
| a161b44cc7 | |||
| 7072244420 | |||
| f68b66e606 | |||
| 4e7c6c1fb4 | |||
| d8a7e36478 | |||
| 8971ad8445 | |||
| 270af8accb | |||
| b0ef63fc9d | |||
| 6e1f8c3cfc | |||
| e58d7e8dda | |||
| 3a3384cf06 | |||
| ef2a9f6fd1 | |||
| 63f756f731 | |||
| bb1c27c36a | |||
| 87cd0703c0 | |||
| 6317d9de53 | |||
| 7acf16fdba | |||
| a52b454d3e | |||
| 816e075c52 | |||
| 9882b361a8 | |||
| 4415dcf1c1 | |||
| 42fd583bfd | |||
| c70bc7c96a | |||
| 02bd780a33 | |||
| 24a2192ce2 | |||
| d8ef8a1f5d | |||
| 745a913cfb | |||
| 13dba407a2 | |||
| 34e7c5f809 | |||
| d570624caa | |||
| f18312419b | |||
| 89f869dd44 | |||
| 20b64507b1 | |||
| 7443246e05 | |||
| f9e7823c14 | |||
| 52943b283c | |||
| 36403fdc08 | |||
| 0317f6812e | |||
| 5169137069 | |||
| 7d9bccf912 | |||
| 14f56ea18f | |||
| 1736d271d2 | |||
| 19755ad233 | |||
| cfdd043e4e | |||
| 78917ed5cb | |||
| 88684ca00a | |||
| 784983806a | |||
| cdb3b94e22 | |||
| 83dcc4a5e0 | |||
| a6c0b281a3 | |||
| 3120f24553 | |||
| ac9be6134b | |||
| 4c1b4f7858 | |||
| 1e45bf93d8 | |||
| e8dd3979b6 | |||
| 374ee4c0ea | |||
| 95411aff77 | |||
| 1999ef95c1 | |||
| b15075fdfe | |||
| 4d2b244319 | |||
| 4c3af63076 | |||
| 91bbff6ced | |||
| 26aab44ec8 | |||
| 2e10ce421b | |||
| 8eda69b11e | |||
| 7d7b242968 | |||
| 202419202f | |||
| 55078beed1 | |||
| d9f4e6eaa6 | |||
| 2ba0913bea | |||
| 8f2dcf21ff | |||
| 01b06e0745 | |||
| 665b5960c8 | |||
| eb6a7a4728 | |||
| 7df53ff55e | |||
| 8021f39710 | |||
| f2af08270b | |||
| c2f028848c | |||
| d9c7224861 | |||
| f59a1ada88 | |||
| 705ab1d33f |
@ -9,7 +9,7 @@ import (
|
||||
|
||||
func main() {
|
||||
// 创建一个对象池,过期时间为1000毫秒
|
||||
p := gpool.New(1000, nil)
|
||||
p := gpool.New(1000*time.Millisecond, nil)
|
||||
|
||||
// 从池中取一个对象,返回nil及错误信息
|
||||
fmt.Println(p.Get())
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
|
||||
func main() {
|
||||
// 创建对象复用池,对象过期时间为3000毫秒,并给定创建及销毁方法
|
||||
p := gpool.New(3000, func() (interface{}, error) {
|
||||
p := gpool.New(3000*time.Millisecond, func() (interface{}, error) {
|
||||
return gtcp.NewConn("www.baidu.com:80")
|
||||
}, func(i interface{}) {
|
||||
glog.Println("expired")
|
||||
|
||||
79
.example/database/gdb/driver/driver/driver.go
Normal file
79
.example/database/gdb/driver/driver/driver.go
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package driver
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
|
||||
// MyDriver is a custom database driver, which is used for testing only.
|
||||
// For simplifying the unit testing case purpose, MyDriver struct inherits the mysql driver
|
||||
// gdb.DriverMysql and overwrites its functions DoQuery and DoExec.
|
||||
// So if there's any sql execution, it goes through MyDriver.DoQuery/MyDriver.DoExec firstly
|
||||
// and then gdb.DriverMysql.DoQuery/gdb.DriverMysql.DoExec.
|
||||
// You can call it sql "HOOK" or "HiJack" as your will.
|
||||
type MyDriver struct {
|
||||
*gdb.DriverMysql
|
||||
}
|
||||
|
||||
var (
|
||||
// customDriverName is my driver name, which is used for registering.
|
||||
customDriverName = "MyDriver"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// It here registers my custom driver in package initialization function "init".
|
||||
// You can later use this type in the database configuration.
|
||||
if err := gdb.Register(customDriverName, &MyDriver{}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// New creates and returns a database object for mysql.
|
||||
// It implements the interface of gdb.Driver for extra database driver installation.
|
||||
func (d *MyDriver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) {
|
||||
return &MyDriver{
|
||||
&gdb.DriverMysql{
|
||||
Core: core,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DoQuery commits the sql string and its arguments to underlying driver
|
||||
// through given link object and returns the execution result.
|
||||
func (d *MyDriver) DoQuery(link gdb.Link, sql string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
tsMilli := gtime.TimestampMilli()
|
||||
rows, err = d.DriverMysql.DoQuery(link, sql, args...)
|
||||
if _, err := d.DriverMysql.InsertIgnore("monitor", g.Map{
|
||||
"sql": gdb.FormatSqlWithArgs(sql, args),
|
||||
"cost": gtime.TimestampMilli() - tsMilli,
|
||||
"time": gtime.Now(),
|
||||
"error": err.Error(),
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DoExec commits the query string and its arguments to underlying driver
|
||||
// through given link object and returns the execution result.
|
||||
func (d *MyDriver) DoExec(link gdb.Link, sql string, args ...interface{}) (result sql.Result, err error) {
|
||||
tsMilli := gtime.TimestampMilli()
|
||||
result, err = d.DriverMysql.DoExec(link, sql, args...)
|
||||
if _, err := d.DriverMysql.InsertIgnore("monitor", g.Map{
|
||||
"sql": gdb.FormatSqlWithArgs(sql, args),
|
||||
"cost": gtime.TimestampMilli() - tsMilli,
|
||||
"time": gtime.Now(),
|
||||
"error": err.Error(),
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
1
.example/database/gdb/driver/main.go
Normal file
1
.example/database/gdb/driver/main.go
Normal file
@ -0,0 +1 @@
|
||||
package main
|
||||
@ -1,8 +1,3 @@
|
||||
|
||||
[database]
|
||||
type = "mssql"
|
||||
host = "127.0.0.1"
|
||||
port = "1451"
|
||||
user = "sa"
|
||||
pass = "eno@123"
|
||||
name = "frpc"
|
||||
linkinfo = "mssql:user id=test;password=test1;server=122.152.202.91;port=1433;database=test;encrypt=disable"
|
||||
@ -2,13 +2,22 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
|
||||
_ "github.com/denisenkom/go-mssqldb"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r, err := g.DB().GetAll(`SELECT TOP 10 * FROM KF_PatInfo_Emergency`)
|
||||
fmt.Println(err)
|
||||
g.Dump(r.ToList())
|
||||
type Table2 struct {
|
||||
Id string `orm:"id;pr" json:"id"` //ID
|
||||
Createtime gtime.Time `orm:"createtime" json:"createtime"` //创建时间
|
||||
Updatetime gtime.Time `orm:"updatetime" json:"updatetime"` //更新时间
|
||||
}
|
||||
var table2 Table2
|
||||
err := g.DB().Table("table2").Where("id=?", 1).Struct(&table2)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(table2.Createtime)
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
|
||||
# MySQL数据库配置
|
||||
[database]
|
||||
# debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
|
||||
debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
|
||||
MaxOpen = 100
|
||||
|
||||
#[database]
|
||||
# [[database.default]]
|
||||
|
||||
@ -11,11 +11,11 @@ func main() {
|
||||
// 开启调试模式,以便于记录所有执行的SQL
|
||||
db.SetDebug(true)
|
||||
|
||||
r, e := db.Table("test").OrderBy("id asc").All()
|
||||
r, e := db.Table("test").Order("id asc").All()
|
||||
if e != nil {
|
||||
panic(e)
|
||||
fmt.Println(e)
|
||||
}
|
||||
if r != nil {
|
||||
fmt.Println(r.ToList())
|
||||
fmt.Println(r.List())
|
||||
}
|
||||
}
|
||||
|
||||
18
.example/database/gdb/mysql/gdb_reconnect.go
Normal file
18
.example/database/gdb/mysql/gdb_reconnect.go
Normal file
@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db := g.DB()
|
||||
db.SetDebug(true)
|
||||
for {
|
||||
r, err := db.Table("user").All()
|
||||
fmt.Println(err)
|
||||
fmt.Println(r)
|
||||
time.Sleep(time.Second * 10)
|
||||
}
|
||||
}
|
||||
@ -8,10 +8,5 @@ func main() {
|
||||
db := g.DB()
|
||||
db.SetDebug(true)
|
||||
|
||||
one, e := db.Table("order.order o").LeftJoin("user.user u", "o.uid=u.id").Where("u.id", 1).One()
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
g.Dump(one)
|
||||
|
||||
db.Table("user").Fields("DISTINCT id,nickname").Filter().All()
|
||||
}
|
||||
|
||||
@ -1,16 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/encoding/gcompress"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := gcompress.ZipPath(
|
||||
`D:\Workspace\Go\GOPATH\src\github.com\gogf\gf\geg`,
|
||||
`D:\Workspace\Go\GOPATH\src\github.com\gogf\gf\geg\encoding\gcompress\data.zip`,
|
||||
"my-dir",
|
||||
)
|
||||
fmt.Println(err)
|
||||
// srcFile could be a single file or a directory
|
||||
func Zip(srcFile string, destZip string) error {
|
||||
zipfile, err := os.Create(destZip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer zipfile.Close()
|
||||
|
||||
archive := zip.NewWriter(zipfile)
|
||||
defer archive.Close()
|
||||
|
||||
filepath.Walk(srcFile, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header, err := zip.FileInfoHeader(info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header.Name = strings.TrimPrefix(path, filepath.Dir(srcFile)+"/")
|
||||
// header.Name = path
|
||||
if info.IsDir() {
|
||||
header.Name += "/"
|
||||
} else {
|
||||
header.Method = zip.Deflate
|
||||
}
|
||||
|
||||
writer, err := archive.CreateHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = io.Copy(writer, file)
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func main() {
|
||||
src := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/test`
|
||||
dst := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/test.zip`
|
||||
//src := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/README.MD`
|
||||
//dst := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/README.MD.zip`
|
||||
fmt.Println(gcompress.ZipPath(src, dst))
|
||||
//fmt.Println(Zip(src, dst))
|
||||
}
|
||||
|
||||
@ -100,53 +100,53 @@ func testConvert() {
|
||||
func testSplitChar() {
|
||||
var v interface{}
|
||||
j := gjson.New(nil)
|
||||
t1 := gtime.Nanosecond()
|
||||
t1 := gtime.TimestampNano()
|
||||
j.Set("a.b.c.d.e.f.g.h.i.j.k", 1)
|
||||
t2 := gtime.Nanosecond()
|
||||
t2 := gtime.TimestampNano()
|
||||
fmt.Println(t2 - t1)
|
||||
|
||||
t5 := gtime.Nanosecond()
|
||||
t5 := gtime.TimestampNano()
|
||||
v = j.Get("a.b.c.d.e.f.g.h.i.j.k")
|
||||
t6 := gtime.Nanosecond()
|
||||
t6 := gtime.TimestampNano()
|
||||
fmt.Println(v)
|
||||
fmt.Println(t6 - t5)
|
||||
|
||||
j.SetSplitChar('#')
|
||||
|
||||
t7 := gtime.Nanosecond()
|
||||
t7 := gtime.TimestampNano()
|
||||
v = j.Get("a#b#c#d#e#f#g#h#i#j#k")
|
||||
t8 := gtime.Nanosecond()
|
||||
t8 := gtime.TimestampNano()
|
||||
fmt.Println(v)
|
||||
fmt.Println(t8 - t7)
|
||||
}
|
||||
|
||||
func testViolenceCheck() {
|
||||
j := gjson.New(nil)
|
||||
t1 := gtime.Nanosecond()
|
||||
t1 := gtime.TimestampNano()
|
||||
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
|
||||
t2 := gtime.Nanosecond()
|
||||
t2 := gtime.TimestampNano()
|
||||
fmt.Println(t2 - t1)
|
||||
|
||||
t3 := gtime.Nanosecond()
|
||||
t3 := gtime.TimestampNano()
|
||||
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
|
||||
t4 := gtime.Nanosecond()
|
||||
t4 := gtime.TimestampNano()
|
||||
fmt.Println(t4 - t3)
|
||||
|
||||
t5 := gtime.Nanosecond()
|
||||
t5 := gtime.TimestampNano()
|
||||
j.Get("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a")
|
||||
t6 := gtime.Nanosecond()
|
||||
t6 := gtime.TimestampNano()
|
||||
fmt.Println(t6 - t5)
|
||||
|
||||
j.SetViolenceCheck(false)
|
||||
|
||||
t7 := gtime.Nanosecond()
|
||||
t7 := gtime.TimestampNano()
|
||||
j.Set("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", 1)
|
||||
t8 := gtime.Nanosecond()
|
||||
t8 := gtime.TimestampNano()
|
||||
fmt.Println(t8 - t7)
|
||||
|
||||
t9 := gtime.Nanosecond()
|
||||
t9 := gtime.TimestampNano()
|
||||
j.Get("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a")
|
||||
t10 := gtime.Nanosecond()
|
||||
t10 := gtime.TimestampNano()
|
||||
fmt.Println(t10 - t9)
|
||||
}
|
||||
|
||||
|
||||
@ -3,34 +3,16 @@ package main
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Upload uploads files to /tmp .
|
||||
func Upload(r *ghttp.Request) {
|
||||
saveDir := "/tmp/"
|
||||
for _, item := range r.GetMultipartFiles("upload-file") {
|
||||
file, err := item.Open()
|
||||
if err != nil {
|
||||
r.Response.Write(err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
f, err := gfile.Create(saveDir + gfile.Basename(item.Filename))
|
||||
if err != nil {
|
||||
r.Response.Write(err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := io.Copy(f, file); err != nil {
|
||||
r.Response.Write(err)
|
||||
return
|
||||
}
|
||||
saveDirPath := "/tmp/"
|
||||
files := r.GetUploadFiles("upload-file")
|
||||
if _, err := files.Save(saveDirPath); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.Write("upload successfully")
|
||||
r.Response.WriteExit("upload successfully")
|
||||
}
|
||||
|
||||
// UploadShow shows uploading simgle file page.
|
||||
@ -71,7 +53,7 @@ func UploadShowBatch(r *ghttp.Request) {
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/upload", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/", Upload)
|
||||
group.POST("/", Upload)
|
||||
group.ALL("/show", UploadShow)
|
||||
group.ALL("/batch", UploadShowBatch)
|
||||
})
|
||||
|
||||
@ -2,12 +2,34 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
func SendXmlFile(gameId int, areaName string, filePath string) error {
|
||||
path := filepath.FromSlash(filePath)
|
||||
fmt.Println(path)
|
||||
data := g.Map{
|
||||
"gameName": gameId,
|
||||
"area": areaName,
|
||||
"file": "@file:" + path,
|
||||
"contentType": "json",
|
||||
}
|
||||
if r, err := ghttp.Post("http://127.0.0.1:8199/upload", data); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
defer r.Close()
|
||||
fmt.Println("ok")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
SendXmlFile(1, "xxx", "/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/net/ghttp/server/session.go")
|
||||
return
|
||||
path := "/home/john/Workspace/Go/github.com/gogf/gf/version.go"
|
||||
r, e := ghttp.Post("http://127.0.0.1:8199/upload", "upload-file=@file:"+path)
|
||||
if e != nil {
|
||||
|
||||
@ -3,34 +3,16 @@ package main
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Upload uploads files to /tmp .
|
||||
func Upload(r *ghttp.Request) {
|
||||
saveDir := "/tmp/"
|
||||
for _, item := range r.GetMultipartFiles("upload-file") {
|
||||
file, err := item.Open()
|
||||
if err != nil {
|
||||
r.Response.Write(err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
f, err := gfile.Create(saveDir + gfile.Basename(item.Filename))
|
||||
if err != nil {
|
||||
r.Response.Write(err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := io.Copy(f, file); err != nil {
|
||||
r.Response.Write(err)
|
||||
return
|
||||
}
|
||||
saveDirPath := "/tmp/"
|
||||
files := r.GetUploadFiles("file")
|
||||
if _, err := files.Save(saveDirPath); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.Write("upload successfully")
|
||||
r.Response.WriteExit("upload successfully")
|
||||
}
|
||||
|
||||
// UploadShow shows uploading simgle file page.
|
||||
@ -71,7 +53,7 @@ func UploadShowBatch(r *ghttp.Request) {
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/upload", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/", Upload)
|
||||
group.POST("/", Upload)
|
||||
group.ALL("/show", UploadShow)
|
||||
group.ALL("/batch", UploadShowBatch)
|
||||
})
|
||||
|
||||
22
.example/net/ghttp/server/body.go
Normal file
22
.example/net/ghttp/server/body.go
Normal file
@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetIndexFolder(true)
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
body1 := r.GetBody()
|
||||
body2, _ := ioutil.ReadAll(r.Body)
|
||||
fmt.Println(body1)
|
||||
fmt.Println(body2)
|
||||
r.Response.Write("hello world")
|
||||
})
|
||||
s.SetPort(8999)
|
||||
s.Run()
|
||||
}
|
||||
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
func MiddlewareCORS(r *ghttp.Request) {
|
||||
@ -11,6 +12,7 @@ func MiddlewareCORS(r *ghttp.Request) {
|
||||
}
|
||||
|
||||
func Order(r *ghttp.Request) {
|
||||
glog.Println("order")
|
||||
r.Response.Write("GET")
|
||||
}
|
||||
|
||||
@ -18,7 +20,7 @@ func main() {
|
||||
s := g.Server()
|
||||
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(MiddlewareCORS)
|
||||
g.GET("/order", Order)
|
||||
group.GET("/order", Order)
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
|
||||
@ -20,7 +20,7 @@ func main() {
|
||||
s := g.Server()
|
||||
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(MiddlewareCORS)
|
||||
g.GET("/order", Order)
|
||||
group.GET("/order", Order)
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
|
||||
@ -26,7 +26,7 @@ func main() {
|
||||
s := g.Server()
|
||||
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(MiddlewareCORS)
|
||||
g.GET("/order", Order)
|
||||
group.GET("/order", Order)
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
|
||||
@ -19,7 +19,7 @@ func MiddlewareAuth(r *ghttp.Request) {
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/admin", func(group *ghttp.RouterGroup) {
|
||||
g.MiddlewarePattern("/*action", func(r *ghttp.Request) {
|
||||
group.Middleware(func(r *ghttp.Request) {
|
||||
if action := r.GetRouterString("action"); action != "" {
|
||||
switch action {
|
||||
case "login":
|
||||
|
||||
@ -25,14 +25,12 @@ func MiddlewareCORS(r *ghttp.Request) {
|
||||
|
||||
func MiddlewareLog(r *ghttp.Request) {
|
||||
r.Middleware.Next()
|
||||
glog.Println(r.Response.Status, r.URL.Path)
|
||||
g.Log().Println(r.Response.Status, r.URL.Path)
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(MiddlewareLog)
|
||||
})
|
||||
s.Use(MiddlewareLog)
|
||||
s.Group("/api.v2", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(MiddlewareAuth, MiddlewareCORS)
|
||||
group.ALL("/user/list", func(r *ghttp.Request) {
|
||||
|
||||
@ -13,20 +13,20 @@ func main() {
|
||||
r.Middleware.Next()
|
||||
r.Response.Write("end")
|
||||
})
|
||||
g.Group("/order", func(group *ghttp.RouterGroup) {
|
||||
g.GET("/list", func(r *ghttp.Request) {
|
||||
group.Group("/order", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/list", func(r *ghttp.Request) {
|
||||
r.Response.Write("list")
|
||||
})
|
||||
})
|
||||
g.Group("/user", func(group *ghttp.RouterGroup) {
|
||||
g.GET("/info", func(r *ghttp.Request) {
|
||||
group.Group("/user", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/info", func(r *ghttp.Request) {
|
||||
r.Response.Write("info")
|
||||
})
|
||||
g.POST("/edit", func(r *ghttp.Request) {
|
||||
group.POST("/edit", func(r *ghttp.Request) {
|
||||
r.Response.Write("edit")
|
||||
})
|
||||
})
|
||||
g.Group("/hook", func(group *ghttp.RouterGroup) {
|
||||
group.Group("/hook", func(group *ghttp.RouterGroup) {
|
||||
group.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
r.Response.Write("hook any")
|
||||
})
|
||||
|
||||
@ -5,21 +5,18 @@ import (
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
}
|
||||
type User struct{}
|
||||
|
||||
func (c *User) Index(r *ghttp.Request) {
|
||||
r.Response.Write("Index")
|
||||
}
|
||||
|
||||
// 不符合规范,不会被注册
|
||||
func (c *User) Test(r *ghttp.Request, value interface{}) {
|
||||
func (c *User) Test(r *ghttp.Request) {
|
||||
r.Response.Write("Test")
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindObject("/user", new(User))
|
||||
u := new(User)
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/db-{table}/{id}", u, "Test")
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
@ -9,14 +9,14 @@ import (
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
g.GET("/set", func(r *ghttp.Request) {
|
||||
r.Session.Set("time", gtime.Second())
|
||||
group.GET("/set", func(r *ghttp.Request) {
|
||||
r.Session.Set("time", gtime.Timestamp())
|
||||
r.Response.Write("ok")
|
||||
})
|
||||
g.GET("/get", func(r *ghttp.Request) {
|
||||
group.GET("/get", func(r *ghttp.Request) {
|
||||
r.Response.WriteJson(r.Session.Map())
|
||||
})
|
||||
g.GET("/clear", func(r *ghttp.Request) {
|
||||
group.GET("/clear", func(r *ghttp.Request) {
|
||||
r.Session.Clear()
|
||||
})
|
||||
})
|
||||
29
.example/net/ghttp/server/session/redis/redis.go
Normal file
29
.example/net/ghttp/server/session/redis/redis.go
Normal file
@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gsession"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetSessionMaxAge(2 * time.Minute)
|
||||
s.SetSessionStorage(gsession.NewStorageRedis(g.Redis()))
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/set", func(r *ghttp.Request) {
|
||||
r.Session.Set("time", gtime.Timestamp())
|
||||
r.Response.Write("ok")
|
||||
})
|
||||
group.GET("/get", func(r *ghttp.Request) {
|
||||
r.Response.WriteJson(r.Session.Map())
|
||||
})
|
||||
group.GET("/clear", func(r *ghttp.Request) {
|
||||
r.Session.Clear()
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
17
.example/net/ghttp/server/template/config/config.go
Normal file
17
.example/net/ghttp/server/template/config/config.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.WriteTplContent(`${.name}`, g.Map{
|
||||
"name": "john",
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
2
.example/net/ghttp/server/template/config/config.toml
Normal file
2
.example/net/ghttp/server/template/config/config.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[viewer]
|
||||
delimiters = ["${", "}"]
|
||||
@ -8,7 +8,6 @@ import (
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write("Hello World")
|
||||
r.Response.WriteTpl("index.tpl", g.Map{
|
||||
"title": "Test",
|
||||
"name": "John",
|
||||
|
||||
@ -27,7 +27,7 @@ func main() {
|
||||
},
|
||||
MemUsed: 15560320,
|
||||
MemTotal: 16333788,
|
||||
Time: int(gtime.Second()),
|
||||
Time: int(gtime.Timestamp()),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
func main() {
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
if f, err := gfpool.Open("/home/john/temp/log.log", os.O_RDONLY, 0666, 60000000*1000); err == nil {
|
||||
if f, err := gfpool.Open("/home/john/temp/log.log", os.O_RDONLY, 0666, time.Hour); err == nil {
|
||||
fmt.Println(f.Name())
|
||||
f.Close()
|
||||
} else {
|
||||
|
||||
14
.example/os/glog/glog_ctx.go
Normal file
14
.example/os/glog/glog_ctx.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
glog.SetCtxKeys("Trace-Id", "Span-Id", "Test")
|
||||
ctx := context.WithValue(context.Background(), "Trace-Id", "1234567890")
|
||||
ctx = context.WithValue(ctx, "Span-Id", "abcdefg")
|
||||
|
||||
glog.Ctx(ctx).Print(1, 2, 3)
|
||||
}
|
||||
11
.example/os/glog/glog_level_prefix.go
Normal file
11
.example/os/glog/glog_level_prefix.go
Normal file
@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
l := glog.New()
|
||||
l.SetLevelPrefix(glog.LEVEL_DEBU, "debug")
|
||||
l.Debug("test")
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/os/gres"
|
||||
_ "github.com/gogf/gf/os/gres/testdata"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gres.Dump()
|
||||
//file := gres.Get("www")
|
||||
//fmt.Println(file.Open())
|
||||
//g.Dump(gres.ScanDir("/root/image", "*"))
|
||||
//g.Dump(gres.Scan("/root/image/", "*", true))
|
||||
//g.Dump(gres.Scan("/template", "*"))
|
||||
//g.Dump(gres.Scan("/template/layout2", "*.html", true))
|
||||
}
|
||||
20
.example/os/gres/gres_example.go
Normal file
20
.example/os/gres/gres_example.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/gogf/gf/os/gres/testdata/example/boot"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/template", func(r *ghttp.Request) {
|
||||
r.Response.WriteTplDefault(g.Map{
|
||||
"name": "GoFrame",
|
||||
})
|
||||
})
|
||||
})
|
||||
s.Run()
|
||||
}
|
||||
@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := gtime.Millisecond()
|
||||
start := gtime.TimestampMilli()
|
||||
wg := sync.WaitGroup{}
|
||||
for i := 0; i < 100000; i++ {
|
||||
wg.Add(1)
|
||||
@ -19,5 +19,5 @@ func main() {
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
fmt.Println("time spent:", gtime.Millisecond()-start)
|
||||
fmt.Println("time spent:", gtime.TimestampMilli()-start)
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := gtime.Millisecond()
|
||||
start := gtime.TimestampMilli()
|
||||
wg := sync.WaitGroup{}
|
||||
for i := 0; i < 100000; i++ {
|
||||
wg.Add(1)
|
||||
@ -21,5 +21,5 @@ func main() {
|
||||
}
|
||||
wg.Wait()
|
||||
fmt.Println(grpool.Size())
|
||||
fmt.Println("time spent:", gtime.Millisecond()-start)
|
||||
fmt.Println("time spent:", gtime.TimestampMilli()-start)
|
||||
}
|
||||
|
||||
@ -9,8 +9,8 @@ import (
|
||||
func main() {
|
||||
fmt.Println("Date :", gtime.Date())
|
||||
fmt.Println("Datetime :", gtime.Datetime())
|
||||
fmt.Println("Second :", gtime.Second())
|
||||
fmt.Println("Millisecond:", gtime.Millisecond())
|
||||
fmt.Println("Microsecond:", gtime.Microsecond())
|
||||
fmt.Println("Nanosecond :", gtime.Nanosecond())
|
||||
fmt.Println("Second :", gtime.Timestamp())
|
||||
fmt.Println("Millisecond:", gtime.TimestampMilli())
|
||||
fmt.Println("Microsecond:", gtime.TimestampMicro())
|
||||
fmt.Println("Nanosecond :", gtime.TimestampNano())
|
||||
}
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
|
||||
[database]
|
||||
debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
|
||||
[redis]
|
||||
default = "127.0.0.1:6379,0"
|
||||
cache = "127.0.0.1:6379,1"
|
||||
|
||||
[viewer]
|
||||
delimiters = ["${", "}"]
|
||||
autoencode = true
|
||||
@ -1,11 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(g.Cfg().FilePath())
|
||||
g.Cfg().Dump()
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := ghttp.PostContent("http://127.0.0.1:8199/test", `<doc><id>1</id><name>john</name><password1>123Abc!@#</password1><password2>123Abc!@#</password2></doc>`)
|
||||
fmt.Println(r)
|
||||
}
|
||||
@ -4,13 +4,12 @@ import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gview"
|
||||
"github.com/gogf/gf/util/gpage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/demo", func(r *ghttp.Request) {
|
||||
page := gpage.New(100, 10, r.Get("page"), r.URL.String())
|
||||
page := r.GetPage(100, 10)
|
||||
buffer, _ := gview.ParseContent(`
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@ -4,14 +4,13 @@ import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gview"
|
||||
"github.com/gogf/gf/util/gpage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/ajax", func(r *ghttp.Request) {
|
||||
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
|
||||
page.EnableAjax("DoAjax")
|
||||
page := r.GetPage(100, 10)
|
||||
page.AjaxActionName = "DoAjax"
|
||||
buffer, _ := gview.ParseContent(`
|
||||
<html>
|
||||
<head>
|
||||
@ -29,11 +28,17 @@ func main() {
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>{{.page}}</div>
|
||||
<div>{{.page1}}</div>
|
||||
<div>{{.page2}}</div>
|
||||
<div>{{.page3}}</div>
|
||||
<div>{{.page4}}</div>
|
||||
</body>
|
||||
</html>
|
||||
`, g.Map{
|
||||
"page": page.GetContent(1),
|
||||
"page1": page.GetContent(1),
|
||||
"page2": page.GetContent(2),
|
||||
"page3": page.GetContent(3),
|
||||
"page4": page.GetContent(4),
|
||||
})
|
||||
r.Response.Write(buffer)
|
||||
})
|
||||
|
||||
@ -8,7 +8,7 @@ import (
|
||||
"github.com/gogf/gf/util/gpage"
|
||||
)
|
||||
|
||||
// 分页标签使用li标签包裹
|
||||
// wrapContent wraps each of the page tag with html li and ul.
|
||||
func wrapContent(page *gpage.Page) string {
|
||||
content := page.GetContent(4)
|
||||
content = gstr.ReplaceByMap(content, map[string]string{
|
||||
@ -21,9 +21,9 @@ func wrapContent(page *gpage.Page) string {
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/custom1/*page", func(r *ghttp.Request) {
|
||||
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
|
||||
page := r.GetPage(100, 10)
|
||||
content := wrapContent(page)
|
||||
buffer, _ := gview.ParseContent(`
|
||||
<html>
|
||||
|
||||
@ -7,7 +7,7 @@ import (
|
||||
"github.com/gogf/gf/util/gpage"
|
||||
)
|
||||
|
||||
// 自定义分页名称
|
||||
// pageContent customizes the page tag name.
|
||||
func pageContent(page *gpage.Page) string {
|
||||
page.NextPageTag = "NextPage"
|
||||
page.PrevPageTag = "PrevPage"
|
||||
@ -15,16 +15,16 @@ func pageContent(page *gpage.Page) string {
|
||||
page.LastPageTag = "LastPage"
|
||||
pageStr := page.FirstPage()
|
||||
pageStr += page.PrevPage()
|
||||
pageStr += page.PageBar("current-page")
|
||||
pageStr += page.PageBar()
|
||||
pageStr += page.NextPage()
|
||||
pageStr += page.LastPage()
|
||||
return pageStr
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/custom2/*page", func(r *ghttp.Request) {
|
||||
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
|
||||
page := r.GetPage(100, 10)
|
||||
buffer, _ := gview.ParseContent(`
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@ -4,13 +4,12 @@ import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gview"
|
||||
"github.com/gogf/gf/util/gpage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/static/*page", func(r *ghttp.Request) {
|
||||
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
|
||||
page := r.GetPage(100, 10)
|
||||
buffer, _ := gview.ParseContent(`
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@ -4,13 +4,12 @@ import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gview"
|
||||
"github.com/gogf/gf/util/gpage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/:obj/*action/{page}.html", func(r *ghttp.Request) {
|
||||
page := gpage.New(100, 10, r.Get("page"), r.URL.String(), r.Router)
|
||||
page := r.GetPage(100, 10)
|
||||
buffer, _ := gview.ParseContent(`
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@ -4,14 +4,13 @@ import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gview"
|
||||
"github.com/gogf/gf/util/gpage"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/page/template/{page}.html", func(r *ghttp.Request) {
|
||||
page := gpage.New(100, 10, r.Get("page"), r.URL.String())
|
||||
page.SetUrlTemplate("/order/list/{.page}.html")
|
||||
page := r.GetPage(100, 10)
|
||||
page.UrlTemplate = "/order/list/{.page}.html"
|
||||
buffer, _ := gview.ParseContent(`
|
||||
<html>
|
||||
<head>
|
||||
|
||||
@ -8,6 +8,9 @@ import (
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 100; i++ {
|
||||
fmt.Println(grand.Rand(0, 99999))
|
||||
fmt.Println(grand.S(16))
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
fmt.Println(grand.N(0, 99999999))
|
||||
}
|
||||
}
|
||||
|
||||
13
.example/util/guid/guid.go
Normal file
13
.example/util/guid/guid.go
Normal file
@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/util/guid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S()
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
}
|
||||
20
.example/util/guid/guid_length.go
Normal file
20
.example/util/guid/guid_length.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 36*36^2+36*36+36
|
||||
var s string
|
||||
fmt.Println(strconv.ParseUint("zzz", 36, 3))
|
||||
fmt.Println(1 << 1)
|
||||
// MaxInt64
|
||||
s = strconv.FormatUint(math.MaxUint64, 16)
|
||||
fmt.Println(s, len(s))
|
||||
// PID
|
||||
s = strconv.FormatInt(1000000, 36)
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
23
.example/util/guid/guid_unique.go
Normal file
23
.example/util/guid/guid_unique.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/util/guid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S([]byte("123"))
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
fmt.Println()
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S([]byte("123"), []byte("456"))
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
fmt.Println()
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S([]byte("123"), []byte("456"), []byte("789"))
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
}
|
||||
13
.example/util/gvalid/gvalid_custom_message.go
Normal file
13
.example/util/gvalid/gvalid_custom_message.go
Normal file
@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g.I18n().SetLanguage("cn")
|
||||
err := gvalid.Check("", "required", nil)
|
||||
fmt.Println(err.String())
|
||||
}
|
||||
14
.example/util/gvalid/i18n/cn.toml
Normal file
14
.example/util/gvalid/i18n/cn.toml
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
"gf.gvalid.required" = "字段不能为空"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,3 +15,4 @@ cbuild
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
go.sum
|
||||
.example/other/
|
||||
@ -4,6 +4,7 @@ go:
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
- "1.13.x"
|
||||
- "1.14.x"
|
||||
|
||||
branches:
|
||||
only:
|
||||
@ -12,7 +13,7 @@ branches:
|
||||
- staging
|
||||
|
||||
env:
|
||||
- GF_DEV=1 GO111MODULE=on
|
||||
- GF_DEBUG=1 GO111MODULE=on
|
||||
|
||||
services:
|
||||
- mysql
|
||||
|
||||
53
DONATOR.MD
53
DONATOR.MD
@ -2,6 +2,7 @@
|
||||
|
||||
We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill.
|
||||
|
||||
> If you cannot view the donation image, please click [here](https://goframe.org/images/donate.png).
|
||||
|
||||
| Name | Channel | Amount | Comment
|
||||
|---|---|--- | ---
|
||||
@ -14,7 +15,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|
||||
|[zhuhuan12](https://gitee.com/zhuhuan12)|gitee|¥50.00 |
|
||||
|[zfan_codes](https://gitee.com/zfan_codes)|gitee|¥10.00 |
|
||||
|[arden](https://github.com/arden)|alipay|¥10.00 |
|
||||
|[macnie](https://www.macnie.com)|wechat|¥100.00 |
|
||||
|[macnie](https://www.macnie.com)|wechat|¥110.00 |
|
||||
|lah|wechat|¥100.00 |
|
||||
|x*z|wechat|¥20.00 |
|
||||
|潘兄|wechat|¥100.00 |
|
||||
@ -39,9 +40,57 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|
||||
|R*s|wechat|¥18.88| 谢谢GF!辛苦了!
|
||||
|粟*e|wechat|¥50.00|
|
||||
|[李超](https://github.com/effortlee)|wechat|¥124.00|
|
||||
|张炳贤|wechat+qq|¥600.00|
|
||||
|soidea666|wechat+qq|¥800.00|
|
||||
|[王哈哈](https://gitee.com/develop1024)|wechat|¥6.66| 希望gf越来越好
|
||||
|夕景|alipay+qq|¥9.96+3.57|
|
||||
|struggler|alipay|¥18.80|
|
||||
|*铁|wechat|¥0.01|
|
||||
|C*e|wechat|¥66.66| GF越来越好,棒👍!
|
||||
|(佚名)|wechat|¥6.66| (名字打不出来,也没备注,捐赠时间2020-02-21 14:24:34)
|
||||
|[王飞](https://gitee.com/wang_2018)|gitee|¥20.00| 感谢您的开源项目!
|
||||
|[Zeroing-ZY](https://gitee.com/yunjieg)|gitee|¥20.00| 感谢您的开源项目!
|
||||
|[katydid酱](https://gitee.com/katydid2005)|gitee|¥50.00| 感谢您的开源项目!框架给予了很大的帮助!谢谢大佬!
|
||||
|[李海峰](https://gitee.com/dlhf)|gitee|¥10.00| 希望GF越来越好,框架很牛逼!
|
||||
|陆昱天|alipay|¥100.00|
|
||||
|[Dockercore](https://github.com/dockercore)|wechat|¥200.00| 非常喜欢!简洁好用!文档超级全!
|
||||
|🚶|wechat|¥6.88| 喝杯冰阔落
|
||||
|a*l|wechat|¥10.00| gf
|
||||
|[wxkj](https://gitee.com/wxkj)|wechat|¥10.00|
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥9.99|
|
||||
|重庆宝尔威科技|wechat|¥6.66|
|
||||
|琦玉-QPT|wechat|¥6.66|
|
||||
|sailsea|wechat|¥11.00|
|
||||
|[seny0929](https://gitee.com/seny0929)|wechat|¥99.90|
|
||||
|*华|wechat|¥6.66| 感谢郭强的热心
|
||||
|[Playhi](https://github.com/Playhi)|alipay|¥10.00|
|
||||
|北京京纬互动科技|alipay|¥200.00|
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥99.99|
|
||||
|金毛|alipay|¥100.00|
|
||||
|1*1x|wechat|¥100.00|
|
||||
|[ywanbing](https://github.com/ywanbing)|wechat|¥66.66|
|
||||
|[侯哥](http://www.macnie.com)|wechat|¥10.00|
|
||||
|如果🍋|alipay|¥100.00| 错过的奶茶^_^
|
||||
|蔡蔡|wechat|¥666.00| gf真强大,让项目省心
|
||||
|jack|wechat|¥100.00|
|
||||
|sbilly|wechat|¥100.00| 祝好!
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥166.66| 大佬加油!
|
||||
|*秦|wechat|¥20.00| 给群主献上一杯咖啡...
|
||||
|[zhuhuan12](https://gitee.com/zhuhuan12)|wechat|¥50.00|
|
||||
|faddei|qq|¥9.99|
|
||||
|*天|wechat|¥10.00|
|
||||
|*.|wechat|¥20.00| 学生一个微薄之力,冲
|
||||
|李勇|wechat|¥50.00|
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥66.66|
|
||||
|千年|wechat|¥1.08|
|
||||
|[szzxing](https://github.com/szzxing)|wechat|¥50.00|
|
||||
|伟客互联|wechat|¥66.66|
|
||||
|sbilly|wechat|¥99.00|
|
||||
|**阳|alipay|¥100.01|
|
||||
|**亮|alipay|¥10.00|
|
||||
|[mingzaily](https://github.com/mingzaily)|alipay|¥30.00|
|
||||
|[seny0929](https://gitee.com/seny0929)|alipay|¥9.90|
|
||||
|Fly的狐狸|alipay|¥100.00|
|
||||
|
||||
|
||||
<img src="https://goframe.org/images/donate.png"/>
|
||||
|
||||
|
||||
118
README.MD
118
README.MD
@ -1,6 +1,6 @@
|
||||
# GoFrame
|
||||
|
||||
[](https://godoc.org/github.com/gogf/gf/g#pkg-subdirectories)
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
@ -9,13 +9,13 @@
|
||||
|
||||
English | [简体中文](README_ZH.MD)
|
||||
|
||||
`GF(GoFrame)` is a modular, full-featured and production-ready application development framework of golang.
|
||||
Providing a series of core components and dozens of practical modules,
|
||||
such as: memcache, configure, validator, logging, array/queue/set/map containers,
|
||||
timer/timing tasks, file/memory lock, object pool, database ORM, etc.
|
||||
Supporting web server integrated with router, cookie, session, middleware, logger,
|
||||
template, https, hooks, rewrites and many more features.
|
||||
`GF(GoFrame)` is a modular, powerful, high-performance and production-ready application development framework
|
||||
of golang. Providing a series of core components and dozens of practical modules, such as:
|
||||
cache, logging, containers, timer, resource, validator, database orm, etc.
|
||||
Supporting web server integrated with router, cookie, session, middleware, logger, configure,
|
||||
template, https, hooks, rewrites and many more features.
|
||||
|
||||
> If you're a newbie to `Go`, you may consider `GoFrame` easy and great as `Laravel` in `PHP`, `SpringBoot` in `Java` or `Django` in `Python`.
|
||||
|
||||
# Installation
|
||||
```
|
||||
@ -28,36 +28,122 @@ require github.com/gogf/gf latest
|
||||
|
||||
# Limitation
|
||||
```
|
||||
golang version >= 1.10
|
||||
golang version >= 1.11
|
||||
```
|
||||
|
||||
# Documentation
|
||||
# Packages
|
||||
1. **Primary Package**
|
||||
|
||||
* [APIDoc](https://godoc.org/github.com/gogf/gf)
|
||||
* [中文文档](https://goframe.org)
|
||||
The `gf` repository maintains some basic and most commonly used packages, keeping it as lightweight and simple as possible.
|
||||
|
||||
1. **Community Package**
|
||||
|
||||
The community packages are contributed and maintained by community members, which are hosted in `gogf` organization. Some of the community packages are separated from the `gf` repository, which are not of common usage or are with heavy dependencies.
|
||||
|
||||
# Architecture
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png?v=10"/>
|
||||
<img src="https://goframe.org/images/arch.png?v=11"/>
|
||||
</div>
|
||||
|
||||
# Performance
|
||||
|
||||
Here's the most popular Golang frameworks and libraries performance testing result in `WEB Server`. Performance testing cases source codes are hosted at: https://github.com/gogf/gf-performance
|
||||
|
||||
## Environment
|
||||
|
||||
OS : Ubuntu 18.04 amd64
|
||||
CPU : AMD A8-6600K x 4
|
||||
MEM : 32GB
|
||||
GO : v1.13.4
|
||||
|
||||
## Testing Tool
|
||||
|
||||
`ab`: Apache HTTP server benchmarking tool.
|
||||
|
||||
Command:
|
||||
```
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/hello
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/query?id=10000
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/json
|
||||
```
|
||||
The concurrency starts from `100` to `10000`.
|
||||
|
||||
> Run `5` times for each case of each project and pick up the best testing result.
|
||||
|
||||
## 1. Hello World
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency1.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 2. Json Response
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency3.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
# Documentation
|
||||
|
||||
* 中文官网: https://goframe.org
|
||||
* GoDoc API: https://godoc.org/github.com/gogf/gf
|
||||
|
||||
|
||||
# Discussion
|
||||
- QQ Group:[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
|
||||
- WX Group:Add friend`389961817` in WeChat, commenting `GF`
|
||||
- Issues:https://github.com/gogf/gf/issues
|
||||
|
||||
> It's recommended learning `GoFrame` through its awesome source codes and API reference.
|
||||
|
||||
# License
|
||||
|
||||
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
|
||||
|
||||
# Known Users
|
||||
|
||||
Logos are not authorized to be shown due to trademark copyrights.
|
||||
|
||||
|
||||
# Contributors
|
||||
This project exists thanks to all the people who contribute. [[Contributors](https://github.com/gogf/gf/graphs/contributors)].
|
||||
<a href="https://github.com/gogf/gf/graphs/contributors"><img src="https://opencollective.com/goframe/contributors.svg?width=890&button=false" /></a>
|
||||
|
||||
|
||||
# Donators
|
||||
|
||||
We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill. If you like `GF`, why not [buy developer a cup of coffee](DONATOR.MD)?
|
||||
|
||||
# Sponsors
|
||||
We appreciate any kind of sponsorship for `GF` development. If you've got some interesting, please contact WeChat `389961817` / Email `john@goframe.org`.
|
||||
|
||||
|
||||
|
||||
# Thanks
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
|
||||
|
||||
|
||||
<!--
|
||||
# Sponsor
|
||||
We appreciate any kind of sponsorship for `GF` development. If you've got some interested, please contact john@goframe.org.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
121
README_ZH.MD
121
README_ZH.MD
@ -1,5 +1,5 @@
|
||||
# GoFrame
|
||||
[](https://godoc.org/github.com/gogf/gf/g#pkg-subdirectories)
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
@ -8,21 +8,28 @@
|
||||
|
||||
[English](README.MD) | 简体中文
|
||||
|
||||
`GF(Go Frame)`是一款模块化、高性能、生产级的Go基础开发框架。实现了比较完善的基础设施建设,包括常用的核心开发组件,
|
||||
如:缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、资源管理、数据校验、数据编码、文件监控、
|
||||
定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、并发安全容器等等。
|
||||
并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、配置管理、模板引擎等等,
|
||||
支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。
|
||||
`GF(Go Frame)`是一款模块化、高性能、生产级的Go基础开发框架。
|
||||
实现了比较完善的基础设施建设以及开发工具链,提供了常用的基础开发模块,
|
||||
如:缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、
|
||||
配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。
|
||||
并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、模板引擎等等,
|
||||
支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
|
||||
|
||||
> 如果您初识`Go`语言,您可以将`GoFrame`类似于`PHP`中的`Laravel`, `Java`中的`SpringBoot`或者`Python`中的`Django`。
|
||||
|
||||
# 特点
|
||||
* 模块化、松耦合设计;
|
||||
* 模块丰富,开箱即用;
|
||||
* 简便及可维护性为宗旨;
|
||||
* 模块丰富、开箱即用;
|
||||
* 简便易用、易于维护;
|
||||
* 高代码质量、高单元测试覆盖率;
|
||||
* 社区活跃,大牛谦逊低调脾气好;
|
||||
* 详尽的开发文档及示例;
|
||||
* 完善的本地中文化支持;
|
||||
* 更适合企业及团队使用;
|
||||
* 更多请查阅文档及源码;
|
||||
* 设计为团队及企业使用;
|
||||
|
||||
# 地址
|
||||
- **主库**:https://github.com/gogf/gf
|
||||
- **码云**:https://gitee.com/johng/gf
|
||||
|
||||
# 安装
|
||||
```html
|
||||
@ -38,27 +45,113 @@ require github.com/gogf/gf latest
|
||||
golang版本 >= 1.11
|
||||
```
|
||||
|
||||
# 模块
|
||||
|
||||
1. **核心模块**
|
||||
|
||||
`GoFrame`提供了一些基础的、常用的模块,简单、易用和轻量级,并保持极少的外部依赖,这些模块由`gf`主仓库细致维护。
|
||||
|
||||
1. **社区模块**
|
||||
|
||||
社区模块主要由社区贡献并维护,大部分也是由`gf`主仓库的贡献者提供及维护,存放于`gogf`空间下,与`gf`主仓库处于同一级别。有的社区模块是从`gf`主仓库中剥离出来单独维护的模块,这些模块并不是特别常用,或者对外部依赖较重。
|
||||
|
||||
|
||||
# 架构
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png?v=10"/>
|
||||
<img src="https://goframe.org/images/arch.png?v=11"/>
|
||||
</div>
|
||||
|
||||
# 性能
|
||||
|
||||
以下是目前最流行的`WEB Server` Golang框架/类库性能测试结果。
|
||||
性能测试用例源代码仓库: https://github.com/gogf/gf-performance
|
||||
|
||||
## 环境:
|
||||
|
||||
OS : Ubuntu 18.04 amd64
|
||||
CPU : AMD A8-6600K x 4
|
||||
MEM : 32GB
|
||||
GO : v1.13.4
|
||||
|
||||
## 工具
|
||||
|
||||
`ab`: Apache HTTP server benchmarking tool.
|
||||
|
||||
测试命令:
|
||||
```
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/hello
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/query?id=10000
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/json
|
||||
```
|
||||
并发客户端数量从 `100` 递增到 `10000`。
|
||||
|
||||
> 每个项目的每个用例均运行`5`次,取最优的结果展示。
|
||||
|
||||
## 1. Hello World
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency1.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 2. Json Response
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency3.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
# 文档
|
||||
|
||||
开发文档:[https://goframe.org](https://goframe.org)
|
||||
开发文档:https://goframe.org
|
||||
|
||||
接口文档:[https://godoc.org/github.com/gogf/gf](https://godoc.org/github.com/gogf/gf)
|
||||
接口文档:https://godoc.org/github.com/gogf/gf
|
||||
|
||||
# 帮助
|
||||
- QQ交流群:[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
|
||||
- WX交流群:微信添加`389961817`备注`GF`
|
||||
- 主库ISSUE:https://github.com/gogf/gf/issues
|
||||
|
||||
> 建议通过阅读`GoFrame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。
|
||||
|
||||
# 协议
|
||||
|
||||
`GF` 使用非常友好的 [MIT](LICENSE) 开源协议进行发布,永久`100%`开源免费。
|
||||
|
||||
# 用户
|
||||
|
||||
由于商标版权缘故,未经厂商商务部授权允许无法用于宣传展示。
|
||||
|
||||
# 贡献
|
||||
|
||||
感谢所有参与`GoFrame`开发的贡献者。 [[贡献者列表](https://github.com/gogf/gf/graphs/contributors)].
|
||||
<a href="https://github.com/gogf/gf/graphs/contributors"><img src="https://opencollective.com/goframe/contributors.svg?width=890&button=false" /></a>
|
||||
|
||||
|
||||
# 捐赠
|
||||
|
||||
如果您喜欢`GF`,要不[给开发者来杯咖啡吧](DONATOR.MD)!
|
||||
请在捐赠时备注您的`github`/`gitee`账号名称。
|
||||
|
||||
# 赞助
|
||||
|
||||
赞助支持`GF`框架的快速研发,如果您感兴趣,请联系 微信 `389961817` / 邮件 `john@goframe.org`。
|
||||
|
||||
# 感谢
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
|
||||
|
||||
|
||||
348
RELEASE.2.MD
348
RELEASE.2.MD
@ -1,3 +1,351 @@
|
||||
# `v1.12.1` (2020-03-31)
|
||||
|
||||
大家好啊!久等啦!
|
||||
|
||||
由于自从上次版本的发布以来,越来越多小伙伴加入了`GF`的大家庭,并提供了许多不错的建议和反馈,这次版本对其中大部分反馈进行了处理,包括大部分的改进建议和部分新特性,因此这次的版本发布时隔了两个多月。`GF`非常注重代码质量以及可持续维护性,这次版本也进一步对框架大部分模块的示例、注释和单元测试用例进行了完善,目前单元测试用例数量约为`1991`例,代码覆盖率为`71%`,覆盖了所有模块的绝大部分主要功能。
|
||||
|
||||
`GF`框架提供了比较常用、高质量的基础开发模块,轻量级、模块化、高性能,推荐大家阅读框架源码了解更多细节。本次发布有个别的不兼容升级,往往批量替换即可,以下`change log`比较完善,建议升级前仔细阅读。
|
||||
|
||||
本次发布即意味下一版本开发计划的开启,欢迎更多小伙伴参与开源贡献:https://github.com/gogf/gf/projects/8
|
||||
|
||||
感谢大家支持!Enjoy your `GF`!
|
||||
|
||||
|
||||
# GoFrame
|
||||
|
||||
`GF(Go Frame)`是一款模块化、高性能、生产级的Go基础开发框架。实现了比较完善的基础设施建设以及开发工具链,提供了常用的基础开发模块,如:缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、模板引擎等等,支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
|
||||
|
||||
## 特点
|
||||
* 模块化、松耦合设计;
|
||||
* 模块丰富,开箱即用;
|
||||
* 简便易用,易于维护;
|
||||
* 社区活跃,大牛谦逊低调脾气好;
|
||||
* 高代码质量、高单元测试覆盖率;
|
||||
* 详尽的开发文档及示例;
|
||||
* 完善的本地中文化支持;
|
||||
* 更适合企业及团队使用;
|
||||
|
||||
## 地址
|
||||
- 官网:https://goframe.org
|
||||
- 主库:https://github.com/gogf/gf
|
||||
- 码云:https://gitee.com/johng/gf
|
||||
|
||||
# Change Log
|
||||
|
||||
从`GF v1.12`版本开始,框架要求的最低`Golang`运行版本为`v1.13`,由于`Golang`新版本都是向后兼容的,因此推荐大家更新使用`Golang`新版本:https://golang.google.cn/dl/
|
||||
|
||||
> 本次版本也新增了`Swagger`的工具及插件支持,另行独立发布。
|
||||
|
||||
## `tool chain`
|
||||
1. `gen model`命令新增对`pgsql/mssql/sqlite/oracle`的模型生成支持。
|
||||
1. `gen model`命令生成模型新增公开包变量`Columns`用于获得表的字段名称。
|
||||
|
||||
## `net`
|
||||
1. `ghttp`
|
||||
- 注意:从该版本开始,`Server`默认关闭了平滑重启特性。开发者可以通过相应的配置选项打开。
|
||||
- 改进`Client.Get`方法,增加可选的请求参数。
|
||||
- 新增`Client`链式操作方法:`Header`, `HeaderRaw`, `Cookie`, `ContentType`, `ContentJson`, `ContentXml`, `Timeout`, `BasicAuth`, `Ctx`:https://goframe.org/net/ghttp/client/chain
|
||||
- 新增`Request.GetCtx/GetCtxVar/SetCtxVar/Context`上下文变量管理方法,用于请求内部的上下文变量特性:
|
||||
- 自定义变量:https://goframe.org/net/ghttp/request/custom
|
||||
- 上下文变量:https://goframe.org/net/ghttp/request/context
|
||||
- 新增`Request.GetUploadFile/GetUploadFiles`方法,以及`UploadFile`类型,极大简化文件上传处理逻辑:https://goframe.org/net/ghttp/client/demo/upload
|
||||
- 新增`Request.GetPage`方法,用于便捷地获得分页对象:
|
||||
- 基本介绍:https://goframe.org/util/gpage/index
|
||||
- 动态分页:https://goframe.org/util/gpage/dynamic
|
||||
- 静态分页:https://goframe.org/util/gpage/static
|
||||
- Ajax分页:https://goframe.org/util/gpage/ajax
|
||||
- URL模板:https://goframe.org/util/gpage/template
|
||||
- 自定义分页:https://goframe.org/util/gpage/custom
|
||||
- 改进`Response.Redirect*`方法,增加自定义的跳转HTTP状态码参数。
|
||||
- 改进`CORS`特性,完善跨域功能处理,并完全遵守`W3C`关于`OPTIONS`请求方法的规范约定:https://goframe.org/net/ghttp/cors
|
||||
- 模板视图对象增加`Request`内置变量,用于模板获得请求参数。由于`GF`框架的模板引擎采用两级缓存设计,减少`IO`开销的同时提升了执行效率,即使增加了大量的内置变量以及内置方法,经过大规模的性能测试,性能比其他`WebServer`库相同逻辑下高出`50% - 200%`的效率。
|
||||
- 新增`Server`实验性的`Plugin`特性。
|
||||
- 改进`Server`底层路由存储、检索逻辑,优化代码,提升效率。
|
||||
- 改进分组路由注册的源码位置记录功能,当路由注册冲突时能够精准定位及提示重复路由源码位置。
|
||||
- 改进`Server`日志处理。
|
||||
- 完善单元测试。
|
||||
|
||||
|
||||
## `database`
|
||||
1. `gdb`
|
||||
- 代码重构改进,增加`Driver`驱动接口设计,方便开发者自定义驱动实现。新增`Register`包方法,用于开发者注册自定义的数据库类型驱动:https://goframe.org/database/gdb/driver
|
||||
- 新增`GetArray`接口及实现,用于获取指定字段列的数据,构造成数组返回:https://goframe.org/database/gdb/chaining/select
|
||||
- 新增`InsertIgnore`接口及实现,用于写入时忽略写入冲突,仅对`mysql`数据库类型有效:https://goframe.org/database/gdb/chaining/insert-save
|
||||
- 新增`Schema`接口及实现,用于动态切换并获取指定名称的数据库对象:https://goframe.org/database/gdb/chaining/schema
|
||||
- 新增`FieldsStr/FieldsExStr`模型方法,用于获取表字段,并构造成字符串返回:hhttps://goframe.org/database/gdb/chaining/fields-retrieve
|
||||
- 新增`LockUpdate/LockShared`模型链式操作方法,用于实现悲观锁操作:https://goframe.org/database/gdb/chaining/lock
|
||||
- 改进`Where/Data`方法对更新参数输入方式的支持,提高灵活性:
|
||||
- https://goframe.org/database/gdb/chaining/select
|
||||
- https://goframe.org/database/gdb/chaining/update-delete
|
||||
- 查询结果对象`Result`新增`Array`方法,用于获得指定字段的数值,构造成数组返回:https://goframe.org/database/gdb/result
|
||||
- 改进`OmitEmpty`方法对`Data`输入参数的过滤,当给定的`Data`参数为空时间对象时,将会被过滤。
|
||||
- 增加默认的数据库连接池参数:`MaxIdleConns=10`。
|
||||
- 其他一些改进。
|
||||
- 完善单元测试。
|
||||
|
||||
1. `gredis`
|
||||
- 增加/修改默认的数据库连接池参数:`MaxIdle=10`, `IdleTimeout=10s`, `MaxConnLifetime=30s`, `Wait=true`。
|
||||
- 完善单元测试。
|
||||
|
||||
## `container`
|
||||
1. 所有容器对象新增`UnmarshalValue(interface{}) error`接口方法实现,用于`gconv`转换时根据任意类型变量创建/设置对象内容,`GF`框架的所有容器对象均实现了该接口。
|
||||
|
||||
1. `garray`
|
||||
- 新增`RemoveValue`方法,用于根据数值检索并删除元素项。
|
||||
- 新增`FilterNil`方法,用于遍历并删除数组中的`nil`元素项。
|
||||
- 新增`FilterEmpty`方法,用于遍历并删除数组中的空值元素项,空值包括:`0, nil, false, "", len(slice/map/chan) == 0`。
|
||||
- 改进`Set/Fill/InsertBefore/InsertAfter`方法严谨性,将原有的链式操作返回值对象修改为`error`返回值。
|
||||
- 改进`Get/Remove/PopRand/PopLeft/PopRight/Rand`方法严谨性,增加`found`的`bool`返回值,标识当前方法是否有数据返回,当空数组或者操作越界时`found`返回值为`false`。
|
||||
- 改变`Rands`方法原有逻辑,保证按照给定大小返回随机数组。
|
||||
- 完善单元测试。
|
||||
|
||||
1. `gpool`
|
||||
- 调整缓存池过期时间参数类型,旧版本为`int`类型表示毫秒,新版本为`time.Duration`类型使得时间控制更灵活。
|
||||
- 内部代码优化,性能改进。
|
||||
- 完善单元测试。
|
||||
- 完善注释。
|
||||
|
||||
## `os`
|
||||
1. `glog`
|
||||
- 增加`Rotation`日志滚动切分特性,新增按照文件大小或过期时间进行日志切分,并支持切分文件数量限制、对日志文件进行自动压缩、可自定义压缩级别(`1-9`)、支持对切分文件过期时间清理:https://goframe.org/os/glog/rotate
|
||||
- 新增`LevelPrefixes`特性,支持对日志级别的前缀名称进行自定义:https://goframe.org/os/glog/level
|
||||
- 新增`SetLevelStr`方法,并增加按照字符串进行日志级别配置的特性:
|
||||
- https://goframe.org/os/glog/level
|
||||
- https://goframe.org/os/glog/config
|
||||
- 新增`SetDefaultLogger`包方法,用于设置全局默认的`Logger`对象。
|
||||
|
||||
1. `gres`
|
||||
1. 改进资源内容编码设计,采用新的压缩算法,减少资源文件大小,例如原本`15MB`的网站静态资源文件(`css/js/html`等),资源文件打包后约为`4MB`左右:https://goframe.org/os/gres/index
|
||||
1. 注意:该改进与旧版本无法兼容,需要重新打包原有的资源文件。
|
||||
1. 完善单元测试。
|
||||
|
||||
1. `gcfg`
|
||||
- 去掉配置对象属性的原子并发安全控制,改为普通变量类型。由于配置管理是最常用模块之一,因此确保高效的设计及方法实现。
|
||||
- 单例对象做较大调整:为方便多文件场景下的配置文件调用,简便使用并提高开发效率,因此当给定的单例名称对应的`toml`配置文件在配置目录中存在时,将自动设置该单例对象的默认配置文件为该文件。例如:`g.Cfg("redis")`获取到的单例对象将会默认去检索并设置默认的配置文件为`redis.toml`,当该文件不存在时,则使用默认的配置文件(`config.toml`):https://goframe.org/net/ghttp/config
|
||||
- 完善单元测试。
|
||||
|
||||
1. `gview`
|
||||
- 新增`concat`内置字符串拼接方法:https://goframe.org/os/gview/function/buildin
|
||||
- 完善单元测试。
|
||||
|
||||
1. `gfile`
|
||||
- 改进`SelfPath`获取当前执行文件路径方法,提高执行效率。
|
||||
- 改进`Join`文件路径连接方法,防止多余的路径连接符号。
|
||||
- 改建`GetContents`文件内容获取方法执行效率,降低内存占用。
|
||||
- 新增`StrToSize`方法,用于将大小字符串转换为字节数字,大小字符串例如`10m`, `5KB`, `1.25Gib`等。
|
||||
- 新增`ReadLines/ReadByteLines`方法,用于按行读取指定文件内容,并给定读取回调函数。
|
||||
- 完善单元测试。
|
||||
|
||||
1. `gtime`
|
||||
- 改进`gtime.Time`对象实现,统一字符串打印时间格式为`Y-m-d H:i:s`,如:`2020-01-01 12:00:00`。
|
||||
|
||||
1. `gcmd`
|
||||
- 命令行解析方法增加`strict`参数,用于设置当前解析是否严格解析,严格解析下如果给定了非法的选项,将会停止解析并返回错误。默认情况下为非严格解析。
|
||||
|
||||
1. `genv`
|
||||
- 改进`Remove`删除环境变量键值对方法,增加对多个键值对环境变量的删除。
|
||||
|
||||
1. `gfpool`
|
||||
- 改进代码实现,提高效率。
|
||||
- 完善单元测试。
|
||||
|
||||
1. `gfsnotify`
|
||||
- 改进包初始化方法,当系统原因引起默认`Watcher`对象创建失败时,直接`panic`。
|
||||
|
||||
1. `gproc`
|
||||
- 改进`SearchBinaryPath`方法。
|
||||
- 改进`Process.Kill`方法在`windows`及`*niux`平台下不同表现的处理。
|
||||
|
||||
## `encoding`
|
||||
1. `gjson`
|
||||
- 代码改进、完善注释、新增大量代码示例。
|
||||
- 文档更新:
|
||||
- 基本介绍:https://goframe.org/encoding/gjson/index
|
||||
- 对象创建:https://goframe.org/encoding/gjson/object
|
||||
- 层级访问:https://goframe.org/encoding/gjson/pattern
|
||||
- Struct转换:https://goframe.org/encoding/gjson/conversion-struct
|
||||
- 动态创建修改:https://goframe.org/encoding/gjson/dataset
|
||||
- 数据格式转换:https://goframe.org/encoding/gjson/conversion-format
|
||||
|
||||
## `frame`
|
||||
1. `g`
|
||||
- 新增`IsNil`方法,用于判断当前给定的变量是否为`nil`,该方法有可能会使用到反射来实现判断。
|
||||
- 新增`IsEmpty`方法,用于判断当前给定的变量是否为`空`,该方法优先使用断言判断但有可能会使用到反射来实现判断。空值包括:`0, nil, false, "", len(slice/map/chan) == 0`。
|
||||
- 标记废弃`SetServerGraceful`方法,转而统一交给`Server`的配置来管理。
|
||||
1. `gins`
|
||||
- 改进代码结构,方便维护。
|
||||
- 改进、完善单元测试。
|
||||
1. `gmvc`
|
||||
- 新增`M`类型,为`*gdb.Model`的别名简称,用于工具链自动生成模型中的`M`属性。
|
||||
|
||||
## `text`
|
||||
1. `gstr`
|
||||
- 新增`HasPrefix/HasSuffix`方法。
|
||||
- 新增`OctStr`方法,用于将八进制字符串转换为其对应的`unicode`字符串,例如转换为中文。常用于`gRPC`底层通信编码中。
|
||||
- 完善单元测试。
|
||||
|
||||
## `debug`
|
||||
1. `gdebug`
|
||||
- 改进代码结构,方便维护。
|
||||
- 新增`TestDataPath`方法,用于当前包单元测试中获得当前包中`testdata`目录的绝对路径。
|
||||
|
||||
## `util`
|
||||
1. `gconv`
|
||||
- 改进`String`字符串转换方法,增加对`time.Time/*time.Time/gtime.Time`类型的内置支持。
|
||||
- 改进`Map/Struct`转换方法,增加对一些特殊场景的细节处理。经过大规模的使用,大量的反馈改进,不断完善了细节。
|
||||
- 改进`Struct`转换方法,增加对`UnmarshalValue(interface{}) error`接口的支持。
|
||||
- 完善单元测试。
|
||||
|
||||
1. `grand`
|
||||
- 注意:不兼容调整,原有的`Str`方法更名为`S`方法,用以获取指定长度的随机字符串、数字,并增加`symbol`参数,指定是否可以随机返回特殊可见字符。
|
||||
- 新增`Str`方法,用于从指定的字符串字符中随机获取指定长度的字符串。该方法同时支持`unicode`字符串,例如:中文:https://goframe.org/util/grand/index
|
||||
- 新增`Symbols`方法,用于随机返回指定场孤独的特殊可见字符:https://goframe.org/util/grand/index
|
||||
- 完善单元测试。
|
||||
|
||||
1. `gvalid`
|
||||
- 长度校验规则增加对`unicode`字符串的支持,例如:中文。
|
||||
|
||||
# Bug Fix
|
||||
1. 修复`Server`的视图对象配置失效问题。
|
||||
1. 修复`Server`中间件在中间件`panic`情况下,忽略`Middleware.Next`方法控制,导致鉴权中间件失效的问题。
|
||||
1. 修复`gudp.Server`在请求包大小超过`64bytes`时的断包问题,并调整默认缓冲区大小为`1024byte`,开发者可自定义缓冲区大小。
|
||||
1. 修复`gfile.MTimeMillisecond`方法返回错误的文件修改毫秒时间戳。
|
||||
1. 修复`gconv.Int64`对负数转换的支持。
|
||||
1. 其他一些修复。
|
||||
1. 详见:https://github.com/gogf/gf/issues?q=label%3Abug
|
||||
|
||||
|
||||
|
||||
# `v1.11.2` (2020-01-14)
|
||||
|
||||
`GF(Go Frame)` https://goframe.org 是一款模块化、高性能、生产级的Go基础开发框架。实现了比较完善的基础设施建设,包括常用的核心开发组件, 如:缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、资源管理、数据校验、数据编码、文件监控、 定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、并发安全容器等等。 并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、配置管理、模板引擎等等, 支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。
|
||||
|
||||
`GF`有着丰富的基础模块、完善的工具链、详尽的开发文档。开源近两年以来,`GF`得到越来越多小伙伴的肯定和支持,从寂寂无名到现在被广泛应用于微服务、物联网、区块链、电商系统、银行系统等企业级的生产项目中,经历了百万级、千万级项目的考验,2019年度被码云`gitee`评选为`GVP`最有价值开源项目。`GF`正在快速地成长中,目前保持着1-2个月迭代版本的发布规律,社区活跃,欢迎加入`GF`大家庭。
|
||||
|
||||
最后,祝大家2020新年快乐,鼠年大吉!
|
||||
|
||||
|
||||
|
||||
## 新特性
|
||||
|
||||
1. 新年新气象,官网文档大量更新:https://goframe.org/index
|
||||
1. `GF`工具链更新:https://goframe.org/toolchain/cli
|
||||
- 新增`gf run`热编译运行命令;
|
||||
- 新增`gf docker` Docker镜像编译命令;
|
||||
- 新增`gf gen model` 强大的模型自动生成命令;
|
||||
- `gf build`命令增加对配置文件配置支持;
|
||||
- 大量命令行工具改进工作;
|
||||
- 新增自动代理设置特性;
|
||||
1. 数据库`ORM`新特性:
|
||||
- 增加`prefix`数据表前缀支持:https://goframe.org/database/gdb/config
|
||||
- 新增`Schema`数据库对象并改进数据库切换特性:https://goframe.org/database/gdb/chaining/schema
|
||||
- 新增`WherePri`方法,用于自动识别主键的条件方法:https://goframe.org/database/gdb/chaining/select
|
||||
- 文档及示例大量更新,覆盖95%以上的功能特性;
|
||||
|
||||
|
||||
|
||||
## 功能改进
|
||||
|
||||
### `container`
|
||||
1. `garray`
|
||||
- 新增`New*ArrayRange`方法,用于初始化创建指定数值范围的数组。
|
||||
- 新增`Iterator*`方法,用于数组项元素回调遍历。
|
||||
- 完善单元测试。
|
||||
1. `gvar`
|
||||
- 改进`MapStrStr`、`MapStrStrDeep`方法实现。
|
||||
|
||||
### `net`
|
||||
1. `ghttp`
|
||||
- 改进HTTP客户端,增加对提交参数的自动`Content-Type`识别功能。
|
||||
- `Request`对象增加`Parse`方法,用于快捷的对象转换即参数校验。
|
||||
- `Request.GetPost*`方法全部标记为`deprecated`,统一客户端参数提交方式为`QueryString`, `Form`, `Body`。
|
||||
- 去掉`Response`模板解析时的`Get`/`Post`内置变量,新增`Query`, `Form`, `Request`内置变量:https://goframe.org/net/ghttp/response/template
|
||||
- 改进`Response.WriteJson*`及`Response.WriteXml*`方法,增加对`string`, `[]byte`类型参数的支持。
|
||||
- `Server`新增`GetRouterArray`方法,用于向应用层暴露并获取`Server`的路由列表。
|
||||
- `Server`新增`Use`方法,该方法为`BindMiddlewareDefault`的别名,用以全局中间件的注册。
|
||||
- `Server`新增`RouteOverWrite`配置项,用于控制是否在注册路由冲突时自动覆盖,默认关闭并提示。
|
||||
- `Server`新增`Graceful`配置项,用于在单服务场景下控制平滑重启特性的开启/关闭,默认开启。
|
||||
- 完善单元测试。
|
||||
1. `gtcp`
|
||||
- 改进简单协议下的数据包发送接收功能。
|
||||
- 将连接池默认的缓存过期时间`30`秒修改为`10`秒。
|
||||
- 完善单元测试。
|
||||
|
||||
### `database`
|
||||
1. `gdb`
|
||||
- 新增`As`数据表别名方法。
|
||||
- 改进数据表、字段的安全字符自动识别添加功能。
|
||||
- 新增`DB`数据库对象切换方法。
|
||||
- 新增`TX`链式操作事务支持方法。
|
||||
- 完善单元测试。
|
||||
### `os`
|
||||
1. `gcfg`
|
||||
- 新增`GetMapStrStr`方法。
|
||||
1. `gcmd`
|
||||
- 增加参数解析的`strict`严格参数,默认严格解析,不存在指定参数/选项名称时则报错返回。
|
||||
1. `genv`
|
||||
- 改进`Remove`方法支持多个环境变量的删除。
|
||||
1. `gfile`
|
||||
- 改进`TempDir`临时目录获取方法,在`*nix`系统下默认为`/tmp`目录。
|
||||
- 新增`ReadLines`, `ReadByteLines`方法,用以按行回调读取文件内容。
|
||||
- 新增`Copy*`方法,用以文件/目录的拷贝,支持递归。
|
||||
- 新增`Replace*`方法,用以目录下的文件内容替换,支持递归。
|
||||
- 改进`Scan*`方法,用以检索并返回指定目录下的所有文件/目录,支持文件模式指定,支持递归。
|
||||
- 完善单元测试。
|
||||
1. `gproc`
|
||||
- 改进命令行运行方法。
|
||||
- 改进`Shell`命令文件检索逻辑。
|
||||
- 改进实验性的进程间通信设计。
|
||||
1. `gtime`
|
||||
- 将包方法以及`Time`对象的时间戳方法`Second`, `Millisecond`, `Microsecond`, `Nanosecond`标记为废除,
|
||||
并新增`Timestamp`, `TimestampMilli`, `TimestampMicro`, `TimestampNano`替换。
|
||||
- 需要注意的是以上修改可能和老版本存在兼容性问题。
|
||||
|
||||
1. `gview`
|
||||
- 解析功能、缓存设计改进。
|
||||
- 新增`encode`, `decode`HTML编码/解码模板函数。
|
||||
- 新增`concat`字符串拼接模板函数。
|
||||
- 新增`dump`模板函数,功能类似于`g.Dump`方法。
|
||||
- 新增`AutoEncode`配置项,用于自动转码输出的`HTML`内容,常用于防止`XSS`,默认关闭。需要注意的是该特性并不会影响`include`内置函数: https://goframe.org/os/gview/xss
|
||||
- 单元测试完善。
|
||||
|
||||
### `crypto`
|
||||
1. `gmd5`
|
||||
- 增加`MustEncrypt`, `MustEncryptBytes`, `MustEncryptString`, `MustEncryptFile`方法。
|
||||
1. `gsha1`
|
||||
- 增加`MustEncryptFile`方法
|
||||
|
||||
### `encoding`
|
||||
1. `gbase64`
|
||||
- 新增`MustEncodeFile`, `MustEncodeFileToString`, `MustDecode`, `MustDecodeToString`方法。
|
||||
1. `gjson`/`gparser`
|
||||
- 新增`GetMapStrStr`方法。
|
||||
- 新增`Must*`方法,用于指定数据格式的转换失败时产生`panic`错误,而不会返回`error`参数。
|
||||
|
||||
### `util`
|
||||
1. `gconv`
|
||||
- 改进`Convert`方法增加对`[]int32`, `[]int64`, `[]uint`, `[]uint32`, `[]uint64`, `[]float32`, `[]float64`数据类型的转换支持。
|
||||
- 改进`String`字符串转换方法对指针参数的支持。
|
||||
- 改进`Map*` Map转换方法的代码结构及性能。
|
||||
- 新增`Floats`, `Float32s`, `Float64s`对`[]float32`, `[]float64`类型转换方法。
|
||||
- 新增`Ints`, `Int32s`, `Int64s`对`[]int`, `[]int32`, `[]int64`类型转换方法。
|
||||
- 新增`Uints`, `Uint32s`, `Uint64s`对`[]uint`, `[]uint32`, `[]uint64`类型转换方法。
|
||||
- 完善单元测试。
|
||||
|
||||
|
||||
### `frame`
|
||||
1. `gins`
|
||||
- 所有的单例对象在获取失败时产生`panic`错误。
|
||||
|
||||
## Bug Fix
|
||||
1. 增加对常见错误路由格式例如`/user//index`的兼容支持。
|
||||
1. 修复`gtcp`/`gudp`在数据接收时的间隔时间单位问题。
|
||||
1. 修复`gfile`/`gspath`/`gfsnotify`包对文件的存在性判断不严谨问题。
|
||||
1. 修复`gproc.Kill`方法在`windows`系统下的运行阻塞问题。
|
||||
1. 修复`gstr.TrimLeftStr`/`gstr.TrimRightStr`在被替换字符串长度小于替换字符串长度时的数组溢出问题。
|
||||
|
||||
|
||||
|
||||
# `v1.10.0` (2019-12-05)
|
||||
|
||||
各位`gfer`久等了,较上一次发布时间过去已有两个多月了,这段时间`GF`也在不断地迭代改进,细节比较多,拟了个大概,以下是`release log`。
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
> This markdown is deprecated and will be removed in future. All TODO features are staged in the issue: https://github.com/gogf/gf/issues
|
||||
|
||||
# ON THE WAY
|
||||
1. 增加图形验证码支持,至少支持数字和英文字母;
|
||||
1. Cookie&Session数据池化处理;
|
||||
@ -10,7 +12,6 @@
|
||||
1. gjson对大json数据的解析效率问题;
|
||||
1. ghttp增加route name特性,并同时支持backend和template(提供内置函数)引用,可以通过RedirectRoute方法给定route name和路由参数跳转到指定的路由地址上;
|
||||
1. gvalid校验支持当第一个规则失败后便不再校验后续的规则,最好做成链式操作;
|
||||
1. gvalid增加支持对[]rune的长度校验(一个中文占3个字节);
|
||||
1. ghttp.Request增加对输入参数的自动HtmlEncode机制;
|
||||
1. 常量命名风格根据golint进行修改;
|
||||
1. 开放rwmutex包,并将gjson的互斥锁使用自定义的mutex替换;
|
||||
@ -21,8 +22,6 @@
|
||||
- ghttp Server&Client basic auth、
|
||||
- glog分类&日志等级&链式操作、gdb debug自动输出调试信息、gmlock内存锁、
|
||||
1. 服务注册域名增加对泛域名的支持;
|
||||
1. Cookie设置中文失效问题;
|
||||
1. 使用gconv将slice映射到struct属性上,例如redis hscan的结果集;
|
||||
1. 项目参考:
|
||||
- https://github.com/namreg/godown
|
||||
- https://github.com/Masterminds/sprig
|
||||
@ -31,32 +30,36 @@
|
||||
1. 路由增加不区分大小写得匹配方式;
|
||||
1. 改进WebServer获取POST参数处理逻辑,当提交非form数据时,例如json数据,针对某些方法可以直接解析;
|
||||
1. WebServer增加可选择的路由覆盖配置,默认情况下不覆盖;
|
||||
1. grpool性能压测结果变慢的问题;
|
||||
1. 增加jumplist的数据结构容器;
|
||||
1. DelayQueue/PriorityQueue;
|
||||
1. 权限管理模块;
|
||||
1. 从ghttp中剥离SESSION功能构成单独的模块gsession;
|
||||
1. 改进gproc进程间通信处理逻辑,提高稳定性,以应对进程间大批量的数据发送/接收;
|
||||
1. ghttp的热重启的本地进程端口监听,在不使用该特性时默认关闭掉;
|
||||
1. gtcp增加对TLS加密通信的支持;
|
||||
1. 添加Save/Replace/BatchSave/BatchReplace方法对sqlite数据库的支持;
|
||||
1. 添加sqlite数据库的单元测试用例;
|
||||
1. gredis增加cluster支持;
|
||||
1. gset.Add/Remove/Contains方法增加批量操作支持;
|
||||
1. gmlock增加手动清理机制:当内存锁不再使用时,由调用端决定是否清理内存锁;
|
||||
1. gtimer增加DelayAdd*方法返回Entry对象,以便DelayAdd*的定时任务也能进行状态控制;gcron同理需要改进;
|
||||
1. 改进gdb对pgsql/mssql/oracle的支持,使用方法覆盖的方式改进操作,而不是完全依靠正则替换的方式;
|
||||
1. gdb的Cache缓存功能增加可自定义缓存接口,以便支持外部缓存功能,缓存接口可以通过io.ReadWriter接口实现;
|
||||
1. grpool增加支持阻塞添加任务接口;
|
||||
1. gdb.Model在链式安全的对象创建中增加sync.Pool的使用;
|
||||
1. 增加g.Table快捷方法以方便操作数据表,但是得考虑后续模型操作设计,特别是脚手架的模型管理;
|
||||
1. 改进ghttp分组路由中对hook的支持方式,以便格式与BindHookHandler统一;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# DONE
|
||||
1. Cookie设置中文失效问题;
|
||||
1. gvalid增加支持对[]rune的长度校验(一个中文占3个字节);
|
||||
1. grpool性能压测结果变慢的问题;
|
||||
1. ghttp的热重启的本地进程端口监听,在不使用该特性时默认关闭掉;
|
||||
1. gtcp增加对TLS加密通信的支持;
|
||||
1. 改进gdb对pgsql/mssql/oracle的支持,使用方法覆盖的方式改进操作,而不是完全依靠正则替换的方式;
|
||||
1. gdb的Cache缓存功能增加可自定义缓存接口,以便支持外部缓存功能,缓存接口可以通过io.ReadWriter接口实现;
|
||||
1. 改进ghttp分组路由中对hook的支持方式,以便格式与BindHookHandler统一;
|
||||
1. 使用gconv将slice映射到struct属性上,例如redis hscan的结果集;
|
||||
1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换;
|
||||
2. ghttp.Server请求执行中增加服务退出的方法,不再执行后续操作;
|
||||
3. ghttp.Response对象完善并改进数据返回方法(Write/WriteString);
|
||||
@ -4,5 +4,5 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package garray provides concurrent-safe/unsafe arrays.
|
||||
// Package garray provides most commonly used array containers which also support concurrent-safe/unsafe switch feature.
|
||||
package garray
|
||||
|
||||
@ -8,6 +8,12 @@ package garray
|
||||
|
||||
import "strings"
|
||||
|
||||
// apiInterfaces is used for type assert api for Interfaces.
|
||||
type apiInterfaces interface {
|
||||
Interfaces() []interface{}
|
||||
}
|
||||
|
||||
// defaultComparatorInt for int comparison.
|
||||
func defaultComparatorInt(a, b int) int {
|
||||
if a < b {
|
||||
return -1
|
||||
@ -18,6 +24,7 @@ func defaultComparatorInt(a, b int) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// defaultComparatorStr for string comparison.
|
||||
func defaultComparatorStr(a, b string) int {
|
||||
return strings.Compare(a, b)
|
||||
}
|
||||
|
||||
@ -9,7 +9,9 @@ package garray
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"math"
|
||||
"sort"
|
||||
@ -19,8 +21,11 @@ import (
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// Array is a golang array with rich features.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type Array struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
array []interface{}
|
||||
}
|
||||
|
||||
@ -41,7 +46,7 @@ func NewArray(safe ...bool) *Array {
|
||||
// which is false in default.
|
||||
func NewArraySize(size int, cap int, safe ...bool) *Array {
|
||||
return &Array{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]interface{}, size, cap),
|
||||
}
|
||||
}
|
||||
@ -76,7 +81,7 @@ func NewFromCopy(array []interface{}, safe ...bool) *Array {
|
||||
// which is false in default.
|
||||
func NewArrayFrom(array []interface{}, safe ...bool) *Array {
|
||||
return &Array{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: array,
|
||||
}
|
||||
}
|
||||
@ -88,26 +93,31 @@ func NewArrayFromCopy(array []interface{}, safe ...bool) *Array {
|
||||
newArray := make([]interface{}, len(array))
|
||||
copy(newArray, array)
|
||||
return &Array{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *Array) Get(index int) interface{} {
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *Array) Get(index int) (value interface{}, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return nil, false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Set sets value to specified index.
|
||||
func (a *Array) Set(index int, value interface{}) *Array {
|
||||
func (a *Array) Set(index int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
a.array[index] = value
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
@ -153,45 +163,70 @@ func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
|
||||
}
|
||||
|
||||
// InsertBefore inserts the <value> to the front of <index>.
|
||||
func (a *Array) InsertBefore(index int, value interface{}) *Array {
|
||||
func (a *Array) InsertBefore(index int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
rear := append([]interface{}{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
func (a *Array) InsertAfter(index int, value interface{}) *Array {
|
||||
func (a *Array) InsertAfter(index int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
rear := append([]interface{}{}, a.array[index+1:]...)
|
||||
a.array = append(a.array[0:index+1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
func (a *Array) Remove(index int) interface{} {
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *Array) Remove(index int) (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// Determine array boundaries when deleting to improve deletion efficiency。
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *Array) doRemoveWithoutLock(index int) (value interface{}, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return nil, false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *Array) RemoveValue(value interface{}) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
a.Remove(i)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PushLeft pushes one or multiple items to the beginning of array.
|
||||
@ -212,52 +247,68 @@ func (a *Array) PushRight(value ...interface{}) *Array {
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
func (a *Array) PopRand() interface{} {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *Array) PopRand() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
func (a *Array) PopRands(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]interface{}, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *Array) PopLeft() interface{} {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *Array) PopLeft() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
value := a.array[0]
|
||||
if len(a.array) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
value = a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
func (a *Array) PopRight() interface{} {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *Array) PopRight() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
value := a.array[index]
|
||||
if index < 0 {
|
||||
return nil, false
|
||||
}
|
||||
value = a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
func (a *Array) PopLefts(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
length := len(a.array)
|
||||
if size > length {
|
||||
size = length
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[0:size]
|
||||
a.array = a.array[size:]
|
||||
@ -268,9 +319,14 @@ func (a *Array) PopLefts(size int) []interface{} {
|
||||
func (a *Array) PopRights(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
index := len(a.array) - size
|
||||
if index < 0 {
|
||||
index = 0
|
||||
if index <= 0 {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[index:]
|
||||
a.array = a.array[:index]
|
||||
@ -418,10 +474,11 @@ func (a *Array) Contains(value interface{}) bool {
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *Array) Search(value interface{}) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if v == value {
|
||||
@ -429,12 +486,11 @@ func (a *Array) Search(value interface{}) int {
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *Array) Unique() *Array {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
@ -469,32 +525,16 @@ func (a *Array) RLockFunc(f func(array []interface{})) *Array {
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *Array) Merge(array interface{}) *Array {
|
||||
switch v := array.(type) {
|
||||
case *Array:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *IntArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *StrArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedIntArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedStrArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
default:
|
||||
a.Append(gconv.Interfaces(array)...)
|
||||
}
|
||||
return a
|
||||
return a.Append(gconv.Interfaces(array)...)
|
||||
}
|
||||
|
||||
// Fill fills an array with num entries of the value <value>,
|
||||
// keys starting at the <startIndex> parameter.
|
||||
func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
|
||||
func (a *Array) Fill(startIndex int, num int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if startIndex < 0 {
|
||||
startIndex = 0
|
||||
if startIndex < 0 || startIndex > len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
|
||||
}
|
||||
for i := startIndex; i < startIndex+num; i++ {
|
||||
if i > len(a.array)-1 {
|
||||
@ -503,7 +543,7 @@ func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
|
||||
a.array[i] = value
|
||||
}
|
||||
}
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
@ -557,27 +597,27 @@ func (a *Array) Pad(size int, val interface{}) *Array {
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *Array) Rand() interface{} {
|
||||
func (a *Array) Rand() (value interface{}, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
if len(a.array) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
func (a *Array) Rands(size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
n := make([]interface{}, size)
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
n[i] = a.array[v]
|
||||
if i == size-1 {
|
||||
break
|
||||
}
|
||||
array := make([]interface{}, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i] = a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
return n
|
||||
return array
|
||||
}
|
||||
|
||||
// Shuffle randomly shuffles the array.
|
||||
@ -604,6 +644,9 @@ func (a *Array) Reverse() *Array {
|
||||
func (a *Array) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
@ -630,7 +673,7 @@ func (a *Array) Iterator(f func(k int, v interface{}) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
@ -642,7 +685,7 @@ func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *Array) IteratorDesc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
@ -677,7 +720,8 @@ func (a *Array) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *Array) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a Array) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -685,8 +729,7 @@ func (a *Array) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *Array) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.array == nil {
|
||||
a.array = make([]interface{}, 0)
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -696,3 +739,60 @@ func (a *Array) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *Array) UnmarshalValue(value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceAny(value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterNil removes all nil value of the array.
|
||||
func (a *Array) FilterNil() *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if empty.IsNil(a.array[i]) {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// FilterEmpty removes all empty value of the array.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (a *Array) FilterEmpty() *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if empty.IsEmpty(a.array[i]) {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *Array) Walk(f func(value interface{}) interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *Array) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ package garray
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
@ -18,8 +19,11 @@ import (
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// IntArray is a golang int array with rich features.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type IntArray struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
array []int
|
||||
}
|
||||
|
||||
@ -35,7 +39,7 @@ func NewIntArray(safe ...bool) *IntArray {
|
||||
// which is false in default.
|
||||
func NewIntArraySize(size int, cap int, safe ...bool) *IntArray {
|
||||
return &IntArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]int, size, cap),
|
||||
}
|
||||
}
|
||||
@ -60,7 +64,7 @@ func NewIntArrayRange(start, end, step int, safe ...bool) *IntArray {
|
||||
// which is false in default.
|
||||
func NewIntArrayFrom(array []int, safe ...bool) *IntArray {
|
||||
return &IntArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: array,
|
||||
}
|
||||
}
|
||||
@ -72,26 +76,31 @@ func NewIntArrayFromCopy(array []int, safe ...bool) *IntArray {
|
||||
newArray := make([]int, len(array))
|
||||
copy(newArray, array)
|
||||
return &IntArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *IntArray) Get(index int) int {
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *IntArray) Get(index int) (value int, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return 0, false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Set sets value to specified index.
|
||||
func (a *IntArray) Set(index int, value int) *IntArray {
|
||||
func (a *IntArray) Set(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
a.array[index] = value
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
@ -127,8 +136,7 @@ func (a *IntArray) Sum() (sum int) {
|
||||
}
|
||||
|
||||
// Sort sorts the array in increasing order.
|
||||
// The parameter <reverse> controls whether sort
|
||||
// in increasing order(default) or decreasing order
|
||||
// The parameter <reverse> controls whether sort in increasing order(default) or decreasing order.
|
||||
func (a *IntArray) Sort(reverse ...bool) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -156,45 +164,70 @@ func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
|
||||
}
|
||||
|
||||
// InsertBefore inserts the <value> to the front of <index>.
|
||||
func (a *IntArray) InsertBefore(index int, value int) *IntArray {
|
||||
func (a *IntArray) InsertBefore(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
rear := append([]int{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
func (a *IntArray) InsertAfter(index int, value int) *IntArray {
|
||||
func (a *IntArray) InsertAfter(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
rear := append([]int{}, a.array[index+1:]...)
|
||||
a.array = append(a.array[0:index+1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
func (a *IntArray) Remove(index int) int {
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *IntArray) Remove(index int) (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *IntArray) doRemoveWithoutLock(index int) (value int, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return 0, false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *IntArray) RemoveValue(value int) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
_, found := a.Remove(i)
|
||||
return found
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PushLeft pushes one or multiple items to the beginning of array.
|
||||
@ -215,52 +248,72 @@ func (a *IntArray) PushRight(value ...int) *IntArray {
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *IntArray) PopLeft() int {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *IntArray) PopLeft() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
value := a.array[0]
|
||||
if len(a.array) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
value = a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
func (a *IntArray) PopRight() int {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *IntArray) PopRight() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
value := a.array[index]
|
||||
if index < 0 {
|
||||
return 0, false
|
||||
}
|
||||
value = a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
func (a *IntArray) PopRand() int {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *IntArray) PopRand() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *IntArray) PopRands(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *IntArray) PopLefts(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
length := len(a.array)
|
||||
if size > length {
|
||||
size = length
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[0:size]
|
||||
a.array = a.array[size:]
|
||||
@ -268,12 +321,19 @@ func (a *IntArray) PopLefts(size int) []int {
|
||||
}
|
||||
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *IntArray) PopRights(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
index := len(a.array) - size
|
||||
if index < 0 {
|
||||
index = 0
|
||||
if index <= 0 {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[index:]
|
||||
a.array = a.array[:index]
|
||||
@ -430,10 +490,11 @@ func (a *IntArray) Contains(value int) bool {
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *IntArray) Search(value int) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if v == value {
|
||||
@ -441,12 +502,11 @@ func (a *IntArray) Search(value int) int {
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *IntArray) Unique() *IntArray {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
@ -481,32 +541,16 @@ func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *IntArray) Merge(array interface{}) *IntArray {
|
||||
switch v := array.(type) {
|
||||
case *Array:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *IntArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *StrArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedIntArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedStrArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
default:
|
||||
a.Append(gconv.Ints(array)...)
|
||||
}
|
||||
return a
|
||||
return a.Append(gconv.Ints(array)...)
|
||||
}
|
||||
|
||||
// Fill fills an array with num entries of the value <value>,
|
||||
// keys starting at the <startIndex> parameter.
|
||||
func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
|
||||
func (a *IntArray) Fill(startIndex int, num int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if startIndex < 0 {
|
||||
startIndex = 0
|
||||
if startIndex < 0 || startIndex > len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
|
||||
}
|
||||
for i := startIndex; i < startIndex+num; i++ {
|
||||
if i > len(a.array)-1 {
|
||||
@ -515,7 +559,7 @@ func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
|
||||
a.array[i] = value
|
||||
}
|
||||
}
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
@ -569,27 +613,27 @@ func (a *IntArray) Pad(size int, value int) *IntArray {
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *IntArray) Rand() int {
|
||||
func (a *IntArray) Rand() (value int, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
if len(a.array) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
func (a *IntArray) Rands(size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
n := make([]int, size)
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
n[i] = a.array[v]
|
||||
if i == size-1 {
|
||||
break
|
||||
}
|
||||
array := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i] = a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
return n
|
||||
return array
|
||||
}
|
||||
|
||||
// Shuffle randomly shuffles the array.
|
||||
@ -616,6 +660,9 @@ func (a *IntArray) Reverse() *IntArray {
|
||||
func (a *IntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
@ -642,7 +689,7 @@ func (a *IntArray) Iterator(f func(k int, v int) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
@ -654,7 +701,7 @@ func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *IntArray) IteratorDesc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
@ -672,7 +719,8 @@ func (a *IntArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *IntArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a IntArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -680,8 +728,7 @@ func (a *IntArray) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *IntArray) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.array == nil {
|
||||
a.array = make([]int, 0)
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -691,3 +738,45 @@ func (a *IntArray) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *IntArray) UnmarshalValue(value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceInt(value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterEmpty removes all zero value of the array.
|
||||
func (a *IntArray) FilterEmpty() *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if a.array[i] == 0 {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *IntArray) Walk(f func(value int) int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *IntArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
@ -9,6 +9,8 @@ package garray
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"math"
|
||||
"sort"
|
||||
@ -19,8 +21,11 @@ import (
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// StrArray is a golang string array with rich features.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type StrArray struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
array []string
|
||||
}
|
||||
|
||||
@ -36,7 +41,7 @@ func NewStrArray(safe ...bool) *StrArray {
|
||||
// which is false in default.
|
||||
func NewStrArraySize(size int, cap int, safe ...bool) *StrArray {
|
||||
return &StrArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]string, size, cap),
|
||||
}
|
||||
}
|
||||
@ -46,7 +51,7 @@ func NewStrArraySize(size int, cap int, safe ...bool) *StrArray {
|
||||
// which is false in default.
|
||||
func NewStrArrayFrom(array []string, safe ...bool) *StrArray {
|
||||
return &StrArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: array,
|
||||
}
|
||||
}
|
||||
@ -58,26 +63,31 @@ func NewStrArrayFromCopy(array []string, safe ...bool) *StrArray {
|
||||
newArray := make([]string, len(array))
|
||||
copy(newArray, array)
|
||||
return &StrArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *StrArray) Get(index int) string {
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *StrArray) Get(index int) (value string, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return "", false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Set sets value to specified index.
|
||||
func (a *StrArray) Set(index int, value string) *StrArray {
|
||||
func (a *StrArray) Set(index int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
a.array[index] = value
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
@ -142,45 +152,70 @@ func (a *StrArray) SortFunc(less func(v1, v2 string) bool) *StrArray {
|
||||
}
|
||||
|
||||
// InsertBefore inserts the <value> to the front of <index>.
|
||||
func (a *StrArray) InsertBefore(index int, value string) *StrArray {
|
||||
func (a *StrArray) InsertBefore(index int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
rear := append([]string{}, a.array[index:]...)
|
||||
a.array = append(a.array[0:index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
func (a *StrArray) InsertAfter(index int, value string) *StrArray {
|
||||
func (a *StrArray) InsertAfter(index int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", index, len(a.array)))
|
||||
}
|
||||
rear := append([]string{}, a.array[index+1:]...)
|
||||
a.array = append(a.array[0:index+1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
func (a *StrArray) Remove(index int) string {
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *StrArray) Remove(index int) (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// Determine array boundaries when deleting to improve deletion efficiency。
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *StrArray) doRemoveWithoutLock(index int) (value string, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return "", false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *StrArray) RemoveValue(value string) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
_, found := a.Remove(i)
|
||||
return found
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PushLeft pushes one or multiple items to the beginning of array.
|
||||
@ -201,52 +236,72 @@ func (a *StrArray) PushRight(value ...string) *StrArray {
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *StrArray) PopLeft() string {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *StrArray) PopLeft() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
value := a.array[0]
|
||||
if len(a.array) == 0 {
|
||||
return "", false
|
||||
}
|
||||
value = a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
func (a *StrArray) PopRight() string {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *StrArray) PopRight() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
value := a.array[index]
|
||||
if index < 0 {
|
||||
return "", false
|
||||
}
|
||||
value = a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
func (a *StrArray) PopRand() string {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *StrArray) PopRand() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *StrArray) PopRands(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]string, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *StrArray) PopLefts(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
length := len(a.array)
|
||||
if size > length {
|
||||
size = length
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[0:size]
|
||||
a.array = a.array[size:]
|
||||
@ -254,12 +309,19 @@ func (a *StrArray) PopLefts(size int) []string {
|
||||
}
|
||||
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *StrArray) PopRights(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
index := len(a.array) - size
|
||||
if index < 0 {
|
||||
index = 0
|
||||
if index <= 0 {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[index:]
|
||||
a.array = a.array[:index]
|
||||
@ -413,13 +475,30 @@ func (a *StrArray) Contains(value string) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// ContainsI checks whether a value exists in the array with case-insensitively.
|
||||
// Note that it internally iterates the whole array to do the comparison with case-insensitively.
|
||||
func (a *StrArray) ContainsI(value string) bool {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, v := range a.array {
|
||||
if strings.EqualFold(v, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *StrArray) Search(value string) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if strings.Compare(v, value) == 0 {
|
||||
@ -427,11 +506,11 @@ func (a *StrArray) Search(value string) int {
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *StrArray) Unique() *StrArray {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
@ -466,32 +545,16 @@ func (a *StrArray) RLockFunc(f func(array []string)) *StrArray {
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *StrArray) Merge(array interface{}) *StrArray {
|
||||
switch v := array.(type) {
|
||||
case *Array:
|
||||
a.Append(gconv.Strings(v.Slice())...)
|
||||
case *IntArray:
|
||||
a.Append(gconv.Strings(v.Slice())...)
|
||||
case *StrArray:
|
||||
a.Append(gconv.Strings(v.Slice())...)
|
||||
case *SortedArray:
|
||||
a.Append(gconv.Strings(v.Slice())...)
|
||||
case *SortedIntArray:
|
||||
a.Append(gconv.Strings(v.Slice())...)
|
||||
case *SortedStrArray:
|
||||
a.Append(gconv.Strings(v.Slice())...)
|
||||
default:
|
||||
a.Append(gconv.Strings(array)...)
|
||||
}
|
||||
return a
|
||||
return a.Append(gconv.Strings(array)...)
|
||||
}
|
||||
|
||||
// Fill fills an array with num entries of the value <value>,
|
||||
// keys starting at the <startIndex> parameter.
|
||||
func (a *StrArray) Fill(startIndex int, num int, value string) *StrArray {
|
||||
func (a *StrArray) Fill(startIndex int, num int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if startIndex < 0 {
|
||||
startIndex = 0
|
||||
if startIndex < 0 || startIndex > len(a.array) {
|
||||
return errors.New(fmt.Sprintf("index %d out of array range %d", startIndex, len(a.array)))
|
||||
}
|
||||
for i := startIndex; i < startIndex+num; i++ {
|
||||
if i > len(a.array)-1 {
|
||||
@ -500,7 +563,7 @@ func (a *StrArray) Fill(startIndex int, num int, value string) *StrArray {
|
||||
a.array[i] = value
|
||||
}
|
||||
}
|
||||
return a
|
||||
return nil
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
@ -554,27 +617,27 @@ func (a *StrArray) Pad(size int, value string) *StrArray {
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *StrArray) Rand() string {
|
||||
func (a *StrArray) Rand() (value string, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
if len(a.array) == 0 {
|
||||
return "", false
|
||||
}
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
func (a *StrArray) Rands(size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
n := make([]string, size)
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
n[i] = a.array[v]
|
||||
if i == size-1 {
|
||||
break
|
||||
}
|
||||
array := make([]string, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i] = a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
return n
|
||||
return array
|
||||
}
|
||||
|
||||
// Shuffle randomly shuffles the array.
|
||||
@ -601,6 +664,9 @@ func (a *StrArray) Reverse() *StrArray {
|
||||
func (a *StrArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(v)
|
||||
@ -627,7 +693,7 @@ func (a *StrArray) Iterator(f func(k int, v string) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *StrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
@ -639,7 +705,7 @@ func (a *StrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *StrArray) IteratorDesc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
@ -668,7 +734,8 @@ func (a *StrArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *StrArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a StrArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -676,8 +743,7 @@ func (a *StrArray) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *StrArray) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.array == nil {
|
||||
a.array = make([]string, 0)
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -687,3 +753,45 @@ func (a *StrArray) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *StrArray) UnmarshalValue(value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceStr(value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterEmpty removes all empty string value of the array.
|
||||
func (a *StrArray) FilterEmpty() *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if a.array[i] == "" {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *StrArray) Walk(f func(value string) string) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *StrArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
@ -10,31 +10,35 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// It's using increasing order in default.
|
||||
// SortedArray is a golang sorted array with rich features.
|
||||
// It is using increasing order in default, which can be changed by
|
||||
// setting it a custom comparator.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type SortedArray struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
array []interface{}
|
||||
unique *gtype.Bool // Whether enable unique feature(false)
|
||||
unique bool // Whether enable unique feature(false)
|
||||
comparator func(a, b interface{}) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
|
||||
}
|
||||
|
||||
// NewSortedArray creates and returns an empty sorted array.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety, which is false in default.
|
||||
// The parameter <comparator> used to compare values to sort in array,
|
||||
// if it returns value < 0, means v1 < v2;
|
||||
// if it returns value = 0, means v1 = v2;
|
||||
// if it returns value > 0, means v1 > v2;
|
||||
// if it returns value < 0, means v1 < v2; the v1 will be inserted before v2;
|
||||
// if it returns value = 0, means v1 = v2; the v1 will be replaced by v2;
|
||||
// if it returns value > 0, means v1 > v2; the v1 will be inserted after v2;
|
||||
func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
|
||||
return NewSortedArraySize(0, comparator, safe...)
|
||||
}
|
||||
@ -44,8 +48,7 @@ func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *Sorted
|
||||
// which is false in default.
|
||||
func NewSortedArraySize(cap int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
|
||||
return &SortedArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
unique: gtype.NewBool(),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]interface{}, 0, cap),
|
||||
comparator: comparator,
|
||||
}
|
||||
@ -73,7 +76,7 @@ func NewSortedArrayFrom(array []interface{}, comparator func(a, b interface{}) i
|
||||
a := NewSortedArraySize(0, comparator, safe...)
|
||||
a.array = array
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
@ -93,19 +96,19 @@ func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// SetComparator sets/changes the comparator for sorting.
|
||||
// It resorts the array as the comparator is changed.
|
||||
func (a *SortedArray) SetComparator(comparator func(a, b interface{}) int) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.comparator = comparator
|
||||
// Resort the array if comparator is changed.
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
}
|
||||
|
||||
@ -116,13 +119,19 @@ func (a *SortedArray) Sort() *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// Add adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
// It's alias of function Append, see Append.
|
||||
func (a *SortedArray) Add(values ...interface{}) *SortedArray {
|
||||
return a.Append(values...)
|
||||
}
|
||||
|
||||
// Append adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
func (a *SortedArray) Append(values ...interface{}) *SortedArray {
|
||||
if len(values) == 0 {
|
||||
return a
|
||||
}
|
||||
@ -130,7 +139,7 @@ func (a *SortedArray) Add(values ...interface{}) *SortedArray {
|
||||
defer a.mu.Unlock()
|
||||
for _, value := range values {
|
||||
index, cmp := a.binSearch(value, false)
|
||||
if a.unique.Val() && cmp == 0 {
|
||||
if a.unique && cmp == 0 {
|
||||
continue
|
||||
}
|
||||
if index < 0 {
|
||||
@ -147,73 +156,106 @@ func (a *SortedArray) Add(values ...interface{}) *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *SortedArray) Get(index int) interface{} {
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *SortedArray) Get(index int) (value interface{}, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return nil, false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
func (a *SortedArray) Remove(index int) interface{} {
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *SortedArray) Remove(index int) (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *SortedArray) doRemoveWithoutLock(index int) (value interface{}, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return nil, false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *SortedArray) RemoveValue(value interface{}) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
a.Remove(i)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *SortedArray) PopLeft() interface{} {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *SortedArray) PopLeft() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
value := a.array[0]
|
||||
if len(a.array) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
value = a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
func (a *SortedArray) PopRight() interface{} {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *SortedArray) PopRight() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
value := a.array[index]
|
||||
if index < 0 {
|
||||
return nil, false
|
||||
}
|
||||
value = a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
func (a *SortedArray) PopRand() interface{} {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *SortedArray) PopRand() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
func (a *SortedArray) PopRands(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]interface{}, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
@ -222,9 +264,13 @@ func (a *SortedArray) PopRands(size int) []interface{} {
|
||||
func (a *SortedArray) PopLefts(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
length := len(a.array)
|
||||
if size > length {
|
||||
size = length
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[0:size]
|
||||
a.array = a.array[size:]
|
||||
@ -235,9 +281,14 @@ func (a *SortedArray) PopLefts(size int) []interface{} {
|
||||
func (a *SortedArray) PopRights(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
index := len(a.array) - size
|
||||
if index < 0 {
|
||||
index = 0
|
||||
if index <= 0 {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[index:]
|
||||
a.array = a.array[:index]
|
||||
@ -346,7 +397,7 @@ func (a *SortedArray) Len() int {
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (a *SortedArray) Slice() []interface{} {
|
||||
array := ([]interface{})(nil)
|
||||
var array []interface{}
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -383,20 +434,20 @@ func (a *SortedArray) Search(value interface{}) (index int) {
|
||||
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
|
||||
// If <result> greater than 0, it means the value at <index> is greater than <value>.
|
||||
func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
}
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
min := 0
|
||||
max := len(a.array) - 1
|
||||
mid := 0
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = int((min + max) / 2)
|
||||
cmp = a.comparator(value, a.array[mid])
|
||||
mid = (min + max) / 2
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
max = mid - 1
|
||||
@ -413,8 +464,8 @@ func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result
|
||||
// which means it does not contain any repeated items.
|
||||
// It also do unique check, remove all repeated items.
|
||||
func (a *SortedArray) SetUnique(unique bool) *SortedArray {
|
||||
oldUnique := a.unique.Val()
|
||||
a.unique.Set(unique)
|
||||
oldUnique := a.unique
|
||||
a.unique = unique
|
||||
if unique && oldUnique != unique {
|
||||
a.Unique()
|
||||
}
|
||||
@ -425,12 +476,15 @@ func (a *SortedArray) SetUnique(unique bool) *SortedArray {
|
||||
func (a *SortedArray) Unique() *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return a
|
||||
}
|
||||
i := 0
|
||||
for {
|
||||
if i == len(a.array)-1 {
|
||||
break
|
||||
}
|
||||
if a.comparator(a.array[i], a.array[i+1]) == 0 {
|
||||
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
|
||||
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
|
||||
} else {
|
||||
i++
|
||||
@ -462,6 +516,12 @@ func (a *SortedArray) Clear() *SortedArray {
|
||||
func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
// Keep the array always sorted.
|
||||
defer sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
@ -479,23 +539,7 @@ func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedArray) Merge(array interface{}) *SortedArray {
|
||||
switch v := array.(type) {
|
||||
case *Array:
|
||||
a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *IntArray:
|
||||
a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *StrArray:
|
||||
a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedArray:
|
||||
a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedIntArray:
|
||||
a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedStrArray:
|
||||
a.Add(gconv.Interfaces(v.Slice())...)
|
||||
default:
|
||||
a.Add(gconv.Interfaces(array)...)
|
||||
}
|
||||
return a
|
||||
return a.Add(gconv.Interfaces(array)...)
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
@ -522,33 +566,36 @@ func (a *SortedArray) Chunk(size int) [][]interface{} {
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *SortedArray) Rand() interface{} {
|
||||
func (a *SortedArray) Rand() (value interface{}, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
if len(a.array) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
func (a *SortedArray) Rands(size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
n := make([]interface{}, size)
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
n[i] = a.array[v]
|
||||
if i == size-1 {
|
||||
break
|
||||
}
|
||||
array := make([]interface{}, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i] = a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
return n
|
||||
return array
|
||||
}
|
||||
|
||||
// Join joins array elements with a string <glue>.
|
||||
func (a *SortedArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
@ -575,7 +622,7 @@ func (a *SortedArray) Iterator(f func(k int, v interface{}) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
@ -587,7 +634,7 @@ func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedArray) IteratorDesc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
@ -622,19 +669,18 @@ func (a *SortedArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *SortedArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a SortedArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
// Note that the comparator is set as string comparator in default.
|
||||
func (a *SortedArray) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.comparator == nil {
|
||||
a.array = make([]interface{}, 0)
|
||||
a.unique = gtype.NewBool()
|
||||
// Note that the comparator is string comparator in default.
|
||||
a.comparator = gutil.ComparatorString
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -642,10 +688,103 @@ func (a *SortedArray) UnmarshalJSON(b []byte) error {
|
||||
if err := json.Unmarshal(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
if a.comparator != nil {
|
||||
if a.comparator != nil && a.array != nil {
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
// Note that the comparator is set as string comparator in default.
|
||||
func (a *SortedArray) UnmarshalValue(value interface{}) (err error) {
|
||||
if a.comparator == nil {
|
||||
a.comparator = gutil.ComparatorString
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.Unmarshal(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceAny(value)
|
||||
}
|
||||
if a.comparator != nil && a.array != nil {
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// FilterNil removes all nil value of the array.
|
||||
func (a *SortedArray) FilterNil() *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if empty.IsNil(a.array[i]) {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
for i := len(a.array) - 1; i >= 0; {
|
||||
if empty.IsNil(a.array[i]) {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// FilterEmpty removes all empty value of the array.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (a *SortedArray) FilterEmpty() *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if empty.IsEmpty(a.array[i]) {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
for i := len(a.array) - 1; i >= 0; {
|
||||
if empty.IsEmpty(a.array[i]) {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *SortedArray) Walk(f func(value interface{}) interface{}) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// Keep the array always sorted.
|
||||
defer sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *SortedArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
// or else it panics.
|
||||
func (a *SortedArray) getComparator() func(a, b interface{}) int {
|
||||
if a.comparator == nil {
|
||||
panic("comparator is missing for sorted array")
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
|
||||
@ -13,17 +13,20 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// It's using increasing order in default.
|
||||
// SortedIntArray is a golang sorted int array with rich features.
|
||||
// It is using increasing order in default, which can be changed by
|
||||
// setting it a custom comparator.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type SortedIntArray struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
array []int
|
||||
unique *gtype.Bool // Whether enable unique feature(false)
|
||||
unique bool // Whether enable unique feature(false)
|
||||
comparator func(a, b int) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
|
||||
}
|
||||
|
||||
@ -47,9 +50,8 @@ func NewSortedIntArrayComparator(comparator func(a, b int) int, safe ...bool) *S
|
||||
// which is false in default.
|
||||
func NewSortedIntArraySize(cap int, safe ...bool) *SortedIntArray {
|
||||
return &SortedIntArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]int, 0, cap),
|
||||
unique: gtype.NewBool(),
|
||||
comparator: defaultComparatorInt,
|
||||
}
|
||||
}
|
||||
@ -93,7 +95,7 @@ func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
sort.Ints(a.array)
|
||||
quickSortInt(a.array, a.getComparator())
|
||||
return a
|
||||
}
|
||||
|
||||
@ -103,12 +105,18 @@ func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
|
||||
func (a *SortedIntArray) Sort() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Ints(a.array)
|
||||
quickSortInt(a.array, a.getComparator())
|
||||
return a
|
||||
}
|
||||
|
||||
// Add adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
// It's alias of function Append, see Append.
|
||||
func (a *SortedIntArray) Add(values ...int) *SortedIntArray {
|
||||
return a.Append(values...)
|
||||
}
|
||||
|
||||
// Append adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
func (a *SortedIntArray) Append(values ...int) *SortedIntArray {
|
||||
if len(values) == 0 {
|
||||
return a
|
||||
}
|
||||
@ -116,7 +124,7 @@ func (a *SortedIntArray) Add(values ...int) *SortedIntArray {
|
||||
defer a.mu.Unlock()
|
||||
for _, value := range values {
|
||||
index, cmp := a.binSearch(value, false)
|
||||
if a.unique.Val() && cmp == 0 {
|
||||
if a.unique && cmp == 0 {
|
||||
continue
|
||||
}
|
||||
if index < 0 {
|
||||
@ -133,84 +141,125 @@ func (a *SortedIntArray) Add(values ...int) *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *SortedIntArray) Get(index int) int {
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *SortedIntArray) Get(index int) (value int, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return 0, false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
func (a *SortedIntArray) Remove(index int) int {
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *SortedIntArray) Remove(index int) (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *SortedIntArray) doRemoveWithoutLock(index int) (value int, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return 0, false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *SortedIntArray) RemoveValue(value int) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
_, found := a.Remove(i)
|
||||
return found
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *SortedIntArray) PopLeft() int {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *SortedIntArray) PopLeft() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
value := a.array[0]
|
||||
if len(a.array) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
value = a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
func (a *SortedIntArray) PopRight() int {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *SortedIntArray) PopRight() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
value := a.array[index]
|
||||
if index < 0 {
|
||||
return 0, false
|
||||
}
|
||||
value = a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
func (a *SortedIntArray) PopRand() int {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *SortedIntArray) PopRand() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedIntArray) PopRands(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedIntArray) PopLefts(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
length := len(a.array)
|
||||
if size > length {
|
||||
size = length
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[0:size]
|
||||
a.array = a.array[size:]
|
||||
@ -218,12 +267,19 @@ func (a *SortedIntArray) PopLefts(size int) []int {
|
||||
}
|
||||
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedIntArray) PopRights(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
index := len(a.array) - size
|
||||
if index < 0 {
|
||||
index = 0
|
||||
if index <= 0 {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[index:]
|
||||
a.array = a.array[:index]
|
||||
@ -375,20 +431,20 @@ func (a *SortedIntArray) Search(value int) (index int) {
|
||||
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
|
||||
// If <result> greater than 0, it means the value at <index> is greater than <value>.
|
||||
func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
}
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
min := 0
|
||||
max := len(a.array) - 1
|
||||
mid := 0
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = int((min + max) / 2)
|
||||
cmp = a.comparator(value, a.array[mid])
|
||||
mid = (min + max) / 2
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
max = mid - 1
|
||||
@ -405,8 +461,8 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int)
|
||||
// which means it does not contain any repeated items.
|
||||
// It also do unique check, remove all repeated items.
|
||||
func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
|
||||
oldUnique := a.unique.Val()
|
||||
a.unique.Set(unique)
|
||||
oldUnique := a.unique
|
||||
a.unique = unique
|
||||
if unique && oldUnique != unique {
|
||||
a.Unique()
|
||||
}
|
||||
@ -416,18 +472,21 @@ func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
|
||||
// Unique uniques the array, clear repeated items.
|
||||
func (a *SortedIntArray) Unique() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return a
|
||||
}
|
||||
i := 0
|
||||
for {
|
||||
if i == len(a.array)-1 {
|
||||
break
|
||||
}
|
||||
if a.comparator(a.array[i], a.array[i+1]) == 0 {
|
||||
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
|
||||
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
@ -471,23 +530,7 @@ func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
|
||||
switch v := array.(type) {
|
||||
case *Array:
|
||||
a.Add(gconv.Ints(v.Slice())...)
|
||||
case *IntArray:
|
||||
a.Add(gconv.Ints(v.Slice())...)
|
||||
case *StrArray:
|
||||
a.Add(gconv.Ints(v.Slice())...)
|
||||
case *SortedArray:
|
||||
a.Add(gconv.Ints(v.Slice())...)
|
||||
case *SortedIntArray:
|
||||
a.Add(gconv.Ints(v.Slice())...)
|
||||
case *SortedStrArray:
|
||||
a.Add(gconv.Ints(v.Slice())...)
|
||||
default:
|
||||
a.Add(gconv.Ints(array)...)
|
||||
}
|
||||
return a
|
||||
return a.Add(gconv.Ints(array)...)
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
@ -514,33 +557,36 @@ func (a *SortedIntArray) Chunk(size int) [][]int {
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *SortedIntArray) Rand() int {
|
||||
func (a *SortedIntArray) Rand() (value int, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
if len(a.array) == 0 {
|
||||
return 0, false
|
||||
}
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
func (a *SortedIntArray) Rands(size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
n := make([]int, size)
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
n[i] = a.array[v]
|
||||
if i == size-1 {
|
||||
break
|
||||
}
|
||||
array := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i] = a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
return n
|
||||
return array
|
||||
}
|
||||
|
||||
// Join joins array elements with a string <glue>.
|
||||
func (a *SortedIntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
@ -567,7 +613,7 @@ func (a *SortedIntArray) Iterator(f func(k int, v int) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
@ -579,7 +625,7 @@ func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedIntArray) IteratorDesc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
@ -597,7 +643,8 @@ func (a *SortedIntArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *SortedIntArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a SortedIntArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -605,10 +652,8 @@ func (a *SortedIntArray) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *SortedIntArray) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.comparator == nil {
|
||||
a.array = make([]int, 0)
|
||||
a.unique = gtype.NewBool()
|
||||
a.comparator = defaultComparatorInt
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -616,6 +661,76 @@ func (a *SortedIntArray) UnmarshalJSON(b []byte) error {
|
||||
if err := json.Unmarshal(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
sort.Ints(a.array)
|
||||
if a.array != nil {
|
||||
sort.Ints(a.array)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *SortedIntArray) UnmarshalValue(value interface{}) (err error) {
|
||||
if a.comparator == nil {
|
||||
a.comparator = defaultComparatorInt
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.Unmarshal(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceInt(value)
|
||||
}
|
||||
if a.array != nil {
|
||||
sort.Ints(a.array)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// FilterEmpty removes all zero value of the array.
|
||||
func (a *SortedIntArray) FilterEmpty() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if a.array[i] == 0 {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
for i := len(a.array) - 1; i >= 0; {
|
||||
if a.array[i] == 0 {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *SortedIntArray) Walk(f func(value int) int) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
// Keep the array always sorted.
|
||||
defer quickSortInt(a.array, a.getComparator())
|
||||
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *SortedIntArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
// or else it returns a default comparator.
|
||||
func (a *SortedIntArray) getComparator() func(a, b int) int {
|
||||
if a.comparator == nil {
|
||||
return defaultComparatorInt
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
|
||||
@ -12,18 +12,22 @@ import (
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// It's using increasing order in default.
|
||||
// SortedStrArray is a golang sorted string array with rich features.
|
||||
// It is using increasing order in default, which can be changed by
|
||||
// setting it a custom comparator.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type SortedStrArray struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
array []string
|
||||
unique *gtype.Bool // Whether enable unique feature(false)
|
||||
unique bool // Whether enable unique feature(false)
|
||||
comparator func(a, b string) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
|
||||
}
|
||||
|
||||
@ -47,9 +51,8 @@ func NewSortedStrArrayComparator(comparator func(a, b string) int, safe ...bool)
|
||||
// which is false in default.
|
||||
func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray {
|
||||
return &SortedStrArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]string, 0, cap),
|
||||
unique: gtype.NewBool(),
|
||||
comparator: defaultComparatorStr,
|
||||
}
|
||||
}
|
||||
@ -60,7 +63,7 @@ func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray {
|
||||
func NewSortedStrArrayFrom(array []string, safe ...bool) *SortedStrArray {
|
||||
a := NewSortedStrArraySize(0, safe...)
|
||||
a.array = array
|
||||
quickSortStr(a.array, a.comparator)
|
||||
quickSortStr(a.array, a.getComparator())
|
||||
return a
|
||||
}
|
||||
|
||||
@ -78,7 +81,7 @@ func (a *SortedStrArray) SetArray(array []string) *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
quickSortStr(a.array, a.comparator)
|
||||
quickSortStr(a.array, a.getComparator())
|
||||
return a
|
||||
}
|
||||
|
||||
@ -88,12 +91,18 @@ func (a *SortedStrArray) SetArray(array []string) *SortedStrArray {
|
||||
func (a *SortedStrArray) Sort() *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
quickSortStr(a.array, a.comparator)
|
||||
quickSortStr(a.array, a.getComparator())
|
||||
return a
|
||||
}
|
||||
|
||||
// Add adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
// It's alias of function Append, see Append.
|
||||
func (a *SortedStrArray) Add(values ...string) *SortedStrArray {
|
||||
return a.Append(values...)
|
||||
}
|
||||
|
||||
// Append adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
func (a *SortedStrArray) Append(values ...string) *SortedStrArray {
|
||||
if len(values) == 0 {
|
||||
return a
|
||||
}
|
||||
@ -101,7 +110,7 @@ func (a *SortedStrArray) Add(values ...string) *SortedStrArray {
|
||||
defer a.mu.Unlock()
|
||||
for _, value := range values {
|
||||
index, cmp := a.binSearch(value, false)
|
||||
if a.unique.Val() && cmp == 0 {
|
||||
if a.unique && cmp == 0 {
|
||||
continue
|
||||
}
|
||||
if index < 0 {
|
||||
@ -118,84 +127,125 @@ func (a *SortedStrArray) Add(values ...string) *SortedStrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Get returns the value of the specified index,
|
||||
// the caller should notice the boundary of the array.
|
||||
func (a *SortedStrArray) Get(index int) string {
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *SortedStrArray) Get(index int) (value string, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return "", false
|
||||
}
|
||||
return a.array[index], true
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
func (a *SortedStrArray) Remove(index int) string {
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
func (a *SortedStrArray) Remove(index int) (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(index)
|
||||
}
|
||||
|
||||
// doRemoveWithoutLock removes an item by index without lock.
|
||||
func (a *SortedStrArray) doRemoveWithoutLock(index int) (value string, found bool) {
|
||||
if index < 0 || index >= len(a.array) {
|
||||
return "", false
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
} else if index == len(a.array)-1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
value := a.array[index]
|
||||
value = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *SortedStrArray) RemoveValue(value string) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
a.Remove(i)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
func (a *SortedStrArray) PopLeft() string {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *SortedStrArray) PopLeft() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
value := a.array[0]
|
||||
if len(a.array) == 0 {
|
||||
return "", false
|
||||
}
|
||||
value = a.array[0]
|
||||
a.array = a.array[1:]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
func (a *SortedStrArray) PopRight() string {
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *SortedStrArray) PopRight() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
value := a.array[index]
|
||||
if index < 0 {
|
||||
return "", false
|
||||
}
|
||||
value = a.array[index]
|
||||
a.array = a.array[:index]
|
||||
return value
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
func (a *SortedStrArray) PopRand() string {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
func (a *SortedStrArray) PopRand() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedStrArray) PopRands(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]string, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[:index], a.array[index+1:]...)
|
||||
array[i], _ = a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedStrArray) PopLefts(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
length := len(a.array)
|
||||
if size > length {
|
||||
size = length
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
if size >= len(a.array) {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[0:size]
|
||||
a.array = a.array[size:]
|
||||
@ -203,12 +253,19 @@ func (a *SortedStrArray) PopLefts(size int) []string {
|
||||
}
|
||||
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedStrArray) PopRights(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
index := len(a.array) - size
|
||||
if index < 0 {
|
||||
index = 0
|
||||
if index <= 0 {
|
||||
array := a.array
|
||||
a.array = a.array[:0]
|
||||
return array
|
||||
}
|
||||
value := a.array[index:]
|
||||
a.array = a.array[:index]
|
||||
@ -345,6 +402,22 @@ func (a *SortedStrArray) Contains(value string) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// ContainsI checks whether a value exists in the array with case-insensitively.
|
||||
// Note that it internally iterates the whole array to do the comparison with case-insensitively.
|
||||
func (a *SortedStrArray) ContainsI(value string) bool {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, v := range a.array {
|
||||
if strings.EqualFold(v, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *SortedStrArray) Search(value string) (index int) {
|
||||
@ -360,20 +433,20 @@ func (a *SortedStrArray) Search(value string) (index int) {
|
||||
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
|
||||
// If <result> greater than 0, it means the value at <index> is greater than <value>.
|
||||
func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
}
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
min := 0
|
||||
max := len(a.array) - 1
|
||||
mid := 0
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = int((min + max) / 2)
|
||||
cmp = a.comparator(value, a.array[mid])
|
||||
mid = (min + max) / 2
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
max = mid - 1
|
||||
@ -390,8 +463,8 @@ func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result i
|
||||
// which means it does not contain any repeated items.
|
||||
// It also do unique check, remove all repeated items.
|
||||
func (a *SortedStrArray) SetUnique(unique bool) *SortedStrArray {
|
||||
oldUnique := a.unique.Val()
|
||||
a.unique.Set(unique)
|
||||
oldUnique := a.unique
|
||||
a.unique = unique
|
||||
if unique && oldUnique != unique {
|
||||
a.Unique()
|
||||
}
|
||||
@ -401,18 +474,21 @@ func (a *SortedStrArray) SetUnique(unique bool) *SortedStrArray {
|
||||
// Unique uniques the array, clear repeated items.
|
||||
func (a *SortedStrArray) Unique() *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(a.array) == 0 {
|
||||
return a
|
||||
}
|
||||
i := 0
|
||||
for {
|
||||
if i == len(a.array)-1 {
|
||||
break
|
||||
}
|
||||
if a.comparator(a.array[i], a.array[i+1]) == 0 {
|
||||
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
|
||||
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
@ -456,23 +532,7 @@ func (a *SortedStrArray) RLockFunc(f func(array []string)) *SortedStrArray {
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedStrArray) Merge(array interface{}) *SortedStrArray {
|
||||
switch v := array.(type) {
|
||||
case *Array:
|
||||
a.Add(gconv.Strings(v.Slice())...)
|
||||
case *IntArray:
|
||||
a.Add(gconv.Strings(v.Slice())...)
|
||||
case *StrArray:
|
||||
a.Add(gconv.Strings(v.Slice())...)
|
||||
case *SortedArray:
|
||||
a.Add(gconv.Strings(v.Slice())...)
|
||||
case *SortedIntArray:
|
||||
a.Add(gconv.Strings(v.Slice())...)
|
||||
case *SortedStrArray:
|
||||
a.Add(gconv.Strings(v.Slice())...)
|
||||
default:
|
||||
a.Add(gconv.Strings(array)...)
|
||||
}
|
||||
return a
|
||||
return a.Add(gconv.Strings(array)...)
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
@ -499,33 +559,36 @@ func (a *SortedStrArray) Chunk(size int) [][]string {
|
||||
}
|
||||
|
||||
// Rand randomly returns one item from array(no deleting).
|
||||
func (a *SortedStrArray) Rand() string {
|
||||
func (a *SortedStrArray) Rand() (value string, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
if len(a.array) == 0 {
|
||||
return "", false
|
||||
}
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
func (a *SortedStrArray) Rands(size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
if size <= 0 || len(a.array) == 0 {
|
||||
return nil
|
||||
}
|
||||
n := make([]string, size)
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
n[i] = a.array[v]
|
||||
if i == size-1 {
|
||||
break
|
||||
}
|
||||
array := make([]string, size)
|
||||
for i := 0; i < size; i++ {
|
||||
array[i] = a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
return n
|
||||
return array
|
||||
}
|
||||
|
||||
// Join joins array elements with a string <glue>.
|
||||
func (a *SortedStrArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(v)
|
||||
@ -552,7 +615,7 @@ func (a *SortedStrArray) Iterator(f func(k int, v string) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
@ -564,7 +627,7 @@ func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedStrArray) IteratorDesc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
@ -593,7 +656,8 @@ func (a *SortedStrArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *SortedStrArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a SortedStrArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -601,10 +665,8 @@ func (a *SortedStrArray) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *SortedStrArray) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.comparator == nil {
|
||||
a.array = make([]string, 0)
|
||||
a.unique = gtype.NewBool()
|
||||
a.comparator = defaultComparatorStr
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -612,6 +674,76 @@ func (a *SortedStrArray) UnmarshalJSON(b []byte) error {
|
||||
if err := json.Unmarshal(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
sort.Strings(a.array)
|
||||
if a.array != nil {
|
||||
sort.Strings(a.array)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *SortedStrArray) UnmarshalValue(value interface{}) (err error) {
|
||||
if a.comparator == nil {
|
||||
a.comparator = defaultComparatorStr
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.Unmarshal(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceStr(value)
|
||||
}
|
||||
if a.array != nil {
|
||||
sort.Strings(a.array)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// FilterEmpty removes all empty string value of the array.
|
||||
func (a *SortedStrArray) FilterEmpty() *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i := 0; i < len(a.array); {
|
||||
if a.array[i] == "" {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
for i := len(a.array) - 1; i >= 0; {
|
||||
if a.array[i] == "" {
|
||||
a.array = append(a.array[:i], a.array[i+1:]...)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *SortedStrArray) Walk(f func(value string) string) *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
// Keep the array always sorted.
|
||||
defer quickSortStr(a.array, a.getComparator())
|
||||
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *SortedStrArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
// or else it returns a default comparator.
|
||||
func (a *SortedStrArray) getComparator() func(a, b string) int {
|
||||
if a.comparator == nil {
|
||||
return defaultComparatorStr
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
|
||||
@ -8,51 +8,52 @@ package garray_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
)
|
||||
|
||||
func Example_basic() {
|
||||
// 创建普通的数组,默认并发安全(带锁)
|
||||
a := garray.New(true)
|
||||
// A normal array.
|
||||
a := garray.New()
|
||||
|
||||
// 添加数据项
|
||||
// Adding items.
|
||||
for i := 0; i < 10; i++ {
|
||||
a.Append(i)
|
||||
}
|
||||
|
||||
// 获取当前数组长度
|
||||
// Print the array length.
|
||||
fmt.Println(a.Len())
|
||||
|
||||
// 获取当前数据项列表
|
||||
// Print the array items.
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 获取指定索引项
|
||||
// Retrieve item by index.
|
||||
fmt.Println(a.Get(6))
|
||||
|
||||
// 查找指定数据项是否存在
|
||||
// Check item existence.
|
||||
fmt.Println(a.Contains(6))
|
||||
fmt.Println(a.Contains(100))
|
||||
|
||||
// 在指定索引前插入数据项
|
||||
// Insert item before specified index.
|
||||
a.InsertAfter(9, 11)
|
||||
// 在指定索引后插入数据项
|
||||
// Insert item after specified index.
|
||||
a.InsertBefore(10, 10)
|
||||
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 修改指定索引的数据项
|
||||
// Modify item by index.
|
||||
a.Set(0, 100)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 搜索数据项,返回搜索到的索引位置
|
||||
// Search item and return its index.
|
||||
fmt.Println(a.Search(5))
|
||||
|
||||
// 删除指定索引的数据项
|
||||
// Remove item by index.
|
||||
a.Remove(0)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 清空数组
|
||||
// Empty the array, removes all items of it.
|
||||
fmt.Println(a.Slice())
|
||||
a.Clear()
|
||||
fmt.Println(a.Slice())
|
||||
@ -60,7 +61,7 @@ func Example_basic() {
|
||||
// Output:
|
||||
// 10
|
||||
// [0 1 2 3 4 5 6 7 8 9]
|
||||
// 6
|
||||
// 6 true
|
||||
// true
|
||||
// false
|
||||
// [0 1 2 3 4 5 6 7 8 9 10 11]
|
||||
@ -71,31 +72,129 @@ func Example_basic() {
|
||||
// []
|
||||
}
|
||||
|
||||
func Example_iterate() {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c"})
|
||||
// Iterator is alias of IteratorAsc, which iterates the array readonly in ascending order
|
||||
// with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
array.Iterator(func(k int, v string) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
array.IteratorDesc(func(k int, v string) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 0 a
|
||||
// 1 b
|
||||
// 2 c
|
||||
// 2 c
|
||||
// 1 b
|
||||
// 0 a
|
||||
}
|
||||
|
||||
func Example_reverse() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Reverse makes array with elements in reverse order.
|
||||
fmt.Println(array.Reverse().Slice())
|
||||
|
||||
// Output:
|
||||
// [9 8 7 6 5 4 3 2 1]
|
||||
}
|
||||
|
||||
func Example_shuffle() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Shuffle randomly shuffles the array.
|
||||
fmt.Println(array.Shuffle().Slice())
|
||||
}
|
||||
|
||||
func Example_rand() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
// 随机返回两个数据项(不删除)
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Randomly retrieve and return 2 items from the array.
|
||||
// It does not delete the items from array.
|
||||
fmt.Println(array.Rands(2))
|
||||
|
||||
// Randomly pick and return one item from the array.
|
||||
// It deletes the picked up item from array.
|
||||
fmt.Println(array.PopRand())
|
||||
}
|
||||
|
||||
func Example_pop() {
|
||||
func Example_join() {
|
||||
array := garray.NewFrom(g.Slice{"a", "b", "c", "d"})
|
||||
fmt.Println(array.Join(","))
|
||||
|
||||
// Output:
|
||||
// a,b,c,d
|
||||
}
|
||||
|
||||
func Example_chunk() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// The last chunk may contain less than size elements.
|
||||
fmt.Println(array.Chunk(2))
|
||||
|
||||
// Output:
|
||||
// [[1 2] [3 4] [5 6] [7 8] [9]]
|
||||
}
|
||||
|
||||
func Example_popItem() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Any Pop* functions pick, delete and return the item from array.
|
||||
|
||||
fmt.Println(array.PopLeft())
|
||||
fmt.Println(array.PopLefts(2))
|
||||
fmt.Println(array.PopRight())
|
||||
fmt.Println(array.PopRights(2))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 1 true
|
||||
// [2 3]
|
||||
// 9
|
||||
// 9 true
|
||||
// [7 8]
|
||||
}
|
||||
|
||||
func Example_merge() {
|
||||
array1 := garray.NewFrom([]interface{}{1, 2})
|
||||
array2 := garray.NewFrom([]interface{}{3, 4})
|
||||
slice1 := []interface{}{5, 6}
|
||||
func Example_walk() {
|
||||
var array garray.StrArray
|
||||
tables := g.SliceStr{"user", "user_detail"}
|
||||
prefix := "gf_"
|
||||
array.Append(tables...)
|
||||
// Add prefix for given table names.
|
||||
array.Walk(func(value string) string {
|
||||
return prefix + value
|
||||
})
|
||||
fmt.Println(array.Slice())
|
||||
|
||||
// Output:
|
||||
// [gf_user gf_user_detail]
|
||||
}
|
||||
|
||||
func Example_contains() {
|
||||
var array garray.StrArray
|
||||
array.Append("a")
|
||||
fmt.Println(array.Contains("a"))
|
||||
fmt.Println(array.Contains("A"))
|
||||
fmt.Println(array.ContainsI("A"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
}
|
||||
|
||||
func Example_mergeArray() {
|
||||
array1 := garray.NewFrom(g.Slice{1, 2})
|
||||
array2 := garray.NewFrom(g.Slice{3, 4})
|
||||
slice1 := g.Slice{5, 6}
|
||||
slice2 := []int{7, 8}
|
||||
slice3 := []string{"9", "0"}
|
||||
fmt.Println(array1.Slice())
|
||||
@ -110,3 +209,14 @@ func Example_merge() {
|
||||
// [1 2]
|
||||
// [1 2 1 2 3 4 5 6 7 8 9 0]
|
||||
}
|
||||
|
||||
func Example_filter() {
|
||||
array1 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
|
||||
array2 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
|
||||
fmt.Printf("%#v\n", array1.FilterNil().Slice())
|
||||
fmt.Printf("%#v\n", array2.FilterEmpty().Slice())
|
||||
|
||||
// Output:
|
||||
// []interface {}{0, 1, 2, "", []interface {}{}, "john"}
|
||||
// []interface {}{1, 2, "john"}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -17,90 +18,153 @@ import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
func Test_Array_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.Array
|
||||
expect := []int{2, 3, 1}
|
||||
array.Append(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.IntArray
|
||||
expect := []int{2, 3, 1}
|
||||
array.Append(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.StrArray
|
||||
expect := []string{"b", "a"}
|
||||
array.Append("b", "a")
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.SortedArray
|
||||
array.SetComparator(gutil.ComparatorInt)
|
||||
expect := []int{1, 2, 3}
|
||||
array.Add(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.SortedIntArray
|
||||
expect := []int{1, 2, 3}
|
||||
array.Add(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.SortedStrArray
|
||||
expect := []string{"a", "b", "c"}
|
||||
array.Add("c", "a", "b")
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SortedIntArray_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.SortedIntArray
|
||||
expect := []int{1, 2, 3}
|
||||
array.Add(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntArray_Unique(t *testing.T) {
|
||||
expect := []int{1, 2, 3, 4, 5, 6}
|
||||
array := garray.NewIntArray()
|
||||
array.Append(1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6)
|
||||
array.Unique()
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []int{1, 2, 3, 4, 5, 6}
|
||||
array := garray.NewIntArray()
|
||||
array.Append(1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6)
|
||||
array.Unique()
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SortedIntArray1(t *testing.T) {
|
||||
expect := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||
array := garray.NewSortedIntArray()
|
||||
for i := 10; i > -1; i-- {
|
||||
array.Add(i)
|
||||
}
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.Assert(array.Add().Slice(), expect)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||
array := garray.NewSortedIntArray()
|
||||
for i := 10; i > -1; i-- {
|
||||
array.Add(i)
|
||||
}
|
||||
t.Assert(array.Slice(), expect)
|
||||
t.Assert(array.Add().Slice(), expect)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SortedIntArray2(t *testing.T) {
|
||||
expect := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||
array := garray.NewSortedIntArray()
|
||||
for i := 0; i <= 10; i++ {
|
||||
array.Add(i)
|
||||
}
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||
array := garray.NewSortedIntArray()
|
||||
for i := 0; i <= 10; i++ {
|
||||
array.Add(i)
|
||||
}
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SortedStrArray1(t *testing.T) {
|
||||
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
array1 := garray.NewSortedStrArray()
|
||||
array2 := garray.NewSortedStrArray(true)
|
||||
for i := 10; i > -1; i-- {
|
||||
array1.Add(gconv.String(i))
|
||||
array2.Add(gconv.String(i))
|
||||
}
|
||||
gtest.Assert(array1.Slice(), expect)
|
||||
gtest.Assert(array2.Slice(), expect)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
array1 := garray.NewSortedStrArray()
|
||||
array2 := garray.NewSortedStrArray(true)
|
||||
for i := 10; i > -1; i-- {
|
||||
array1.Add(gconv.String(i))
|
||||
array2.Add(gconv.String(i))
|
||||
}
|
||||
t.Assert(array1.Slice(), expect)
|
||||
t.Assert(array2.Slice(), expect)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_SortedStrArray2(t *testing.T) {
|
||||
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
array := garray.NewSortedStrArray()
|
||||
for i := 0; i <= 10; i++ {
|
||||
array.Add(gconv.String(i))
|
||||
}
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
array.Add()
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
array := garray.NewSortedStrArray()
|
||||
for i := 0; i <= 10; i++ {
|
||||
array.Add(gconv.String(i))
|
||||
}
|
||||
t.Assert(array.Slice(), expect)
|
||||
array.Add()
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SortedArray1(t *testing.T) {
|
||||
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
array := garray.NewSortedArray(func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
array := garray.NewSortedArray(func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
})
|
||||
for i := 10; i > -1; i-- {
|
||||
array.Add(gconv.String(i))
|
||||
}
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
for i := 10; i > -1; i-- {
|
||||
array.Add(gconv.String(i))
|
||||
}
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
}
|
||||
|
||||
func Test_SortedArray2(t *testing.T) {
|
||||
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array := garray.NewSortedArray(func1)
|
||||
array2 := garray.NewSortedArray(func1, true)
|
||||
for i := 0; i <= 10; i++ {
|
||||
array.Add(gconv.String(i))
|
||||
array2.Add(gconv.String(i))
|
||||
}
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.Assert(array.Add().Slice(), expect)
|
||||
gtest.Assert(array2.Slice(), expect)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array := garray.NewSortedArray(func1)
|
||||
array2 := garray.NewSortedArray(func1, true)
|
||||
for i := 0; i <= 10; i++ {
|
||||
array.Add(gconv.String(i))
|
||||
array2.Add(gconv.String(i))
|
||||
}
|
||||
t.Assert(array.Slice(), expect)
|
||||
t.Assert(array.Add().Slice(), expect)
|
||||
t.Assert(array2.Slice(), expect)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewFromCopy(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"100", "200", "300", "400", "500", "600"}
|
||||
array1 := garray.NewFromCopy(a1)
|
||||
gtest.AssertIN(array1.PopRands(2), a1)
|
||||
gtest.Assert(len(array1.PopRands(1)), 1)
|
||||
gtest.Assert(len(array1.PopRands(9)), 3)
|
||||
t.AssertIN(array1.PopRands(2), a1)
|
||||
t.Assert(len(array1.PopRands(1)), 1)
|
||||
t.Assert(len(array1.PopRands(9)), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -20,39 +20,62 @@ import (
|
||||
)
|
||||
|
||||
func Test_Array_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []interface{}{0, 1, 2, 3}
|
||||
array := garray.NewArrayFrom(expect)
|
||||
array2 := garray.NewArrayFrom(expect)
|
||||
array3 := garray.NewArrayFrom([]interface{}{})
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.Assert(array.Interfaces(), expect)
|
||||
t.Assert(array.Slice(), expect)
|
||||
t.Assert(array.Interfaces(), expect)
|
||||
array.Set(0, 100)
|
||||
gtest.Assert(array.Get(0), 100)
|
||||
gtest.Assert(array.Get(1), 1)
|
||||
gtest.Assert(array.Search(100), 0)
|
||||
gtest.Assert(array3.Search(100), -1)
|
||||
gtest.Assert(array.Contains(100), true)
|
||||
gtest.Assert(array.Remove(0), 100)
|
||||
|
||||
gtest.Assert(array2.Remove(3), 3)
|
||||
gtest.Assert(array2.Remove(1), 1)
|
||||
v, ok := array.Get(0)
|
||||
t.Assert(v, 100)
|
||||
t.Assert(ok, true)
|
||||
|
||||
gtest.Assert(array.Contains(100), false)
|
||||
v, ok = array.Get(1)
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array.Search(100), 0)
|
||||
t.Assert(array3.Search(100), -1)
|
||||
t.Assert(array.Contains(100), true)
|
||||
|
||||
v, ok = array.Remove(0)
|
||||
t.Assert(v, 100)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.Remove(-1)
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
|
||||
v, ok = array.Remove(100000)
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
|
||||
v, ok = array2.Remove(3)
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array2.Remove(1)
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array.Contains(100), false)
|
||||
array.Append(4)
|
||||
gtest.Assert(array.Len(), 4)
|
||||
t.Assert(array.Len(), 4)
|
||||
array.InsertBefore(0, 100)
|
||||
array.InsertAfter(0, 200)
|
||||
gtest.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 4})
|
||||
t.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 4})
|
||||
array.InsertBefore(5, 300)
|
||||
array.InsertAfter(6, 400)
|
||||
gtest.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 300, 4, 400})
|
||||
gtest.Assert(array.Clear().Len(), 0)
|
||||
t.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 300, 4, 400})
|
||||
t.Assert(array.Clear().Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Sort(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect1 := []interface{}{0, 1, 2, 3}
|
||||
expect2 := []interface{}{3, 2, 1, 0}
|
||||
array := garray.NewArray()
|
||||
@ -62,78 +85,168 @@ func TestArray_Sort(t *testing.T) {
|
||||
array.SortFunc(func(v1, v2 interface{}) bool {
|
||||
return v1.(int) < v2.(int)
|
||||
})
|
||||
gtest.Assert(array.Slice(), expect1)
|
||||
t.Assert(array.Slice(), expect1)
|
||||
array.SortFunc(func(v1, v2 interface{}) bool {
|
||||
return v1.(int) > v2.(int)
|
||||
})
|
||||
gtest.Assert(array.Slice(), expect2)
|
||||
t.Assert(array.Slice(), expect2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Unique(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []interface{}{1, 1, 2, 3}
|
||||
array := garray.NewArrayFrom(expect)
|
||||
gtest.Assert(array.Unique().Slice(), []interface{}{1, 2, 3})
|
||||
t.Assert(array.Unique().Slice(), []interface{}{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PushAndPop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []interface{}{0, 1, 2, 3}
|
||||
array := garray.NewArrayFrom(expect)
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.Assert(array.PopLeft(), 0)
|
||||
gtest.Assert(array.PopRight(), 3)
|
||||
gtest.AssertIN(array.PopRand(), []interface{}{1, 2})
|
||||
gtest.AssertIN(array.PopRand(), []interface{}{1, 2})
|
||||
gtest.Assert(array.Len(), 0)
|
||||
t.Assert(array.Slice(), expect)
|
||||
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.AssertIN(v, []interface{}{1, 2})
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.AssertIN(v, []interface{}{1, 2})
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array.Len(), 0)
|
||||
array.PushLeft(1).PushRight(2)
|
||||
gtest.Assert(array.Slice(), []interface{}{1, 2})
|
||||
t.Assert(array.Slice(), []interface{}{1, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopRands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{100, 200, 300, 400, 500, 600}
|
||||
array := garray.NewFromCopy(a1)
|
||||
gtest.AssertIN(array.PopRands(2), []interface{}{100, 200, 300, 400, 500, 600})
|
||||
t.AssertIN(array.PopRands(2), []interface{}{100, 200, 300, 400, 500, 600})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopLeft(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopRight(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3})
|
||||
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopLefts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3})
|
||||
t.Assert(array.PopLefts(2), g.Slice{1, 2})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{3})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3})
|
||||
t.Assert(array.PopRights(2), g.Slice{2, 3})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{1})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopLeftsAndPopRights(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.New()
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopLefts(10), nil)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopRights(10), nil)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopRands(10), nil)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
value1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
value2 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(value1)
|
||||
array2 := garray.NewArrayFrom(value2)
|
||||
gtest.Assert(array1.PopLefts(2), []interface{}{0, 1})
|
||||
gtest.Assert(array1.Slice(), []interface{}{2, 3, 4, 5, 6})
|
||||
gtest.Assert(array1.PopRights(2), []interface{}{5, 6})
|
||||
gtest.Assert(array1.Slice(), []interface{}{2, 3, 4})
|
||||
gtest.Assert(array1.PopRights(20), []interface{}{2, 3, 4})
|
||||
gtest.Assert(array1.Slice(), []interface{}{})
|
||||
gtest.Assert(array2.PopLefts(20), []interface{}{0, 1, 2, 3, 4, 5, 6})
|
||||
gtest.Assert(array2.Slice(), []interface{}{})
|
||||
t.Assert(array1.PopLefts(2), []interface{}{0, 1})
|
||||
t.Assert(array1.Slice(), []interface{}{2, 3, 4, 5, 6})
|
||||
t.Assert(array1.PopRights(2), []interface{}{5, 6})
|
||||
t.Assert(array1.Slice(), []interface{}{2, 3, 4})
|
||||
t.Assert(array1.PopRights(20), []interface{}{2, 3, 4})
|
||||
t.Assert(array1.Slice(), []interface{}{})
|
||||
t.Assert(array2.PopLefts(20), []interface{}{0, 1, 2, 3, 4, 5, 6})
|
||||
t.Assert(array2.Slice(), []interface{}{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
value1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(value1)
|
||||
array2 := garray.NewArrayFrom(value1, true)
|
||||
gtest.Assert(array1.Range(0, 1), []interface{}{0})
|
||||
gtest.Assert(array1.Range(1, 2), []interface{}{1})
|
||||
gtest.Assert(array1.Range(0, 2), []interface{}{0, 1})
|
||||
gtest.Assert(array1.Range(-1, 10), value1)
|
||||
gtest.Assert(array1.Range(10, 2), nil)
|
||||
gtest.Assert(array2.Range(1, 3), []interface{}{1, 2})
|
||||
t.Assert(array1.Range(0, 1), []interface{}{0})
|
||||
t.Assert(array1.Range(1, 2), []interface{}{1})
|
||||
t.Assert(array1.Range(0, 2), []interface{}{0, 1})
|
||||
t.Assert(array1.Range(-1, 10), value1)
|
||||
t.Assert(array1.Range(10, 2), nil)
|
||||
t.Assert(array2.Range(1, 3), []interface{}{1, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
@ -145,7 +258,7 @@ func TestArray_Merge(t *testing.T) {
|
||||
i2 := []interface{}{4, 5, 6, 7}
|
||||
array1 := garray.NewArrayFrom(i1)
|
||||
array2 := garray.NewArrayFrom(i2)
|
||||
gtest.Assert(array1.Merge(array2).Slice(), []interface{}{0, 1, 2, 3, 4, 5, 6, 7})
|
||||
t.Assert(array1.Merge(array2).Slice(), []interface{}{0, 1, 2, 3, 4, 5, 6, 7})
|
||||
|
||||
//s1 := []string{"a", "b", "c", "d"}
|
||||
s2 := []string{"e", "f"}
|
||||
@ -157,157 +270,192 @@ func TestArray_Merge(t *testing.T) {
|
||||
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
a1 := garray.NewArrayFrom(i1)
|
||||
|
||||
gtest.Assert(a1.Merge(s2).Len(), 6)
|
||||
gtest.Assert(a1.Merge(i3).Len(), 9)
|
||||
gtest.Assert(a1.Merge(i4).Len(), 10)
|
||||
gtest.Assert(a1.Merge(s3).Len(), 12)
|
||||
gtest.Assert(a1.Merge(s4).Len(), 14)
|
||||
gtest.Assert(a1.Merge(s5).Len(), 16)
|
||||
gtest.Assert(a1.Merge(s6).Len(), 19)
|
||||
t.Assert(a1.Merge(s2).Len(), 6)
|
||||
t.Assert(a1.Merge(i3).Len(), 9)
|
||||
t.Assert(a1.Merge(i4).Len(), 10)
|
||||
t.Assert(a1.Merge(s3).Len(), 12)
|
||||
t.Assert(a1.Merge(s4).Len(), 14)
|
||||
t.Assert(a1.Merge(s5).Len(), 16)
|
||||
t.Assert(a1.Merge(s6).Len(), 19)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Fill(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0}
|
||||
a2 := []interface{}{0}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := garray.NewArrayFrom(a2, true)
|
||||
gtest.Assert(array1.Fill(1, 2, 100).Slice(), []interface{}{0, 100, 100})
|
||||
gtest.Assert(array2.Fill(0, 2, 100).Slice(), []interface{}{100, 100})
|
||||
gtest.Assert(array2.Fill(-1, 2, 100).Slice(), []interface{}{100, 100})
|
||||
|
||||
t.Assert(array1.Fill(1, 2, 100), nil)
|
||||
t.Assert(array1.Slice(), []interface{}{0, 100, 100})
|
||||
|
||||
t.Assert(array2.Fill(0, 2, 100), nil)
|
||||
t.Assert(array2.Slice(), []interface{}{100, 100})
|
||||
|
||||
t.AssertNE(array2.Fill(-1, 2, 100), nil)
|
||||
t.Assert(array2.Slice(), []interface{}{100, 100})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Chunk(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
gtest.Assert(len(chunks), 3)
|
||||
gtest.Assert(chunks[0], []interface{}{1, 2})
|
||||
gtest.Assert(chunks[1], []interface{}{3, 4})
|
||||
gtest.Assert(chunks[2], []interface{}{5})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []interface{}{1, 2})
|
||||
t.Assert(chunks[1], []interface{}{3, 4})
|
||||
t.Assert(chunks[2], []interface{}{5})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []interface{}{1, 2, 3})
|
||||
t.Assert(chunks[1], []interface{}{4, 5})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []interface{}{1, 2})
|
||||
t.Assert(chunks[1], []interface{}{3, 4})
|
||||
t.Assert(chunks[2], []interface{}{5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []interface{}{1, 2, 3})
|
||||
t.Assert(chunks[1], []interface{}{4, 5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Pad(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(array1.Pad(3, 1).Slice(), []interface{}{0, 1, 1})
|
||||
gtest.Assert(array1.Pad(-4, 1).Slice(), []interface{}{1, 0, 1, 1})
|
||||
gtest.Assert(array1.Pad(3, 1).Slice(), []interface{}{1, 0, 1, 1})
|
||||
t.Assert(array1.Pad(3, 1).Slice(), []interface{}{0, 1, 1})
|
||||
t.Assert(array1.Pad(-4, 1).Slice(), []interface{}{1, 0, 1, 1})
|
||||
t.Assert(array1.Pad(3, 1).Slice(), []interface{}{1, 0, 1, 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := garray.NewArrayFrom(a1, true)
|
||||
gtest.Assert(array1.SubSlice(0, 2), []interface{}{0, 1})
|
||||
gtest.Assert(array1.SubSlice(2, 2), []interface{}{2, 3})
|
||||
gtest.Assert(array1.SubSlice(5, 8), []interface{}{5, 6})
|
||||
gtest.Assert(array1.SubSlice(9, 1), nil)
|
||||
gtest.Assert(array1.SubSlice(-2, 2), []interface{}{5, 6})
|
||||
gtest.Assert(array1.SubSlice(-9, 2), nil)
|
||||
gtest.Assert(array1.SubSlice(1, -2), nil)
|
||||
gtest.Assert(array2.SubSlice(0, 2), []interface{}{0, 1})
|
||||
t.Assert(array1.SubSlice(0, 2), []interface{}{0, 1})
|
||||
t.Assert(array1.SubSlice(2, 2), []interface{}{2, 3})
|
||||
t.Assert(array1.SubSlice(5, 8), []interface{}{5, 6})
|
||||
t.Assert(array1.SubSlice(9, 1), nil)
|
||||
t.Assert(array1.SubSlice(-2, 2), []interface{}{5, 6})
|
||||
t.Assert(array1.SubSlice(-9, 2), nil)
|
||||
t.Assert(array1.SubSlice(1, -2), nil)
|
||||
t.Assert(array2.SubSlice(0, 2), []interface{}{0, 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Rand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(len(array1.Rands(2)), 2)
|
||||
gtest.Assert(len(array1.Rands(10)), 7)
|
||||
gtest.AssertIN(array1.Rands(1)[0], a1)
|
||||
t.Assert(len(array1.Rands(2)), 2)
|
||||
t.Assert(len(array1.Rands(10)), 10)
|
||||
t.AssertIN(array1.Rands(1)[0], a1)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewArrayFrom(s1)
|
||||
i1 := a1.Rand()
|
||||
gtest.Assert(a1.Contains(i1), true)
|
||||
gtest.Assert(a1.Len(), 4)
|
||||
i1, ok := a1.Rand()
|
||||
t.Assert(ok, true)
|
||||
t.Assert(a1.Contains(i1), true)
|
||||
t.Assert(a1.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Shuffle(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(array1.Shuffle().Len(), 7)
|
||||
t.Assert(array1.Shuffle().Len(), 7)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Reverse(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(array1.Reverse().Slice(), []interface{}{6, 5, 4, 3, 2, 1, 0})
|
||||
t.Assert(array1.Reverse().Slice(), []interface{}{6, 5, 4, 3, 2, 1, 0})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Join(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(array1.Join("."), `0.1.2.3.4.5.6`)
|
||||
t.Assert(array1.Join("."), `0.1.2.3.4.5.6`)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, `"a"`, `\a`}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(array1.Join("."), `0.1."a".\a`)
|
||||
t.Assert(array1.Join("."), `0.1."a".\a`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_String(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(array1.String(), `[0,1,2,3,4,5,6]`)
|
||||
t.Assert(array1.String(), `[0,1,2,3,4,5,6]`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Replace(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
a2 := []interface{}{"a", "b", "c"}
|
||||
a3 := []interface{}{"m", "n", "p", "z", "x", "y", "d", "u"}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := array1.Replace(a2)
|
||||
gtest.Assert(array2.Len(), 7)
|
||||
gtest.Assert(array2.Contains("b"), true)
|
||||
gtest.Assert(array2.Contains(4), true)
|
||||
gtest.Assert(array2.Contains("v"), false)
|
||||
t.Assert(array2.Len(), 7)
|
||||
t.Assert(array2.Contains("b"), true)
|
||||
t.Assert(array2.Contains(4), true)
|
||||
t.Assert(array2.Contains("v"), false)
|
||||
array3 := array1.Replace(a3)
|
||||
gtest.Assert(array3.Len(), 7)
|
||||
gtest.Assert(array3.Contains(4), false)
|
||||
gtest.Assert(array3.Contains("p"), true)
|
||||
gtest.Assert(array3.Contains("u"), false)
|
||||
t.Assert(array3.Len(), 7)
|
||||
t.Assert(array3.Contains(4), false)
|
||||
t.Assert(array3.Contains("p"), true)
|
||||
t.Assert(array3.Contains("u"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_SetArray(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
a2 := []interface{}{"a", "b", "c"}
|
||||
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array1 = array1.SetArray(a2)
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1.Contains("b"), true)
|
||||
gtest.Assert(array1.Contains("5"), false)
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1.Contains("b"), true)
|
||||
t.Assert(array1.Contains("5"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Sum(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3}
|
||||
a2 := []interface{}{"a", "b", "c"}
|
||||
a3 := []interface{}{"a", "1", "2"}
|
||||
@ -316,39 +464,39 @@ func TestArray_Sum(t *testing.T) {
|
||||
array2 := garray.NewArrayFrom(a2)
|
||||
array3 := garray.NewArrayFrom(a3)
|
||||
|
||||
gtest.Assert(array1.Sum(), 6)
|
||||
gtest.Assert(array2.Sum(), 0)
|
||||
gtest.Assert(array3.Sum(), 3)
|
||||
t.Assert(array1.Sum(), 6)
|
||||
t.Assert(array2.Sum(), 0)
|
||||
t.Assert(array3.Sum(), 3)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Clone(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := array1.Clone()
|
||||
|
||||
gtest.Assert(array1.Len(), 4)
|
||||
gtest.Assert(array2.Sum(), 6)
|
||||
gtest.AssertEQ(array1, array2)
|
||||
t.Assert(array1.Len(), 4)
|
||||
t.Assert(array2.Sum(), 6)
|
||||
t.AssertEQ(array1, array2)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_CountValues(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "b", "c", "d", "e", "d"}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := array1.CountValues()
|
||||
gtest.Assert(len(array2), 5)
|
||||
gtest.Assert(array2["b"], 1)
|
||||
gtest.Assert(array2["d"], 2)
|
||||
t.Assert(len(array2), 5)
|
||||
t.Assert(array2["b"], 1)
|
||||
t.Assert(array2["d"], 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewArrayFrom(s1, true)
|
||||
|
||||
@ -374,13 +522,13 @@ func TestArray_LockFunc(t *testing.T) {
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
t.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_RLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewArrayFrom(s1, true)
|
||||
|
||||
@ -406,32 +554,52 @@ func TestArray_RLockFunc(t *testing.T) {
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
t.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Json(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
// pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
a1 := garray.NewArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
gtest.Assert(b1, b2)
|
||||
gtest.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.New()
|
||||
err2 = json.Unmarshal(b2, &a2)
|
||||
gtest.Assert(err2, nil)
|
||||
gtest.Assert(a2.Slice(), s1)
|
||||
t.Assert(err2, nil)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.Array
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(a3.Slice(), s1)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// value.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
a1 := *garray.NewArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
a2 := garray.New()
|
||||
err2 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(err2, nil)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.Array
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores *garray.Array
|
||||
@ -441,59 +609,151 @@ func TestArray_Json(t *testing.T) {
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.Name, data["Name"])
|
||||
gtest.Assert(user.Scores, data["Scores"])
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
// value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.Array
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Iterator(t *testing.T) {
|
||||
slice := g.Slice{"a", "b", "d", "c"}
|
||||
array := garray.NewArrayFrom(slice)
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.Iterator(func(k int, v interface{}) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorAsc(func(k int, v interface{}) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorDesc(func(k int, v interface{}) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.Iterator(func(k int, v interface{}) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorAsc(func(k int, v interface{}) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorDesc(func(k int, v interface{}) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_RemoveValue(t *testing.T) {
|
||||
slice := g.Slice{"a", "b", "d", "c"}
|
||||
array := garray.NewArrayFrom(slice)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(array.RemoveValue("e"), false)
|
||||
t.Assert(array.RemoveValue("b"), true)
|
||||
t.Assert(array.RemoveValue("a"), true)
|
||||
t.Assert(array.RemoveValue("c"), true)
|
||||
t.Assert(array.RemoveValue("f"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.Array
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`[1,2,3]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.Slice{1, 2, 3},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_FilterNil(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
|
||||
array := garray.NewArrayFromCopy(values)
|
||||
t.Assert(array.FilterNil().Slice(), values)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil})
|
||||
t.Assert(array.FilterNil(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}})
|
||||
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewArrayFrom(g.Slice{1, 2, 3, 4})
|
||||
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewArrayFrom(g.Slice{"1", "2"})
|
||||
t.Assert(array.Walk(func(value interface{}) interface{} {
|
||||
return "key-" + gconv.String(value)
|
||||
}), g.Slice{"key-1", "key-2"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -21,35 +21,54 @@ import (
|
||||
)
|
||||
|
||||
func Test_IntArray_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []int{0, 1, 2, 3}
|
||||
expect2 := []int{}
|
||||
array := garray.NewIntArrayFrom(expect)
|
||||
array2 := garray.NewIntArrayFrom(expect2)
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.Assert(array.Interfaces(), expect)
|
||||
t.Assert(array.Slice(), expect)
|
||||
t.Assert(array.Interfaces(), expect)
|
||||
array.Set(0, 100)
|
||||
gtest.Assert(array.Get(0), 100)
|
||||
gtest.Assert(array.Get(1), 1)
|
||||
gtest.Assert(array.Search(100), 0)
|
||||
gtest.Assert(array2.Search(100), -1)
|
||||
gtest.Assert(array.Contains(100), true)
|
||||
gtest.Assert(array.Remove(0), 100)
|
||||
gtest.Assert(array.Contains(100), false)
|
||||
|
||||
v, ok := array.Get(0)
|
||||
t.Assert(v, 100)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.Get(1)
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array.Search(100), 0)
|
||||
t.Assert(array2.Search(100), -1)
|
||||
t.Assert(array.Contains(100), true)
|
||||
|
||||
v, ok = array.Remove(0)
|
||||
t.Assert(v, 100)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.Remove(-1)
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
|
||||
v, ok = array.Remove(100000)
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
|
||||
t.Assert(array.Contains(100), false)
|
||||
array.Append(4)
|
||||
gtest.Assert(array.Len(), 4)
|
||||
t.Assert(array.Len(), 4)
|
||||
array.InsertBefore(0, 100)
|
||||
array.InsertAfter(0, 200)
|
||||
gtest.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 4})
|
||||
t.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 4})
|
||||
array.InsertBefore(5, 300)
|
||||
array.InsertAfter(6, 400)
|
||||
gtest.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400})
|
||||
gtest.Assert(array.Clear().Len(), 0)
|
||||
t.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400})
|
||||
t.Assert(array.Clear().Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Sort(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect1 := []int{0, 1, 2, 3}
|
||||
expect2 := []int{3, 2, 1, 0}
|
||||
array := garray.NewIntArray()
|
||||
@ -59,69 +78,108 @@ func TestIntArray_Sort(t *testing.T) {
|
||||
array2.Append(i)
|
||||
}
|
||||
array.Sort()
|
||||
gtest.Assert(array.Slice(), expect1)
|
||||
t.Assert(array.Slice(), expect1)
|
||||
array.Sort(true)
|
||||
gtest.Assert(array.Slice(), expect2)
|
||||
gtest.Assert(array2.Slice(), expect2)
|
||||
t.Assert(array.Slice(), expect2)
|
||||
t.Assert(array2.Slice(), expect2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Unique(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []int{1, 1, 2, 3}
|
||||
array := garray.NewIntArrayFrom(expect)
|
||||
gtest.Assert(array.Unique().Slice(), []int{1, 2, 3})
|
||||
t.Assert(array.Unique().Slice(), []int{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PushAndPop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []int{0, 1, 2, 3}
|
||||
array := garray.NewIntArrayFrom(expect)
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.Assert(array.PopLeft(), 0)
|
||||
gtest.Assert(array.PopRight(), 3)
|
||||
gtest.AssertIN(array.PopRand(), []int{1, 2})
|
||||
gtest.AssertIN(array.PopRand(), []int{1, 2})
|
||||
gtest.Assert(array.Len(), 0)
|
||||
t.Assert(array.Slice(), expect)
|
||||
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.AssertIN(v, []int{1, 2})
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.AssertIN(v, []int{1, 2})
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
|
||||
t.Assert(array.Len(), 0)
|
||||
array.PushLeft(1).PushRight(2)
|
||||
gtest.Assert(array.Slice(), []int{1, 2})
|
||||
t.Assert(array.Slice(), []int{1, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopLeftsAndPopRights(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArray()
|
||||
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
|
||||
t.Assert(array.PopLefts(10), nil)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
|
||||
t.Assert(array.PopRights(10), nil)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
|
||||
t.Assert(array.PopRands(10), nil)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
value1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
value2 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(value1)
|
||||
array2 := garray.NewIntArrayFrom(value2)
|
||||
gtest.Assert(array1.PopLefts(2), []int{0, 1})
|
||||
gtest.Assert(array1.Slice(), []int{2, 3, 4, 5, 6})
|
||||
gtest.Assert(array1.PopRights(2), []int{5, 6})
|
||||
gtest.Assert(array1.Slice(), []int{2, 3, 4})
|
||||
gtest.Assert(array1.PopRights(20), []int{2, 3, 4})
|
||||
gtest.Assert(array1.Slice(), []int{})
|
||||
gtest.Assert(array2.PopLefts(20), []int{0, 1, 2, 3, 4, 5, 6})
|
||||
gtest.Assert(array2.Slice(), []int{})
|
||||
t.Assert(array1.PopLefts(2), []int{0, 1})
|
||||
t.Assert(array1.Slice(), []int{2, 3, 4, 5, 6})
|
||||
t.Assert(array1.PopRights(2), []int{5, 6})
|
||||
t.Assert(array1.Slice(), []int{2, 3, 4})
|
||||
t.Assert(array1.PopRights(20), []int{2, 3, 4})
|
||||
t.Assert(array1.Slice(), []int{})
|
||||
t.Assert(array2.PopLefts(20), []int{0, 1, 2, 3, 4, 5, 6})
|
||||
t.Assert(array2.Slice(), []int{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
value1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(value1)
|
||||
array2 := garray.NewIntArrayFrom(value1, true)
|
||||
gtest.Assert(array1.Range(0, 1), []int{0})
|
||||
gtest.Assert(array1.Range(1, 2), []int{1})
|
||||
gtest.Assert(array1.Range(0, 2), []int{0, 1})
|
||||
gtest.Assert(array1.Range(10, 2), nil)
|
||||
gtest.Assert(array1.Range(-1, 10), value1)
|
||||
gtest.Assert(array2.Range(1, 2), []int{1})
|
||||
t.Assert(array1.Range(0, 1), []int{0})
|
||||
t.Assert(array1.Range(1, 2), []int{1})
|
||||
t.Assert(array1.Range(0, 2), []int{0, 1})
|
||||
t.Assert(array1.Range(10, 2), nil)
|
||||
t.Assert(array1.Range(-1, 10), value1)
|
||||
t.Assert(array2.Range(1, 2), []int{1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
@ -145,232 +203,332 @@ func TestIntArray_Merge(t *testing.T) {
|
||||
a7 := garray.NewSortedStrArrayFrom(s1)
|
||||
a8 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
|
||||
|
||||
gtest.Assert(a1.Merge(a2).Slice(), []int{0, 1, 2, 3, 4, 5, 6, 7})
|
||||
gtest.Assert(a1.Merge(a3).Len(), 10)
|
||||
gtest.Assert(a1.Merge(a4).Len(), 13)
|
||||
gtest.Assert(a1.Merge(a5).Len(), 15)
|
||||
gtest.Assert(a1.Merge(a6).Len(), 18)
|
||||
gtest.Assert(a1.Merge(a7).Len(), 21)
|
||||
gtest.Assert(a1.Merge(a8).Len(), 23)
|
||||
t.Assert(a1.Merge(a2).Slice(), []int{0, 1, 2, 3, 4, 5, 6, 7})
|
||||
t.Assert(a1.Merge(a3).Len(), 10)
|
||||
t.Assert(a1.Merge(a4).Len(), 13)
|
||||
t.Assert(a1.Merge(a5).Len(), 15)
|
||||
t.Assert(a1.Merge(a6).Len(), 18)
|
||||
t.Assert(a1.Merge(a7).Len(), 21)
|
||||
t.Assert(a1.Merge(a8).Len(), 23)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Fill(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0}
|
||||
a2 := []int{0}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array2 := garray.NewIntArrayFrom(a2)
|
||||
gtest.Assert(array1.Fill(1, 2, 100).Slice(), []int{0, 100, 100})
|
||||
gtest.Assert(array2.Fill(0, 2, 100).Slice(), []int{100, 100})
|
||||
gtest.Assert(array2.Fill(-1, 2, 100).Slice(), []int{100, 100})
|
||||
t.Assert(array1.Fill(1, 2, 100), nil)
|
||||
t.Assert(array1.Slice(), []int{0, 100, 100})
|
||||
|
||||
t.Assert(array2.Fill(0, 2, 100), nil)
|
||||
t.Assert(array2.Slice(), []int{100, 100})
|
||||
|
||||
t.AssertNE(array2.Fill(-1, 2, 100), nil)
|
||||
t.Assert(array2.Slice(), []int{100, 100})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopLeft(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopRight(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopLefts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
t.Assert(array.PopLefts(2), g.Slice{1, 2})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{3})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
t.Assert(array.PopRights(2), g.Slice{2, 3})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{1})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Chunk(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
gtest.Assert(len(chunks), 3)
|
||||
gtest.Assert(chunks[0], []int{1, 2})
|
||||
gtest.Assert(chunks[1], []int{3, 4})
|
||||
gtest.Assert(chunks[2], []int{5})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []int{1, 2})
|
||||
t.Assert(chunks[1], []int{3, 4})
|
||||
t.Assert(chunks[2], []int{5})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []int{1, 2, 3})
|
||||
t.Assert(chunks[1], []int{4, 5})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []int{1, 2})
|
||||
t.Assert(chunks[1], []int{3, 4})
|
||||
t.Assert(chunks[2], []int{5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []int{1, 2, 3})
|
||||
t.Assert(chunks[1], []int{4, 5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Pad(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(array1.Pad(3, 1).Slice(), []int{0, 1, 1})
|
||||
gtest.Assert(array1.Pad(-4, 1).Slice(), []int{1, 0, 1, 1})
|
||||
gtest.Assert(array1.Pad(3, 1).Slice(), []int{1, 0, 1, 1})
|
||||
t.Assert(array1.Pad(3, 1).Slice(), []int{0, 1, 1})
|
||||
t.Assert(array1.Pad(-4, 1).Slice(), []int{1, 0, 1, 1})
|
||||
t.Assert(array1.Pad(3, 1).Slice(), []int{1, 0, 1, 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array2 := garray.NewIntArrayFrom(a1, true)
|
||||
gtest.Assert(array1.SubSlice(6), []int{6})
|
||||
gtest.Assert(array1.SubSlice(5), []int{5, 6})
|
||||
gtest.Assert(array1.SubSlice(8), nil)
|
||||
gtest.Assert(array1.SubSlice(0, 2), []int{0, 1})
|
||||
gtest.Assert(array1.SubSlice(2, 2), []int{2, 3})
|
||||
gtest.Assert(array1.SubSlice(5, 8), []int{5, 6})
|
||||
gtest.Assert(array1.SubSlice(-1, 1), []int{6})
|
||||
gtest.Assert(array1.SubSlice(-1, 9), []int{6})
|
||||
gtest.Assert(array1.SubSlice(-2, 3), []int{5, 6})
|
||||
gtest.Assert(array1.SubSlice(-7, 3), []int{0, 1, 2})
|
||||
gtest.Assert(array1.SubSlice(-8, 3), nil)
|
||||
gtest.Assert(array1.SubSlice(-1, -3), []int{3, 4, 5})
|
||||
gtest.Assert(array1.SubSlice(-9, 3), nil)
|
||||
gtest.Assert(array1.SubSlice(1, -1), []int{0})
|
||||
gtest.Assert(array1.SubSlice(1, -3), nil)
|
||||
gtest.Assert(array2.SubSlice(0, 2), []int{0, 1})
|
||||
t.Assert(array1.SubSlice(6), []int{6})
|
||||
t.Assert(array1.SubSlice(5), []int{5, 6})
|
||||
t.Assert(array1.SubSlice(8), nil)
|
||||
t.Assert(array1.SubSlice(0, 2), []int{0, 1})
|
||||
t.Assert(array1.SubSlice(2, 2), []int{2, 3})
|
||||
t.Assert(array1.SubSlice(5, 8), []int{5, 6})
|
||||
t.Assert(array1.SubSlice(-1, 1), []int{6})
|
||||
t.Assert(array1.SubSlice(-1, 9), []int{6})
|
||||
t.Assert(array1.SubSlice(-2, 3), []int{5, 6})
|
||||
t.Assert(array1.SubSlice(-7, 3), []int{0, 1, 2})
|
||||
t.Assert(array1.SubSlice(-8, 3), nil)
|
||||
t.Assert(array1.SubSlice(-1, -3), []int{3, 4, 5})
|
||||
t.Assert(array1.SubSlice(-9, 3), nil)
|
||||
t.Assert(array1.SubSlice(1, -1), []int{0})
|
||||
t.Assert(array1.SubSlice(1, -3), nil)
|
||||
t.Assert(array2.SubSlice(0, 2), []int{0, 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Rand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(len(array1.Rands(2)), 2)
|
||||
gtest.Assert(len(array1.Rands(10)), 7)
|
||||
gtest.AssertIN(array1.Rands(1)[0], a1)
|
||||
gtest.AssertIN(array1.Rand(), a1)
|
||||
t.Assert(len(array1.Rands(2)), 2)
|
||||
t.Assert(len(array1.Rands(10)), 10)
|
||||
t.AssertIN(array1.Rands(1)[0], a1)
|
||||
|
||||
v, ok := array1.Rand()
|
||||
t.AssertIN(v, a1)
|
||||
t.Assert(ok, true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopRands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{100, 200, 300, 400, 500, 600}
|
||||
array := garray.NewIntArrayFrom(a1)
|
||||
ns1 := array.PopRands(2)
|
||||
gtest.AssertIN(ns1, []int{100, 200, 300, 400, 500, 600})
|
||||
gtest.Assert(len(ns1), 2)
|
||||
t.AssertIN(ns1, []int{100, 200, 300, 400, 500, 600})
|
||||
t.Assert(len(ns1), 2)
|
||||
|
||||
ns2 := array.PopRands(7)
|
||||
gtest.Assert(len(ns2), 4)
|
||||
gtest.AssertIN(ns2, []int{100, 200, 300, 400, 500, 600})
|
||||
t.Assert(len(ns2), 4)
|
||||
t.AssertIN(ns2, []int{100, 200, 300, 400, 500, 600})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Shuffle(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(array1.Shuffle().Len(), 7)
|
||||
t.Assert(array1.Shuffle().Len(), 7)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Reverse(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(array1.Reverse().Slice(), []int{6, 5, 4, 3, 2, 1, 0})
|
||||
t.Assert(array1.Reverse().Slice(), []int{6, 5, 4, 3, 2, 1, 0})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Join(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
|
||||
t.Assert(array1.Join("."), "0.1.2.3.4.5.6")
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_String(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(array1.String(), "[0,1,2,3,4,5,6]")
|
||||
t.Assert(array1.String(), "[0,1,2,3,4,5,6]")
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_SetArray(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5}
|
||||
a2 := []int{6, 7}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array1.SetArray(a2)
|
||||
gtest.Assert(array1.Len(), 2)
|
||||
gtest.Assert(array1, []int{6, 7})
|
||||
t.Assert(array1.Len(), 2)
|
||||
t.Assert(array1, []int{6, 7})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Replace(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5}
|
||||
a2 := []int{6, 7}
|
||||
a3 := []int{9, 10, 11, 12, 13}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array1.Replace(a2)
|
||||
gtest.Assert(array1, []int{6, 7, 3, 5})
|
||||
t.Assert(array1, []int{6, 7, 3, 5})
|
||||
|
||||
array1.Replace(a3)
|
||||
gtest.Assert(array1, []int{9, 10, 11, 12})
|
||||
t.Assert(array1, []int{9, 10, 11, 12})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Clear(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array1.Clear()
|
||||
gtest.Assert(array1.Len(), 0)
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Clone(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array2 := array1.Clone()
|
||||
gtest.Assert(array1, array2)
|
||||
t.Assert(array1, array2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Get(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(array1.Get(2), 3)
|
||||
gtest.Assert(array1.Len(), 4)
|
||||
v, ok := array1.Get(2)
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array1.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Sum(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(array1.Sum(), 11)
|
||||
t.Assert(array1.Sum(), 11)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_CountValues(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5, 3}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
m1 := array1.CountValues()
|
||||
gtest.Assert(len(m1), 4)
|
||||
gtest.Assert(m1[1], 1)
|
||||
gtest.Assert(m1[3], 2)
|
||||
t.Assert(len(m1), 4)
|
||||
t.Assert(m1[1], 1)
|
||||
t.Assert(m1[3], 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewIntArrayFromCopy(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5, 3}
|
||||
array1 := garray.NewIntArrayFromCopy(a1)
|
||||
gtest.Assert(array1.Len(), 5)
|
||||
gtest.Assert(array1, a1)
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(array1, a1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Remove(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 5, 4}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
n1 := array1.Remove(1)
|
||||
gtest.Assert(n1, 2)
|
||||
gtest.Assert(array1.Len(), 4)
|
||||
v, ok := array1.Remove(1)
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array1.Len(), 4)
|
||||
|
||||
n1 = array1.Remove(0)
|
||||
gtest.Assert(n1, 1)
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
v, ok = array1.Remove(0)
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array1.Len(), 3)
|
||||
|
||||
n1 = array1.Remove(2)
|
||||
gtest.Assert(n1, 4)
|
||||
gtest.Assert(array1.Len(), 2)
|
||||
v, ok = array1.Remove(2)
|
||||
t.Assert(v, 4)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array1.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
a1 := garray.NewIntArrayFrom(s1, true)
|
||||
|
||||
@ -396,26 +554,26 @@ func TestIntArray_LockFunc(t *testing.T) {
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
gtest.Assert(a1.Contains(6), true)
|
||||
t.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
t.Assert(a1.Contains(6), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_SortFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
a1 := garray.NewIntArrayFrom(s1)
|
||||
func1 := func(v1, v2 int) bool {
|
||||
return v1 < v2
|
||||
}
|
||||
a11 := a1.SortFunc(func1)
|
||||
gtest.Assert(a11, []int{1, 2, 3, 4})
|
||||
t.Assert(a11, []int{1, 2, 3, 4})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_RLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
a1 := garray.NewIntArrayFrom(s1, true)
|
||||
|
||||
@ -441,31 +599,50 @@ func TestIntArray_RLockFunc(t *testing.T) {
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
gtest.Assert(a1.Contains(6), true)
|
||||
t.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
t.Assert(a1.Contains(6), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Json(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
a1 := garray.NewIntArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
gtest.Assert(b1, b2)
|
||||
gtest.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewIntArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
gtest.Assert(a2.Slice(), s1)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.IntArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(a3.Slice(), s1)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
a1 := *garray.NewIntArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
a2 := garray.NewIntArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.IntArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores *garray.IntArray
|
||||
@ -475,59 +652,140 @@ func TestIntArray_Json(t *testing.T) {
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.Name, data["Name"])
|
||||
gtest.Assert(user.Scores, data["Scores"])
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.IntArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Iterator(t *testing.T) {
|
||||
slice := g.SliceInt{10, 20, 30, 40}
|
||||
array := garray.NewIntArrayFrom(slice)
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.Iterator(func(k int, v int) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorAsc(func(k int, v int) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorDesc(func(k int, v int) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.Iterator(func(k int, v int) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorAsc(func(k int, v int) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorDesc(func(k int, v int) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_RemoveValue(t *testing.T) {
|
||||
slice := g.SliceInt{10, 20, 30, 40}
|
||||
array := garray.NewIntArrayFrom(slice)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(array.RemoveValue(99), false)
|
||||
t.Assert(array.RemoveValue(20), true)
|
||||
t.Assert(array.RemoveValue(10), true)
|
||||
t.Assert(array.RemoveValue(20), false)
|
||||
t.Assert(array.RemoveValue(88), false)
|
||||
t.Assert(array.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.IntArray
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`[1,2,3]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.Slice{1, 2, 3},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{0, 1, 2, 3, 4, 0})
|
||||
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3, 4})
|
||||
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2})
|
||||
t.Assert(array.Walk(func(value int) int {
|
||||
return 10 + value
|
||||
}), g.Slice{11, 12})
|
||||
})
|
||||
}
|
||||
|
||||
@ -21,36 +21,61 @@ import (
|
||||
)
|
||||
|
||||
func Test_StrArray_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []string{"0", "1", "2", "3"}
|
||||
array := garray.NewStrArrayFrom(expect)
|
||||
array2 := garray.NewStrArrayFrom(expect, true)
|
||||
array3 := garray.NewStrArrayFrom([]string{})
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.Assert(array.Interfaces(), expect)
|
||||
t.Assert(array.Slice(), expect)
|
||||
t.Assert(array.Interfaces(), expect)
|
||||
array.Set(0, "100")
|
||||
gtest.Assert(array.Get(0), 100)
|
||||
gtest.Assert(array.Get(1), 1)
|
||||
gtest.Assert(array.Search("100"), 0)
|
||||
gtest.Assert(array.Contains("100"), true)
|
||||
gtest.Assert(array.Remove(0), 100)
|
||||
gtest.Assert(array.Contains("100"), false)
|
||||
|
||||
v, ok := array.Get(0)
|
||||
t.Assert(v, 100)
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array.Search("100"), 0)
|
||||
t.Assert(array.Contains("100"), true)
|
||||
|
||||
v, ok = array.Remove(0)
|
||||
t.Assert(v, 100)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.Remove(-1)
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
|
||||
v, ok = array.Remove(100000)
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
|
||||
t.Assert(array.Contains("100"), false)
|
||||
array.Append("4")
|
||||
gtest.Assert(array.Len(), 4)
|
||||
t.Assert(array.Len(), 4)
|
||||
array.InsertBefore(0, "100")
|
||||
array.InsertAfter(0, "200")
|
||||
gtest.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "4"})
|
||||
t.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "4"})
|
||||
array.InsertBefore(5, "300")
|
||||
array.InsertAfter(6, "400")
|
||||
gtest.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "300", "4", "400"})
|
||||
gtest.Assert(array.Clear().Len(), 0)
|
||||
gtest.Assert(array2.Slice(), expect)
|
||||
gtest.Assert(array3.Search("100"), -1)
|
||||
t.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "300", "4", "400"})
|
||||
t.Assert(array.Clear().Len(), 0)
|
||||
t.Assert(array2.Slice(), expect)
|
||||
t.Assert(array3.Search("100"), -1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_ContainsI(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := garray.NewStrArray()
|
||||
s.Append("a", "b", "C")
|
||||
t.Assert(s.Contains("A"), false)
|
||||
t.Assert(s.Contains("a"), true)
|
||||
t.Assert(s.ContainsI("A"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Sort(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect1 := []string{"0", "1", "2", "3"}
|
||||
expect2 := []string{"3", "2", "1", "0"}
|
||||
array := garray.NewStrArray()
|
||||
@ -58,73 +83,167 @@ func TestStrArray_Sort(t *testing.T) {
|
||||
array.Append(gconv.String(i))
|
||||
}
|
||||
array.Sort()
|
||||
gtest.Assert(array.Slice(), expect1)
|
||||
t.Assert(array.Slice(), expect1)
|
||||
array.Sort(true)
|
||||
gtest.Assert(array.Slice(), expect2)
|
||||
t.Assert(array.Slice(), expect2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Unique(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []string{"1", "1", "2", "3"}
|
||||
array := garray.NewStrArrayFrom(expect)
|
||||
gtest.Assert(array.Unique().Slice(), []string{"1", "2", "3"})
|
||||
t.Assert(array.Unique().Slice(), []string{"1", "2", "3"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PushAndPop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []string{"0", "1", "2", "3"}
|
||||
array := garray.NewStrArrayFrom(expect)
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.Assert(array.PopLeft(), "0")
|
||||
gtest.Assert(array.PopRight(), "3")
|
||||
gtest.AssertIN(array.PopRand(), []string{"1", "2"})
|
||||
gtest.AssertIN(array.PopRand(), []string{"1", "2"})
|
||||
gtest.Assert(array.Len(), 0)
|
||||
t.Assert(array.Slice(), expect)
|
||||
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, "0")
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, "3")
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.AssertIN(v, []string{"1", "2"})
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.AssertIN(v, []string{"1", "2"})
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
|
||||
t.Assert(array.Len(), 0)
|
||||
array.PushLeft("1").PushRight("2")
|
||||
gtest.Assert(array.Slice(), []string{"1", "2"})
|
||||
t.Assert(array.Slice(), []string{"1", "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopLeft(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopRight(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopLefts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
t.Assert(array.PopLefts(2), g.Slice{"1", "2"})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{"3"})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
t.Assert(array.PopRights(2), g.Slice{"2", "3"})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{"1"})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopLeftsAndPopRights(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArray()
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopLefts(10), nil)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopRights(10), nil)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopRands(10), nil)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
value1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
value2 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(value1)
|
||||
array2 := garray.NewStrArrayFrom(value2)
|
||||
gtest.Assert(array1.PopLefts(2), []interface{}{"0", "1"})
|
||||
gtest.Assert(array1.Slice(), []interface{}{"2", "3", "4", "5", "6"})
|
||||
gtest.Assert(array1.PopRights(2), []interface{}{"5", "6"})
|
||||
gtest.Assert(array1.Slice(), []interface{}{"2", "3", "4"})
|
||||
gtest.Assert(array1.PopRights(20), []interface{}{"2", "3", "4"})
|
||||
gtest.Assert(array1.Slice(), []interface{}{})
|
||||
gtest.Assert(array2.PopLefts(20), []interface{}{"0", "1", "2", "3", "4", "5", "6"})
|
||||
gtest.Assert(array2.Slice(), []interface{}{})
|
||||
t.Assert(array1.PopLefts(2), []interface{}{"0", "1"})
|
||||
t.Assert(array1.Slice(), []interface{}{"2", "3", "4", "5", "6"})
|
||||
t.Assert(array1.PopRights(2), []interface{}{"5", "6"})
|
||||
t.Assert(array1.Slice(), []interface{}{"2", "3", "4"})
|
||||
t.Assert(array1.PopRights(20), []interface{}{"2", "3", "4"})
|
||||
t.Assert(array1.Slice(), []interface{}{})
|
||||
t.Assert(array2.PopLefts(20), []interface{}{"0", "1", "2", "3", "4", "5", "6"})
|
||||
t.Assert(array2.Slice(), []interface{}{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestString_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
value1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(value1)
|
||||
array2 := garray.NewStrArrayFrom(value1, true)
|
||||
gtest.Assert(array1.Range(0, 1), []interface{}{"0"})
|
||||
gtest.Assert(array1.Range(1, 2), []interface{}{"1"})
|
||||
gtest.Assert(array1.Range(0, 2), []interface{}{"0", "1"})
|
||||
gtest.Assert(array1.Range(-1, 10), value1)
|
||||
gtest.Assert(array1.Range(10, 1), nil)
|
||||
gtest.Assert(array2.Range(0, 1), []interface{}{"0"})
|
||||
t.Assert(array1.Range(0, 1), []interface{}{"0"})
|
||||
t.Assert(array1.Range(1, 2), []interface{}{"1"})
|
||||
t.Assert(array1.Range(0, 2), []interface{}{"0", "1"})
|
||||
t.Assert(array1.Range(-1, 10), value1)
|
||||
t.Assert(array1.Range(10, 1), nil)
|
||||
t.Assert(array2.Range(0, 1), []interface{}{"0"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a11 := []string{"0", "1", "2", "3"}
|
||||
a21 := []string{"4", "5", "6", "7"}
|
||||
array1 := garray.NewStrArrayFrom(a11)
|
||||
array2 := garray.NewStrArrayFrom(a21)
|
||||
gtest.Assert(array1.Merge(array2).Slice(), []string{"0", "1", "2", "3", "4", "5", "6", "7"})
|
||||
t.Assert(array1.Merge(array2).Slice(), []string{"0", "1", "2", "3", "4", "5", "6", "7"})
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
@ -143,239 +262,273 @@ func TestStrArray_Merge(t *testing.T) {
|
||||
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
a1 := garray.NewStrArrayFrom(s1)
|
||||
|
||||
gtest.Assert(a1.Merge(s2).Len(), 6)
|
||||
gtest.Assert(a1.Merge(i1).Len(), 9)
|
||||
gtest.Assert(a1.Merge(i2).Len(), 10)
|
||||
gtest.Assert(a1.Merge(s3).Len(), 12)
|
||||
gtest.Assert(a1.Merge(s4).Len(), 14)
|
||||
gtest.Assert(a1.Merge(s5).Len(), 16)
|
||||
gtest.Assert(a1.Merge(s6).Len(), 19)
|
||||
t.Assert(a1.Merge(s2).Len(), 6)
|
||||
t.Assert(a1.Merge(i1).Len(), 9)
|
||||
t.Assert(a1.Merge(i2).Len(), 10)
|
||||
t.Assert(a1.Merge(s3).Len(), 12)
|
||||
t.Assert(a1.Merge(s4).Len(), 14)
|
||||
t.Assert(a1.Merge(s5).Len(), 16)
|
||||
t.Assert(a1.Merge(s6).Len(), 19)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Fill(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0"}
|
||||
a2 := []string{"0"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
array2 := garray.NewStrArrayFrom(a2)
|
||||
gtest.Assert(array1.Fill(1, 2, "100").Slice(), []string{"0", "100", "100"})
|
||||
gtest.Assert(array2.Fill(0, 2, "100").Slice(), []string{"100", "100"})
|
||||
s1 := array2.Fill(-1, 2, "100")
|
||||
gtest.Assert(s1.Len(), 2)
|
||||
t.Assert(array1.Fill(1, 2, "100"), nil)
|
||||
t.Assert(array1.Slice(), []string{"0", "100", "100"})
|
||||
t.Assert(array2.Fill(0, 2, "100"), nil)
|
||||
t.Assert(array2.Slice(), []string{"100", "100"})
|
||||
t.AssertNE(array2.Fill(-1, 2, "100"), nil)
|
||||
t.Assert(array2.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Chunk(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
gtest.Assert(len(chunks), 3)
|
||||
gtest.Assert(chunks[0], []string{"1", "2"})
|
||||
gtest.Assert(chunks[1], []string{"3", "4"})
|
||||
gtest.Assert(chunks[2], []string{"5"})
|
||||
gtest.Assert(len(array1.Chunk(0)), 0)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []string{"1", "2"})
|
||||
t.Assert(chunks[1], []string{"3", "4"})
|
||||
t.Assert(chunks[2], []string{"5"})
|
||||
t.Assert(len(array1.Chunk(0)), 0)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []string{"1", "2", "3"})
|
||||
t.Assert(chunks[1], []string{"4", "5"})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []string{"1", "2"})
|
||||
t.Assert(chunks[1], []string{"3", "4"})
|
||||
t.Assert(chunks[2], []string{"5", "6"})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []string{"1", "2", "3"})
|
||||
t.Assert(chunks[1], []string{"4", "5", "6"})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Pad(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Pad(3, "1").Slice(), []string{"0", "1", "1"})
|
||||
gtest.Assert(array1.Pad(-4, "1").Slice(), []string{"1", "0", "1", "1"})
|
||||
gtest.Assert(array1.Pad(3, "1").Slice(), []string{"1", "0", "1", "1"})
|
||||
t.Assert(array1.Pad(3, "1").Slice(), []string{"0", "1", "1"})
|
||||
t.Assert(array1.Pad(-4, "1").Slice(), []string{"1", "0", "1", "1"})
|
||||
t.Assert(array1.Pad(3, "1").Slice(), []string{"1", "0", "1", "1"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
array2 := garray.NewStrArrayFrom(a1, true)
|
||||
gtest.Assert(array1.SubSlice(0, 2), []string{"0", "1"})
|
||||
gtest.Assert(array1.SubSlice(2, 2), []string{"2", "3"})
|
||||
gtest.Assert(array1.SubSlice(5, 8), []string{"5", "6"})
|
||||
gtest.Assert(array1.SubSlice(8, 2), nil)
|
||||
gtest.Assert(array1.SubSlice(1, -2), nil)
|
||||
gtest.Assert(array1.SubSlice(-5, 2), []string{"2", "3"})
|
||||
gtest.Assert(array1.SubSlice(-10, 1), nil)
|
||||
gtest.Assert(array2.SubSlice(0, 2), []string{"0", "1"})
|
||||
t.Assert(array1.SubSlice(0, 2), []string{"0", "1"})
|
||||
t.Assert(array1.SubSlice(2, 2), []string{"2", "3"})
|
||||
t.Assert(array1.SubSlice(5, 8), []string{"5", "6"})
|
||||
t.Assert(array1.SubSlice(8, 2), nil)
|
||||
t.Assert(array1.SubSlice(1, -2), nil)
|
||||
t.Assert(array1.SubSlice(-5, 2), []string{"2", "3"})
|
||||
t.Assert(array1.SubSlice(-10, 1), nil)
|
||||
t.Assert(array2.SubSlice(0, 2), []string{"0", "1"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Rand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
gtest.Assert(len(array1.Rands(2)), "2")
|
||||
gtest.Assert(len(array1.Rands(10)), "7")
|
||||
gtest.AssertIN(array1.Rands(1)[0], a1)
|
||||
gtest.Assert(len(array1.Rand()), 1)
|
||||
gtest.AssertIN(array1.Rand(), a1)
|
||||
t.Assert(len(array1.Rands(2)), "2")
|
||||
t.Assert(len(array1.Rands(10)), 10)
|
||||
t.AssertIN(array1.Rands(1)[0], a1)
|
||||
v, ok := array1.Rand()
|
||||
t.Assert(ok, true)
|
||||
t.AssertIN(v, a1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopRands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "b", "c", "d", "e", "f", "g"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
gtest.AssertIN(array1.PopRands(1), []string{"a", "b", "c", "d", "e", "f", "g"})
|
||||
gtest.AssertIN(array1.PopRands(1), []string{"a", "b", "c", "d", "e", "f", "g"})
|
||||
gtest.AssertNI(array1.PopRands(1), array1.Slice())
|
||||
gtest.AssertNI(array1.PopRands(1), array1.Slice())
|
||||
gtest.Assert(len(array1.PopRands(10)), 3)
|
||||
t.AssertIN(array1.PopRands(1), []string{"a", "b", "c", "d", "e", "f", "g"})
|
||||
t.AssertIN(array1.PopRands(1), []string{"a", "b", "c", "d", "e", "f", "g"})
|
||||
t.AssertNI(array1.PopRands(1), array1.Slice())
|
||||
t.AssertNI(array1.PopRands(1), array1.Slice())
|
||||
t.Assert(len(array1.PopRands(10)), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Shuffle(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Shuffle().Len(), 7)
|
||||
t.Assert(array1.Shuffle().Len(), 7)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Reverse(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Reverse().Slice(), []string{"6", "5", "4", "3", "2", "1", "0"})
|
||||
t.Assert(array1.Reverse().Slice(), []string{"6", "5", "4", "3", "2", "1", "0"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Join(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Join("."), `0.1.2.3.4.5.6`)
|
||||
t.Assert(array1.Join("."), `0.1.2.3.4.5.6`)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", `"a"`, `\a`}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Join("."), `0.1."a".\a`)
|
||||
t.Assert(array1.Join("."), `0.1."a".\a`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_String(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
gtest.Assert(array1.String(), `["0","1","2","3","4","5","6"]`)
|
||||
t.Assert(array1.String(), `["0","1","2","3","4","5","6"]`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewStrArrayFromCopy(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
a2 := garray.NewStrArrayFromCopy(a1)
|
||||
a3 := garray.NewStrArrayFromCopy(a1, true)
|
||||
gtest.Assert(a2.Contains("1"), true)
|
||||
gtest.Assert(a2.Len(), 7)
|
||||
gtest.Assert(a2, a3)
|
||||
t.Assert(a2.Contains("1"), true)
|
||||
t.Assert(a2.Len(), 7)
|
||||
t.Assert(a2, a3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_SetArray(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
a2 := []string{"a", "b", "c", "d"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Contains("2"), true)
|
||||
gtest.Assert(array1.Len(), 7)
|
||||
t.Assert(array1.Contains("2"), true)
|
||||
t.Assert(array1.Len(), 7)
|
||||
|
||||
array1 = array1.SetArray(a2)
|
||||
gtest.Assert(array1.Contains("2"), false)
|
||||
gtest.Assert(array1.Contains("c"), true)
|
||||
gtest.Assert(array1.Len(), 4)
|
||||
t.Assert(array1.Contains("2"), false)
|
||||
t.Assert(array1.Contains("c"), true)
|
||||
t.Assert(array1.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Replace(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
a2 := []string{"a", "b", "c", "d"}
|
||||
a3 := []string{"o", "p", "q", "x", "y", "z", "w", "r", "v"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Contains("2"), true)
|
||||
gtest.Assert(array1.Len(), 7)
|
||||
t.Assert(array1.Contains("2"), true)
|
||||
t.Assert(array1.Len(), 7)
|
||||
|
||||
array1 = array1.Replace(a2)
|
||||
gtest.Assert(array1.Contains("2"), false)
|
||||
gtest.Assert(array1.Contains("c"), true)
|
||||
gtest.Assert(array1.Contains("5"), true)
|
||||
gtest.Assert(array1.Len(), 7)
|
||||
t.Assert(array1.Contains("2"), false)
|
||||
t.Assert(array1.Contains("c"), true)
|
||||
t.Assert(array1.Contains("5"), true)
|
||||
t.Assert(array1.Len(), 7)
|
||||
|
||||
array1 = array1.Replace(a3)
|
||||
gtest.Assert(array1.Contains("2"), false)
|
||||
gtest.Assert(array1.Contains("c"), false)
|
||||
gtest.Assert(array1.Contains("5"), false)
|
||||
gtest.Assert(array1.Contains("p"), true)
|
||||
gtest.Assert(array1.Contains("r"), false)
|
||||
gtest.Assert(array1.Len(), 7)
|
||||
t.Assert(array1.Contains("2"), false)
|
||||
t.Assert(array1.Contains("c"), false)
|
||||
t.Assert(array1.Contains("5"), false)
|
||||
t.Assert(array1.Contains("p"), true)
|
||||
t.Assert(array1.Contains("r"), false)
|
||||
t.Assert(array1.Len(), 7)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Sum(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
a2 := []string{"0", "a", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
array2 := garray.NewStrArrayFrom(a2)
|
||||
gtest.Assert(array1.Sum(), 21)
|
||||
gtest.Assert(array2.Sum(), 18)
|
||||
t.Assert(array1.Sum(), 21)
|
||||
t.Assert(array2.Sum(), 18)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopRand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
str1 := array1.PopRand()
|
||||
gtest.Assert(strings.Contains("0,1,2,3,4,5,6", str1), true)
|
||||
gtest.Assert(array1.Len(), 6)
|
||||
str1, ok := array1.PopRand()
|
||||
t.Assert(strings.Contains("0,1,2,3,4,5,6", str1), true)
|
||||
t.Assert(array1.Len(), 6)
|
||||
t.Assert(ok, true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Clone(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
array2 := array1.Clone()
|
||||
gtest.Assert(array2, array1)
|
||||
gtest.Assert(array2.Len(), 7)
|
||||
t.Assert(array2, array1)
|
||||
t.Assert(array2.Len(), 7)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_CountValues(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "4", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
|
||||
m1 := array1.CountValues()
|
||||
gtest.Assert(len(m1), 6)
|
||||
gtest.Assert(m1["2"], 1)
|
||||
gtest.Assert(m1["4"], 2)
|
||||
t.Assert(len(m1), 6)
|
||||
t.Assert(m1["2"], 1)
|
||||
t.Assert(m1["4"], 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Remove(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "a", "c"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
s1 := array1.Remove(1)
|
||||
gtest.Assert(s1, "a")
|
||||
gtest.Assert(array1.Len(), 4)
|
||||
s1 = array1.Remove(3)
|
||||
gtest.Assert(s1, "c")
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
s1, ok := array1.Remove(1)
|
||||
t.Assert(s1, "a")
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array1.Len(), 4)
|
||||
s1, ok = array1.Remove(3)
|
||||
t.Assert(s1, "c")
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array1.Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_RLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
a1 := garray.NewStrArrayFrom(s1, true)
|
||||
|
||||
@ -401,25 +554,25 @@ func TestStrArray_RLockFunc(t *testing.T) {
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
t.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_SortFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "d", "c", "b"}
|
||||
a1 := garray.NewStrArrayFrom(s1)
|
||||
func1 := func(v1, v2 string) bool {
|
||||
return v1 < v2
|
||||
}
|
||||
a11 := a1.SortFunc(func1)
|
||||
gtest.Assert(a11, []string{"a", "b", "c", "d"})
|
||||
t.Assert(a11, []string{"a", "b", "c", "d"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
a1 := garray.NewStrArrayFrom(s1, true)
|
||||
|
||||
@ -445,31 +598,50 @@ func TestStrArray_LockFunc(t *testing.T) {
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
t.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Json(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "d", "c"}
|
||||
a1 := garray.NewStrArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
gtest.Assert(b1, b2)
|
||||
gtest.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewStrArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
gtest.Assert(a2.Slice(), s1)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.StrArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(a3.Slice(), s1)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "d", "c"}
|
||||
a1 := *garray.NewStrArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
a2 := garray.NewStrArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.StrArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores *garray.StrArray
|
||||
@ -479,59 +651,139 @@ func TestStrArray_Json(t *testing.T) {
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.Name, data["Name"])
|
||||
gtest.Assert(user.Scores, data["Scores"])
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.StrArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Iterator(t *testing.T) {
|
||||
slice := g.SliceStr{"a", "b", "d", "c"}
|
||||
array := garray.NewStrArrayFrom(slice)
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.Iterator(func(k int, v string) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorAsc(func(k int, v string) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorDesc(func(k int, v string) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.Iterator(func(k int, v string) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorAsc(func(k int, v string) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorDesc(func(k int, v string) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_RemoveValue(t *testing.T) {
|
||||
slice := g.SliceStr{"a", "b", "d", "c"}
|
||||
array := garray.NewStrArrayFrom(slice)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(array.RemoveValue("e"), false)
|
||||
t.Assert(array.RemoveValue("b"), true)
|
||||
t.Assert(array.RemoveValue("a"), true)
|
||||
t.Assert(array.RemoveValue("c"), true)
|
||||
t.Assert(array.RemoveValue("f"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.StrArray
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`["1","2","3"]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.SliceStr{"1", "2", "3"},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"", "1", "2", "0"})
|
||||
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2", "0"})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2"})
|
||||
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2"})
|
||||
t.Assert(array.Walk(func(value string) string {
|
||||
return "key-" + value
|
||||
}), g.Slice{"key-1", "key-2"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
func TestSortedArray_NewSortedArrayFrom(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "f", "c"}
|
||||
a2 := []interface{}{"h", "j", "i", "k"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
@ -34,16 +34,16 @@ func TestSortedArray_NewSortedArrayFrom(t *testing.T) {
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array2 := garray.NewSortedArrayFrom(a2, func2)
|
||||
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1, []interface{}{"a", "c", "f"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"a", "c", "f"})
|
||||
|
||||
gtest.Assert(array2.Len(), 4)
|
||||
gtest.Assert(array2, []interface{}{"k", "i", "j", "h"})
|
||||
t.Assert(array2.Len(), 4)
|
||||
t.Assert(array2, []interface{}{"k", "i", "j", "h"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewSortedArrayFromCopy(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "f", "c"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
@ -54,15 +54,15 @@ func TestNewSortedArrayFromCopy(t *testing.T) {
|
||||
}
|
||||
array1 := garray.NewSortedArrayFromCopy(a1, func1)
|
||||
array2 := garray.NewSortedArrayFromCopy(a1, func2)
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1, []interface{}{"a", "c", "f"})
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array2, []interface{}{"c", "f", "a"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"a", "c", "f"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array2, []interface{}{"c", "f", "a"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_SetArray(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "f", "c"}
|
||||
a2 := []interface{}{"e", "h", "g", "k"}
|
||||
|
||||
@ -72,168 +72,234 @@ func TestSortedArray_SetArray(t *testing.T) {
|
||||
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array1.SetArray(a2)
|
||||
gtest.Assert(array1.Len(), 4)
|
||||
gtest.Assert(array1, []interface{}{"e", "g", "h", "k"})
|
||||
t.Assert(array1.Len(), 4)
|
||||
t.Assert(array1, []interface{}{"e", "g", "h", "k"})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSortedArray_Sort(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "f", "c"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array1.Sort()
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1, []interface{}{"a", "c", "f"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"a", "c", "f"})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSortedArray_Get(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "f", "c"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
gtest.Assert(array1.Get(2), "f")
|
||||
gtest.Assert(array1.Get(1), "c")
|
||||
v, ok := array1.Get(2)
|
||||
t.Assert(v, "f")
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array1.Get(1)
|
||||
t.Assert(v, "c")
|
||||
t.Assert(ok, true)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSortedArray_Remove(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.Remove(1)
|
||||
gtest.Assert(gconv.String(i1), "b")
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1.Contains("b"), false)
|
||||
i1, ok := array1.Remove(1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(gconv.String(i1), "b")
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1.Contains("b"), false)
|
||||
|
||||
i2 := array1.Remove(0)
|
||||
gtest.Assert(gconv.String(i2), "a")
|
||||
gtest.Assert(array1.Len(), 2)
|
||||
gtest.Assert(array1.Contains("a"), false)
|
||||
v, ok := array1.Remove(-1)
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
|
||||
i3 := array1.Remove(1)
|
||||
gtest.Assert(gconv.String(i3), "d")
|
||||
gtest.Assert(array1.Len(), 1)
|
||||
gtest.Assert(array1.Contains("d"), false)
|
||||
v, ok = array1.Remove(100000)
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
|
||||
i2, ok := array1.Remove(0)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(gconv.String(i2), "a")
|
||||
t.Assert(array1.Len(), 2)
|
||||
t.Assert(array1.Contains("a"), false)
|
||||
|
||||
i3, ok := array1.Remove(1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(gconv.String(i3), "d")
|
||||
t.Assert(array1.Len(), 1)
|
||||
t.Assert(array1.Contains("d"), false)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSortedArray_PopLeft(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{"a", "d", "c", "b"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.PopLeft()
|
||||
gtest.Assert(gconv.String(i1), "a")
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1, []interface{}{"b", "c", "d"})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array1 := garray.NewSortedArrayFrom(
|
||||
[]interface{}{"a", "d", "c", "b"},
|
||||
gutil.ComparatorString,
|
||||
)
|
||||
i1, ok := array1.PopLeft()
|
||||
t.Assert(ok, true)
|
||||
t.Assert(gconv.String(i1), "a")
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"b", "c", "d"})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3}, gutil.ComparatorInt)
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSortedArray_PopRight(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{"a", "d", "c", "b"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.PopRight()
|
||||
gtest.Assert(gconv.String(i1), "d")
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1, []interface{}{"a", "b", "c"})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array1 := garray.NewSortedArrayFrom(
|
||||
[]interface{}{"a", "d", "c", "b"},
|
||||
gutil.ComparatorString,
|
||||
)
|
||||
i1, ok := array1.PopRight()
|
||||
t.Assert(ok, true)
|
||||
t.Assert(gconv.String(i1), "d")
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"a", "b", "c"})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3}, gutil.ComparatorInt)
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_PopRand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.PopRand()
|
||||
gtest.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
i1, ok := array1.PopRand()
|
||||
t.Assert(ok, true)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_PopRands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.PopRands(2)
|
||||
gtest.Assert(len(i1), 2)
|
||||
gtest.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
|
||||
gtest.Assert(array1.Len(), 2)
|
||||
t.Assert(len(i1), 2)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c", "b"})
|
||||
t.Assert(array1.Len(), 2)
|
||||
|
||||
i2 := array1.PopRands(3)
|
||||
gtest.Assert(len(i1), 2)
|
||||
gtest.AssertIN(i2, []interface{}{"a", "d", "c", "b"})
|
||||
gtest.Assert(array1.Len(), 0)
|
||||
t.Assert(len(i1), 2)
|
||||
t.AssertIN(i2, []interface{}{"a", "d", "c", "b"})
|
||||
t.Assert(array1.Len(), 0)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Empty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArray(gutil.ComparatorInt)
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopLefts(10), nil)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopRights(10), nil)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopRands(10), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_PopLefts(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.PopLefts(2)
|
||||
gtest.Assert(len(i1), 2)
|
||||
gtest.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
|
||||
gtest.Assert(array1.Len(), 4)
|
||||
t.Assert(len(i1), 2)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
|
||||
t.Assert(array1.Len(), 4)
|
||||
|
||||
i2 := array1.PopLefts(5)
|
||||
gtest.Assert(len(i2), 4)
|
||||
gtest.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
|
||||
gtest.Assert(array1.Len(), 0)
|
||||
|
||||
t.Assert(len(i2), 4)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_PopRights(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.PopRights(2)
|
||||
gtest.Assert(len(i1), 2)
|
||||
gtest.Assert(i1, []interface{}{"e", "f"})
|
||||
gtest.Assert(array1.Len(), 4)
|
||||
t.Assert(len(i1), 2)
|
||||
t.Assert(i1, []interface{}{"e", "f"})
|
||||
t.Assert(array1.Len(), 4)
|
||||
|
||||
i2 := array1.PopRights(10)
|
||||
gtest.Assert(len(i2), 4)
|
||||
|
||||
t.Assert(len(i2), 4)
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
@ -241,25 +307,25 @@ func TestSortedArray_Range(t *testing.T) {
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array2 := garray.NewSortedArrayFrom(a1, func1, true)
|
||||
i1 := array1.Range(2, 5)
|
||||
gtest.Assert(i1, []interface{}{"c", "d", "e"})
|
||||
gtest.Assert(array1.Len(), 6)
|
||||
t.Assert(i1, []interface{}{"c", "d", "e"})
|
||||
t.Assert(array1.Len(), 6)
|
||||
|
||||
i2 := array1.Range(7, 5)
|
||||
gtest.Assert(len(i2), 0)
|
||||
t.Assert(len(i2), 0)
|
||||
i2 = array1.Range(-1, 2)
|
||||
gtest.Assert(i2, []interface{}{"a", "b"})
|
||||
t.Assert(i2, []interface{}{"a", "b"})
|
||||
|
||||
i2 = array1.Range(4, 10)
|
||||
gtest.Assert(len(i2), 2)
|
||||
gtest.Assert(i2, []interface{}{"e", "f"})
|
||||
t.Assert(len(i2), 2)
|
||||
t.Assert(i2, []interface{}{"e", "f"})
|
||||
|
||||
gtest.Assert(array2.Range(1, 3), []interface{}{"b", "c"})
|
||||
t.Assert(array2.Range(1, 3), []interface{}{"b", "c"})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Sum(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
|
||||
a2 := []interface{}{"1", "2", "3", "b", "e", "f"}
|
||||
a3 := []interface{}{"4", "5", "6"}
|
||||
@ -269,15 +335,15 @@ func TestSortedArray_Sum(t *testing.T) {
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array2 := garray.NewSortedArrayFrom(a2, func1)
|
||||
array3 := garray.NewSortedArrayFrom(a3, func1)
|
||||
gtest.Assert(array1.Sum(), 0)
|
||||
gtest.Assert(array2.Sum(), 6)
|
||||
gtest.Assert(array3.Sum(), 15)
|
||||
t.Assert(array1.Sum(), 0)
|
||||
t.Assert(array2.Sum(), 6)
|
||||
t.Assert(array3.Sum(), 15)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Clone(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
@ -285,30 +351,30 @@ func TestSortedArray_Clone(t *testing.T) {
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array2 := array1.Clone()
|
||||
gtest.Assert(array1, array2)
|
||||
t.Assert(array1, array2)
|
||||
array1.Remove(1)
|
||||
gtest.AssertNE(array1, array2)
|
||||
t.AssertNE(array1, array2)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Clear(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e", "f"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
gtest.Assert(array1.Len(), 6)
|
||||
t.Assert(array1.Len(), 6)
|
||||
array1.Clear()
|
||||
gtest.Assert(array1.Len(), 0)
|
||||
t.Assert(array1.Len(), 0)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Chunk(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
@ -316,17 +382,45 @@ func TestSortedArray_Chunk(t *testing.T) {
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.Chunk(2)
|
||||
gtest.Assert(len(i1), 3)
|
||||
gtest.Assert(i1[0], []interface{}{"a", "b"})
|
||||
gtest.Assert(i1[2], []interface{}{"e"})
|
||||
t.Assert(len(i1), 3)
|
||||
t.Assert(i1[0], []interface{}{"a", "b"})
|
||||
t.Assert(i1[2], []interface{}{"e"})
|
||||
|
||||
i1 = array1.Chunk(0)
|
||||
gtest.Assert(len(i1), 0)
|
||||
t.Assert(len(i1), 0)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []interface{}{1, 2, 3})
|
||||
t.Assert(chunks[1], []interface{}{4, 5})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []interface{}{1, 2})
|
||||
t.Assert(chunks[1], []interface{}{3, 4})
|
||||
t.Assert(chunks[2], []interface{}{5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []interface{}{1, 2, 3})
|
||||
t.Assert(chunks[1], []interface{}{4, 5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b", "e"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
@ -335,42 +429,43 @@ func TestSortedArray_SubSlice(t *testing.T) {
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array2 := garray.NewSortedArrayFrom(a1, func1, true)
|
||||
i1 := array1.SubSlice(2, 3)
|
||||
gtest.Assert(len(i1), 3)
|
||||
gtest.Assert(i1, []interface{}{"c", "d", "e"})
|
||||
t.Assert(len(i1), 3)
|
||||
t.Assert(i1, []interface{}{"c", "d", "e"})
|
||||
|
||||
i1 = array1.SubSlice(2, 6)
|
||||
gtest.Assert(len(i1), 3)
|
||||
gtest.Assert(i1, []interface{}{"c", "d", "e"})
|
||||
t.Assert(len(i1), 3)
|
||||
t.Assert(i1, []interface{}{"c", "d", "e"})
|
||||
|
||||
i1 = array1.SubSlice(7, 2)
|
||||
gtest.Assert(len(i1), 0)
|
||||
t.Assert(len(i1), 0)
|
||||
|
||||
s1 := array1.SubSlice(1, -2)
|
||||
gtest.Assert(s1, nil)
|
||||
t.Assert(s1, nil)
|
||||
|
||||
s1 = array1.SubSlice(-9, 2)
|
||||
gtest.Assert(s1, nil)
|
||||
gtest.Assert(array2.SubSlice(1, 3), []interface{}{"b", "c", "d"})
|
||||
t.Assert(s1, nil)
|
||||
t.Assert(array2.SubSlice(1, 3), []interface{}{"b", "c", "d"})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Rand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.Rand()
|
||||
gtest.AssertIN(i1, []interface{}{"a", "d", "c"})
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
i1, ok := array1.Rand()
|
||||
t.Assert(ok, true)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Rands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
@ -378,43 +473,43 @@ func TestSortedArray_Rands(t *testing.T) {
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
i1 := array1.Rands(2)
|
||||
gtest.AssertIN(i1, []interface{}{"a", "d", "c"})
|
||||
gtest.Assert(len(i1), 2)
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c"})
|
||||
t.Assert(len(i1), 2)
|
||||
t.Assert(array1.Len(), 3)
|
||||
|
||||
i1 = array1.Rands(4)
|
||||
gtest.Assert(len(i1), 3)
|
||||
t.Assert(len(i1), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Join(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
gtest.Assert(array1.Join(","), `a,c,d`)
|
||||
gtest.Assert(array1.Join("."), `a.c.d`)
|
||||
t.Assert(array1.Join(","), `a,c,d`)
|
||||
t.Assert(array1.Join("."), `a.c.d`)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, `"a"`, `\a`}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
|
||||
gtest.Assert(array1.Join("."), `"a".0.1.\a`)
|
||||
t.Assert(array1.Join("."), `"a".0.1.\a`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_String(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{0, 1, "a", "b"}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
|
||||
gtest.Assert(array1.String(), `[0,1,"a","b"]`)
|
||||
t.Assert(array1.String(), `[0,1,"a","b"]`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_CountValues(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "c"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
@ -422,15 +517,15 @@ func TestSortedArray_CountValues(t *testing.T) {
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
m1 := array1.CountValues()
|
||||
gtest.Assert(len(m1), 3)
|
||||
gtest.Assert(m1["c"], 2)
|
||||
gtest.Assert(m1["a"], 1)
|
||||
t.Assert(len(m1), 3)
|
||||
t.Assert(m1["c"], 2)
|
||||
t.Assert(m1["a"], 1)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_SetUnique(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "c"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
@ -438,13 +533,13 @@ func TestSortedArray_SetUnique(t *testing.T) {
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array1.SetUnique(true)
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1, []interface{}{"a", "c", "d"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"a", "c", "d"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
@ -473,13 +568,13 @@ func TestSortedArray_LockFunc(t *testing.T) {
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
t.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_RLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
@ -508,13 +603,13 @@ func TestSortedArray_RLockFunc(t *testing.T) {
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候不会被阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
t.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候不会被阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
@ -533,38 +628,59 @@ func TestSortedArray_Merge(t *testing.T) {
|
||||
|
||||
a1 := garray.NewSortedArrayFrom(s1, func1)
|
||||
|
||||
gtest.Assert(a1.Merge(s2).Len(), 6)
|
||||
gtest.Assert(a1.Merge(i1).Len(), 9)
|
||||
gtest.Assert(a1.Merge(i2).Len(), 10)
|
||||
gtest.Assert(a1.Merge(s3).Len(), 12)
|
||||
gtest.Assert(a1.Merge(s4).Len(), 14)
|
||||
gtest.Assert(a1.Merge(s5).Len(), 16)
|
||||
gtest.Assert(a1.Merge(s6).Len(), 19)
|
||||
t.Assert(a1.Merge(s2).Len(), 6)
|
||||
t.Assert(a1.Merge(i1).Len(), 9)
|
||||
t.Assert(a1.Merge(i2).Len(), 10)
|
||||
t.Assert(a1.Merge(s3).Len(), 12)
|
||||
t.Assert(a1.Merge(s4).Len(), 14)
|
||||
t.Assert(a1.Merge(s5).Len(), 16)
|
||||
t.Assert(a1.Merge(s6).Len(), 19)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Json(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
s2 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewSortedArrayFrom(s1, gutil.ComparatorString)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
gtest.Assert(b1, b2)
|
||||
gtest.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedArray(gutil.ComparatorString)
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
gtest.Assert(a2.Slice(), s2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(a3.Slice(), s1)
|
||||
gtest.Assert(a3.Interfaces(), s1)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
s2 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := *garray.NewSortedArrayFrom(s1, gutil.ComparatorString)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
a2 := garray.NewSortedArray(gutil.ComparatorString)
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores *garray.SortedArray
|
||||
@ -574,63 +690,185 @@ func TestSortedArray_Json(t *testing.T) {
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.Name, data["Name"])
|
||||
gtest.AssertNE(user.Scores, nil)
|
||||
gtest.Assert(user.Scores.Len(), 3)
|
||||
gtest.AssertIN(user.Scores.PopLeft(), data["Scores"])
|
||||
gtest.AssertIN(user.Scores.PopLeft(), data["Scores"])
|
||||
gtest.AssertIN(user.Scores.PopLeft(), data["Scores"])
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.AssertNE(user.Scores, nil)
|
||||
t.Assert(user.Scores.Len(), 3)
|
||||
|
||||
v, ok := user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.SortedArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.AssertNE(user.Scores, nil)
|
||||
t.Assert(user.Scores.Len(), 3)
|
||||
|
||||
v, ok := user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Iterator(t *testing.T) {
|
||||
slice := g.Slice{"a", "b", "d", "c"}
|
||||
array := garray.NewSortedArrayFrom(slice, gutil.ComparatorString)
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.Iterator(func(k int, v interface{}) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorAsc(func(k int, v interface{}) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorDesc(func(k int, v interface{}) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.Iterator(func(k int, v interface{}) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorAsc(func(k int, v interface{}) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorDesc(func(k int, v interface{}) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_RemoveValue(t *testing.T) {
|
||||
slice := g.Slice{"a", "b", "d", "c"}
|
||||
array := garray.NewSortedArrayFrom(slice, gutil.ComparatorString)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(array.RemoveValue("e"), false)
|
||||
t.Assert(array.RemoveValue("b"), true)
|
||||
t.Assert(array.RemoveValue("a"), true)
|
||||
t.Assert(array.RemoveValue("c"), true)
|
||||
t.Assert(array.RemoveValue("f"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.SortedArray
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`[2,3,1]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.Slice{2, 3, 1},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_FilterNil(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
values := g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}
|
||||
array := garray.NewSortedArrayFromCopy(values, gutil.ComparatorInt)
|
||||
t.Assert(array.FilterNil().Slice(), g.Slice{0, "", g.Slice{}, 1, 2, 3, 4})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFromCopy(g.Slice{nil, 1, 2, 3, 4, nil}, gutil.ComparatorInt)
|
||||
t.Assert(array.FilterNil(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{0, 1, 2, 3, 4, "", g.Slice{}}, gutil.ComparatorInt)
|
||||
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3, 4}, gutil.ComparatorInt)
|
||||
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{"1", "2"}, gutil.ComparatorString)
|
||||
t.Assert(array.Walk(func(value interface{}) interface{} {
|
||||
return "key-" + gconv.String(value)
|
||||
}), g.Slice{"key-1", "key-2"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -21,323 +21,431 @@ import (
|
||||
)
|
||||
|
||||
func TestNewSortedIntArrayFrom(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 3, 2, 1, 4, 5, 6}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1, true)
|
||||
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
|
||||
gtest.Assert(array1.Slice(), a1)
|
||||
gtest.Assert(array1.Interfaces(), a1)
|
||||
t.Assert(array1.Join("."), "0.1.2.3.4.5.6")
|
||||
t.Assert(array1.Slice(), a1)
|
||||
t.Assert(array1.Interfaces(), a1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewSortedIntArrayFromCopy(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 5, 2, 1, 4, 3, 6}
|
||||
array1 := garray.NewSortedIntArrayFromCopy(a1, false)
|
||||
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
|
||||
t.Assert(array1.Join("."), "0.1.2.3.4.5.6")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_SetArray(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 1, 2, 3}
|
||||
a2 := []int{4, 5, 6}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array2 := array1.SetArray(a2)
|
||||
|
||||
gtest.Assert(array2.Len(), 3)
|
||||
gtest.Assert(array2.Search(3), -1)
|
||||
gtest.Assert(array2.Search(5), 1)
|
||||
gtest.Assert(array2.Search(6), 2)
|
||||
t.Assert(array2.Len(), 3)
|
||||
t.Assert(array2.Search(3), -1)
|
||||
t.Assert(array2.Search(5), 1)
|
||||
t.Assert(array2.Search(6), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Sort(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 3, 2, 1}
|
||||
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array2 := array1.Sort()
|
||||
|
||||
gtest.Assert(array2.Len(), 4)
|
||||
gtest.Assert(array2, []int{0, 1, 2, 3})
|
||||
t.Assert(array2.Len(), 4)
|
||||
t.Assert(array2, []int{0, 1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Get(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 0}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
gtest.Assert(array1.Get(0), 0)
|
||||
gtest.Assert(array1.Get(1), 1)
|
||||
gtest.Assert(array1.Get(3), 5)
|
||||
v, ok := array1.Get(0)
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array1.Get(1)
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array1.Get(3)
|
||||
t.Assert(v, 5)
|
||||
t.Assert(ok, true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Remove(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 0}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
i1 := array1.Remove(2)
|
||||
gtest.Assert(i1, 3)
|
||||
gtest.Assert(array1.Search(5), 2)
|
||||
|
||||
// 再次删除剩下的数组中的第一个
|
||||
i2 := array1.Remove(0)
|
||||
gtest.Assert(i2, 0)
|
||||
gtest.Assert(array1.Search(5), 1)
|
||||
v, ok := array1.Remove(-1)
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
|
||||
v, ok = array1.Remove(-100000)
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
|
||||
v, ok = array1.Remove(2)
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array1.Search(5), 2)
|
||||
|
||||
v, ok = array1.Remove(0)
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array1.Search(5), 1)
|
||||
|
||||
a2 := []int{1, 3, 4}
|
||||
array2 := garray.NewSortedIntArrayFrom(a2)
|
||||
i3 := array2.Remove(1)
|
||||
gtest.Assert(array2.Search(1), 0)
|
||||
gtest.Assert(i3, 3)
|
||||
i3 = array2.Remove(1)
|
||||
gtest.Assert(array2.Search(4), -1)
|
||||
gtest.Assert(i3, 4)
|
||||
|
||||
v, ok = array2.Remove(1)
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array2.Search(1), 0)
|
||||
|
||||
v, ok = array2.Remove(1)
|
||||
t.Assert(v, 4)
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array2.Search(4), -1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_PopLeft(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 2}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
i1 := array1.PopLeft()
|
||||
gtest.Assert(i1, 1)
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1.Search(1), -1)
|
||||
v, ok := array1.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1.Search(1), -1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_PopRight(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 2}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
i1 := array1.PopRight()
|
||||
gtest.Assert(i1, 5)
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1.Search(5), -1)
|
||||
v, ok := array1.PopRight()
|
||||
t.Assert(v, 5)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1.Search(5), -1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_PopRand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 2}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
i1 := array1.PopRand()
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1.Search(i1), -1)
|
||||
gtest.AssertIN(i1, []int{1, 3, 5, 2})
|
||||
i1, ok := array1.PopRand()
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1.Search(i1), -1)
|
||||
t.AssertIN(i1, []int{1, 3, 5, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_PopRands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 2}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
ns1 := array1.PopRands(2)
|
||||
gtest.Assert(array1.Len(), 2)
|
||||
gtest.AssertIN(ns1, []int{1, 3, 5, 2})
|
||||
t.Assert(array1.Len(), 2)
|
||||
t.AssertIN(ns1, []int{1, 3, 5, 2})
|
||||
|
||||
a2 := []int{1, 3, 5, 2}
|
||||
array2 := garray.NewSortedIntArrayFrom(a2)
|
||||
ns2 := array2.PopRands(5)
|
||||
gtest.Assert(array2.Len(), 0)
|
||||
gtest.Assert(len(ns2), 4)
|
||||
gtest.AssertIN(ns2, []int{1, 3, 5, 2})
|
||||
t.Assert(array2.Len(), 0)
|
||||
t.Assert(len(ns2), 4)
|
||||
t.AssertIN(ns2, []int{1, 3, 5, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Empty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArray()
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopLefts(10), nil)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopRights(10), nil)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopRands(10), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_PopLefts(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 2}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
ns1 := array1.PopLefts(2)
|
||||
gtest.Assert(array1.Len(), 2)
|
||||
gtest.Assert(ns1, []int{1, 2})
|
||||
t.Assert(array1.Len(), 2)
|
||||
t.Assert(ns1, []int{1, 2})
|
||||
|
||||
a2 := []int{1, 3, 5, 2}
|
||||
array2 := garray.NewSortedIntArrayFrom(a2)
|
||||
ns2 := array2.PopLefts(5)
|
||||
gtest.Assert(array2.Len(), 0)
|
||||
gtest.AssertIN(ns2, []int{1, 3, 5, 2})
|
||||
t.Assert(array2.Len(), 0)
|
||||
t.AssertIN(ns2, []int{1, 3, 5, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_PopRights(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 2}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
ns1 := array1.PopRights(2)
|
||||
gtest.Assert(array1.Len(), 2)
|
||||
gtest.Assert(ns1, []int{3, 5})
|
||||
t.Assert(array1.Len(), 2)
|
||||
t.Assert(ns1, []int{3, 5})
|
||||
|
||||
a2 := []int{1, 3, 5, 2}
|
||||
array2 := garray.NewSortedIntArrayFrom(a2)
|
||||
ns2 := array2.PopRights(5)
|
||||
gtest.Assert(array2.Len(), 0)
|
||||
gtest.AssertIN(ns2, []int{1, 3, 5, 2})
|
||||
t.Assert(array2.Len(), 0)
|
||||
t.AssertIN(ns2, []int{1, 3, 5, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5, 2, 6, 7}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array2 := garray.NewSortedIntArrayFrom(a1, true)
|
||||
ns1 := array1.Range(1, 4)
|
||||
gtest.Assert(len(ns1), 3)
|
||||
gtest.Assert(ns1, []int{2, 3, 5})
|
||||
t.Assert(len(ns1), 3)
|
||||
t.Assert(ns1, []int{2, 3, 5})
|
||||
|
||||
ns2 := array1.Range(5, 4)
|
||||
gtest.Assert(len(ns2), 0)
|
||||
t.Assert(len(ns2), 0)
|
||||
|
||||
ns3 := array1.Range(-1, 4)
|
||||
gtest.Assert(len(ns3), 4)
|
||||
t.Assert(len(ns3), 4)
|
||||
|
||||
nsl := array1.Range(5, 8)
|
||||
gtest.Assert(len(nsl), 1)
|
||||
gtest.Assert(array2.Range(1, 2), []int{2})
|
||||
t.Assert(len(nsl), 1)
|
||||
t.Assert(array2.Range(1, 2), []int{2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Sum(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
n1 := array1.Sum()
|
||||
gtest.Assert(n1, 9)
|
||||
t.Assert(n1, 9)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Join(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
gtest.Assert(array1.Join("."), `1.3.5`)
|
||||
t.Assert(array1.Join("."), `1.3.5`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_String(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
gtest.Assert(array1.String(), `[1,3,5]`)
|
||||
t.Assert(array1.String(), `[1,3,5]`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Contains(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
gtest.Assert(array1.Contains(4), false)
|
||||
t.Assert(array1.Contains(4), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Clone(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array2 := array1.Clone()
|
||||
gtest.Assert(array2.Len(), 3)
|
||||
gtest.Assert(array2, array1)
|
||||
t.Assert(array2.Len(), 3)
|
||||
t.Assert(array2, array1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Clear(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array1.Clear()
|
||||
gtest.Assert(array1.Len(), 0)
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Chunk(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
ns1 := array1.Chunk(2) //按每几个元素切成一个数组
|
||||
ns2 := array1.Chunk(-1)
|
||||
gtest.Assert(len(ns1), 3)
|
||||
gtest.Assert(ns1[0], []int{1, 2})
|
||||
gtest.Assert(ns1[2], []int{5})
|
||||
gtest.Assert(len(ns2), 0)
|
||||
t.Assert(len(ns1), 3)
|
||||
t.Assert(ns1[0], []int{1, 2})
|
||||
t.Assert(ns1[2], []int{5})
|
||||
t.Assert(len(ns2), 0)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []int{1, 2, 3})
|
||||
t.Assert(chunks[1], []int{4, 5})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []int{1, 2})
|
||||
t.Assert(chunks[1], []int{3, 4})
|
||||
t.Assert(chunks[2], []int{5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []int{1, 2, 3})
|
||||
t.Assert(chunks[1], []int{4, 5, 6})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array2 := garray.NewSortedIntArrayFrom(a1, true)
|
||||
ns1 := array1.SubSlice(1, 2)
|
||||
gtest.Assert(len(ns1), 2)
|
||||
gtest.Assert(ns1, []int{2, 3})
|
||||
t.Assert(len(ns1), 2)
|
||||
t.Assert(ns1, []int{2, 3})
|
||||
|
||||
ns2 := array1.SubSlice(7, 2)
|
||||
gtest.Assert(len(ns2), 0)
|
||||
t.Assert(len(ns2), 0)
|
||||
|
||||
ns3 := array1.SubSlice(3, 5)
|
||||
gtest.Assert(len(ns3), 2)
|
||||
gtest.Assert(ns3, []int{4, 5})
|
||||
t.Assert(len(ns3), 2)
|
||||
t.Assert(ns3, []int{4, 5})
|
||||
|
||||
ns4 := array1.SubSlice(3, 1)
|
||||
gtest.Assert(len(ns4), 1)
|
||||
gtest.Assert(ns4, []int{4})
|
||||
gtest.Assert(array1.SubSlice(-1, 1), []int{5})
|
||||
gtest.Assert(array1.SubSlice(-9, 1), nil)
|
||||
gtest.Assert(array1.SubSlice(1, -9), nil)
|
||||
gtest.Assert(array2.SubSlice(1, 2), []int{2, 3})
|
||||
t.Assert(len(ns4), 1)
|
||||
t.Assert(ns4, []int{4})
|
||||
t.Assert(array1.SubSlice(-1, 1), []int{5})
|
||||
t.Assert(array1.SubSlice(-9, 1), nil)
|
||||
t.Assert(array1.SubSlice(1, -9), nil)
|
||||
t.Assert(array2.SubSlice(1, 2), []int{2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Rand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
ns1 := array1.Rand() //按每几个元素切成一个数组
|
||||
gtest.AssertIN(ns1, a1)
|
||||
ns1, ok := array1.Rand()
|
||||
t.AssertIN(ns1, a1)
|
||||
t.Assert(ok, true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Rands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
ns1 := array1.Rands(2) //按每几个元素切成一个数组
|
||||
gtest.AssertIN(ns1, a1)
|
||||
gtest.Assert(len(ns1), 2)
|
||||
ns1 := array1.Rands(2)
|
||||
t.AssertIN(ns1, a1)
|
||||
t.Assert(len(ns1), 2)
|
||||
|
||||
ns2 := array1.Rands(6) //按每几个元素切成一个数组
|
||||
gtest.AssertIN(ns2, a1)
|
||||
gtest.Assert(len(ns2), 5)
|
||||
ns2 := array1.Rands(6)
|
||||
t.Assert(len(ns2), 6)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_CountValues(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 3}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
ns1 := array1.CountValues() //按每几个元素切成一个数组
|
||||
gtest.Assert(len(ns1), 5)
|
||||
gtest.Assert(ns1[2], 1)
|
||||
gtest.Assert(ns1[3], 2)
|
||||
t.Assert(len(ns1), 5)
|
||||
t.Assert(ns1[2], 1)
|
||||
t.Assert(ns1[3], 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_SetUnique(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 3}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array1.SetUnique(true)
|
||||
gtest.Assert(array1.Len(), 5)
|
||||
gtest.Assert(array1, []int{1, 2, 3, 4, 5})
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(array1, []int{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
a1 := garray.NewSortedIntArrayFrom(s1, true)
|
||||
ch1 := make(chan int64, 3)
|
||||
@ -362,13 +470,13 @@ func TestSortedIntArray_LockFunc(t *testing.T) {
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
gtest.Assert(a1.Contains(6), true)
|
||||
t.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
t.Assert(a1.Contains(6), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_RLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
a1 := garray.NewSortedIntArrayFrom(s1, true)
|
||||
|
||||
@ -394,13 +502,13 @@ func TestSortedIntArray_RLockFunc(t *testing.T) {
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
gtest.Assert(a1.Contains(6), true)
|
||||
t.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
t.Assert(a1.Contains(6), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
@ -417,37 +525,57 @@ func TestSortedIntArray_Merge(t *testing.T) {
|
||||
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
a1 := garray.NewSortedIntArrayFrom(i0)
|
||||
|
||||
gtest.Assert(a1.Merge(s2).Len(), 6)
|
||||
gtest.Assert(a1.Merge(i1).Len(), 9)
|
||||
gtest.Assert(a1.Merge(i2).Len(), 10)
|
||||
gtest.Assert(a1.Merge(s3).Len(), 12)
|
||||
gtest.Assert(a1.Merge(s4).Len(), 14)
|
||||
gtest.Assert(a1.Merge(s5).Len(), 16)
|
||||
gtest.Assert(a1.Merge(s6).Len(), 19)
|
||||
t.Assert(a1.Merge(s2).Len(), 6)
|
||||
t.Assert(a1.Merge(i1).Len(), 9)
|
||||
t.Assert(a1.Merge(i2).Len(), 10)
|
||||
t.Assert(a1.Merge(s3).Len(), 12)
|
||||
t.Assert(a1.Merge(s4).Len(), 14)
|
||||
t.Assert(a1.Merge(s5).Len(), 16)
|
||||
t.Assert(a1.Merge(s6).Len(), 19)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Json(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
s2 := []int{1, 2, 3, 4}
|
||||
a1 := garray.NewSortedIntArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
gtest.Assert(b1, b2)
|
||||
gtest.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedIntArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
gtest.Assert(a2.Slice(), s2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedIntArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(a3.Slice(), s1)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
s2 := []int{1, 2, 3, 4}
|
||||
a1 := *garray.NewSortedIntArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
a2 := garray.NewSortedIntArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedIntArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores *garray.SortedIntArray
|
||||
@ -457,59 +585,140 @@ func TestSortedIntArray_Json(t *testing.T) {
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.Name, data["Name"])
|
||||
gtest.Assert(user.Scores, []int{98, 99, 100})
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []int{98, 99, 100})
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.SortedIntArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []int{98, 99, 100})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Iterator(t *testing.T) {
|
||||
slice := g.SliceInt{10, 20, 30, 40}
|
||||
array := garray.NewSortedIntArrayFrom(slice)
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.Iterator(func(k int, v int) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorAsc(func(k int, v int) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorDesc(func(k int, v int) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.Iterator(func(k int, v int) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorAsc(func(k int, v int) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorDesc(func(k int, v int) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_RemoveValue(t *testing.T) {
|
||||
slice := g.SliceInt{10, 20, 30, 40}
|
||||
array := garray.NewSortedIntArrayFrom(slice)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(array.RemoveValue(99), false)
|
||||
t.Assert(array.RemoveValue(20), true)
|
||||
t.Assert(array.RemoveValue(10), true)
|
||||
t.Assert(array.RemoveValue(20), false)
|
||||
t.Assert(array.RemoveValue(88), false)
|
||||
t.Assert(array.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.SortedIntArray
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`[2,3,1]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.Slice{2, 3, 1},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.Slice{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{0, 1, 2, 3, 4, 0})
|
||||
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2, 3, 4})
|
||||
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2})
|
||||
t.Assert(array.Walk(func(value int) int {
|
||||
return 10 + value
|
||||
}), g.Slice{11, 12})
|
||||
})
|
||||
}
|
||||
|
||||
@ -20,330 +20,450 @@ import (
|
||||
)
|
||||
|
||||
func TestNewSortedStrArrayFrom(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
s1 := garray.NewSortedStrArrayFrom(a1, true)
|
||||
gtest.Assert(s1, []string{"a", "b", "c", "d"})
|
||||
t.Assert(s1, []string{"a", "b", "c", "d"})
|
||||
s2 := garray.NewSortedStrArrayFrom(a1, false)
|
||||
gtest.Assert(s2, []string{"a", "b", "c", "d"})
|
||||
t.Assert(s2, []string{"a", "b", "c", "d"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewSortedStrArrayFromCopy(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
s1 := garray.NewSortedStrArrayFromCopy(a1, true)
|
||||
gtest.Assert(s1.Len(), 4)
|
||||
gtest.Assert(s1, []string{"a", "b", "c", "d"})
|
||||
t.Assert(s1.Len(), 4)
|
||||
t.Assert(s1, []string{"a", "b", "c", "d"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_SetArray(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
a2 := []string{"f", "g", "h"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array1.SetArray(a2)
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1.Contains("d"), false)
|
||||
gtest.Assert(array1.Contains("b"), false)
|
||||
gtest.Assert(array1.Contains("g"), true)
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1.Contains("d"), false)
|
||||
t.Assert(array1.Contains("b"), false)
|
||||
t.Assert(array1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_ContainsI(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.Append("a", "b", "C")
|
||||
t.Assert(s.Contains("A"), false)
|
||||
t.Assert(s.Contains("a"), true)
|
||||
t.Assert(s.ContainsI("A"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Sort(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
|
||||
gtest.Assert(array1, []string{"a", "b", "c", "d"})
|
||||
t.Assert(array1, []string{"a", "b", "c", "d"})
|
||||
array1.Sort()
|
||||
gtest.Assert(array1.Len(), 4)
|
||||
gtest.Assert(array1.Contains("c"), true)
|
||||
gtest.Assert(array1, []string{"a", "b", "c", "d"})
|
||||
t.Assert(array1.Len(), 4)
|
||||
t.Assert(array1.Contains("c"), true)
|
||||
t.Assert(array1, []string{"a", "b", "c", "d"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Get(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Get(2), "c")
|
||||
gtest.Assert(array1.Get(0), "a")
|
||||
v, ok := array1.Get(2)
|
||||
t.Assert(v, "c")
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array1.Get(0)
|
||||
t.Assert(v, "a")
|
||||
t.Assert(ok, true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Remove(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Remove(2), "c")
|
||||
gtest.Assert(array1.Get(2), "d")
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1.Contains("c"), false)
|
||||
|
||||
gtest.Assert(array1.Remove(0), "a")
|
||||
gtest.Assert(array1.Len(), 2)
|
||||
gtest.Assert(array1.Contains("a"), false)
|
||||
v, ok := array1.Remove(-1)
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
|
||||
// 此时array1里的元素只剩下2个
|
||||
gtest.Assert(array1.Remove(1), "d")
|
||||
gtest.Assert(array1.Len(), 1)
|
||||
v, ok = array1.Remove(100000)
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
|
||||
v, ok = array1.Remove(2)
|
||||
t.Assert(v, "c")
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array1.Get(2)
|
||||
t.Assert(v, "d")
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1.Contains("c"), false)
|
||||
|
||||
v, ok = array1.Remove(0)
|
||||
t.Assert(v, "a")
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array1.Len(), 2)
|
||||
t.Assert(array1.Contains("a"), false)
|
||||
|
||||
v, ok = array1.Remove(1)
|
||||
t.Assert(v, "d")
|
||||
t.Assert(ok, true)
|
||||
|
||||
t.Assert(array1.Len(), 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_PopLeft(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
s1 := array1.PopLeft()
|
||||
gtest.Assert(s1, "a")
|
||||
gtest.Assert(array1.Len(), 4)
|
||||
gtest.Assert(array1.Contains("a"), false)
|
||||
v, ok := array1.PopLeft()
|
||||
t.Assert(v, "a")
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array1.Len(), 4)
|
||||
t.Assert(array1.Contains("a"), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_PopRight(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
s1 := array1.PopRight()
|
||||
gtest.Assert(s1, "e")
|
||||
gtest.Assert(array1.Len(), 4)
|
||||
gtest.Assert(array1.Contains("e"), false)
|
||||
v, ok := array1.PopRight()
|
||||
t.Assert(v, "e")
|
||||
t.Assert(ok, ok)
|
||||
t.Assert(array1.Len(), 4)
|
||||
t.Assert(array1.Contains("e"), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_PopRand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
s1 := array1.PopRand()
|
||||
gtest.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
|
||||
gtest.Assert(array1.Len(), 4)
|
||||
gtest.Assert(array1.Contains(s1), false)
|
||||
s1, ok := array1.PopRand()
|
||||
t.Assert(ok, true)
|
||||
t.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
|
||||
t.Assert(array1.Len(), 4)
|
||||
t.Assert(array1.Contains(s1), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_PopRands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
s1 := array1.PopRands(2)
|
||||
gtest.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(len(s1), 2)
|
||||
t.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(len(s1), 2)
|
||||
|
||||
s1 = array1.PopRands(4)
|
||||
gtest.Assert(len(s1), 3)
|
||||
gtest.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
|
||||
t.Assert(len(s1), 3)
|
||||
t.AssertIN(s1, []string{"e", "a", "d", "c", "b"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Empty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArray()
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopLefts(10), nil)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopRights(10), nil)
|
||||
|
||||
v, ok = array.PopRand()
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
t.Assert(array.PopRands(10), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_PopLefts(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
s1 := array1.PopLefts(2)
|
||||
gtest.Assert(s1, []string{"a", "b"})
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(len(s1), 2)
|
||||
t.Assert(s1, []string{"a", "b"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(len(s1), 2)
|
||||
|
||||
s1 = array1.PopLefts(4)
|
||||
gtest.Assert(len(s1), 3)
|
||||
gtest.Assert(s1, []string{"c", "d", "e"})
|
||||
t.Assert(len(s1), 3)
|
||||
t.Assert(s1, []string{"c", "d", "e"})
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_PopRights(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
s1 := array1.PopRights(2)
|
||||
gtest.Assert(s1, []string{"f", "g"})
|
||||
gtest.Assert(array1.Len(), 5)
|
||||
gtest.Assert(len(s1), 2)
|
||||
t.Assert(s1, []string{"f", "g"})
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(len(s1), 2)
|
||||
s1 = array1.PopRights(6)
|
||||
gtest.Assert(len(s1), 5)
|
||||
gtest.Assert(s1, []string{"a", "b", "c", "d", "e"})
|
||||
gtest.Assert(array1.Len(), 0)
|
||||
t.Assert(len(s1), 5)
|
||||
t.Assert(s1, []string{"a", "b", "c", "d", "e"})
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array2 := garray.NewSortedStrArrayFrom(a1, true)
|
||||
s1 := array1.Range(2, 4)
|
||||
gtest.Assert(len(s1), 2)
|
||||
gtest.Assert(s1, []string{"c", "d"})
|
||||
t.Assert(len(s1), 2)
|
||||
t.Assert(s1, []string{"c", "d"})
|
||||
|
||||
s1 = array1.Range(-1, 2)
|
||||
gtest.Assert(len(s1), 2)
|
||||
gtest.Assert(s1, []string{"a", "b"})
|
||||
t.Assert(len(s1), 2)
|
||||
t.Assert(s1, []string{"a", "b"})
|
||||
|
||||
s1 = array1.Range(4, 8)
|
||||
gtest.Assert(len(s1), 3)
|
||||
gtest.Assert(s1, []string{"e", "f", "g"})
|
||||
gtest.Assert(array1.Range(10, 2), nil)
|
||||
t.Assert(len(s1), 3)
|
||||
t.Assert(s1, []string{"e", "f", "g"})
|
||||
t.Assert(array1.Range(10, 2), nil)
|
||||
|
||||
s2 := array2.Range(2, 4)
|
||||
gtest.Assert(s2, []string{"c", "d"})
|
||||
t.Assert(s2, []string{"c", "d"})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Sum(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
|
||||
a2 := []string{"1", "2", "3", "4", "a"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array2 := garray.NewSortedStrArrayFrom(a2)
|
||||
gtest.Assert(array1.Sum(), 0)
|
||||
gtest.Assert(array2.Sum(), 10)
|
||||
t.Assert(array1.Sum(), 0)
|
||||
t.Assert(array2.Sum(), 10)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Clone(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array2 := array1.Clone()
|
||||
gtest.Assert(array1, array2)
|
||||
t.Assert(array1, array2)
|
||||
array1.Remove(1)
|
||||
gtest.Assert(array2.Len(), 7)
|
||||
t.Assert(array2.Len(), 7)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Clear(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array1.Clear()
|
||||
gtest.Assert(array1.Len(), 0)
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array2 := garray.NewSortedStrArrayFrom(a1, true)
|
||||
s1 := array1.SubSlice(1, 3)
|
||||
gtest.Assert(len(s1), 3)
|
||||
gtest.Assert(s1, []string{"b", "c", "d"})
|
||||
gtest.Assert(array1.Len(), 7)
|
||||
t.Assert(len(s1), 3)
|
||||
t.Assert(s1, []string{"b", "c", "d"})
|
||||
t.Assert(array1.Len(), 7)
|
||||
|
||||
s2 := array1.SubSlice(1, 10)
|
||||
gtest.Assert(len(s2), 6)
|
||||
t.Assert(len(s2), 6)
|
||||
|
||||
s3 := array1.SubSlice(10, 2)
|
||||
gtest.Assert(len(s3), 0)
|
||||
t.Assert(len(s3), 0)
|
||||
|
||||
s3 = array1.SubSlice(-5, 2)
|
||||
gtest.Assert(s3, []string{"c", "d"})
|
||||
t.Assert(s3, []string{"c", "d"})
|
||||
|
||||
s3 = array1.SubSlice(-10, 2)
|
||||
gtest.Assert(s3, nil)
|
||||
t.Assert(s3, nil)
|
||||
|
||||
s3 = array1.SubSlice(1, -2)
|
||||
gtest.Assert(s3, nil)
|
||||
t.Assert(s3, nil)
|
||||
|
||||
gtest.Assert(array2.SubSlice(1, 3), []string{"b", "c", "d"})
|
||||
t.Assert(array2.SubSlice(1, 3), []string{"b", "c", "d"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Len(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Len(), 7)
|
||||
t.Assert(array1.Len(), 7)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Rand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
gtest.AssertIN(array1.Rand(), []string{"e", "a", "d"})
|
||||
v, ok := array1.Rand()
|
||||
t.AssertIN(v, []string{"e", "a", "d"})
|
||||
t.Assert(ok, true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Rands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
s1 := array1.Rands(2)
|
||||
|
||||
gtest.AssertIN(s1, []string{"e", "a", "d"})
|
||||
gtest.Assert(len(s1), 2)
|
||||
t.AssertIN(s1, []string{"e", "a", "d"})
|
||||
t.Assert(len(s1), 2)
|
||||
|
||||
s1 = array1.Rands(4)
|
||||
gtest.AssertIN(s1, []string{"e", "a", "d"})
|
||||
gtest.Assert(len(s1), 3)
|
||||
t.Assert(len(s1), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Join(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Join(","), `a,d,e`)
|
||||
gtest.Assert(array1.Join("."), `a.d.e`)
|
||||
t.Assert(array1.Join(","), `a,d,e`)
|
||||
t.Assert(array1.Join("."), `a.d.e`)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", `"b"`, `\c`}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
gtest.Assert(array1.Join("."), `"b".\c.a`)
|
||||
t.Assert(array1.Join("."), `"b".\c.a`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_String(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
gtest.Assert(array1.String(), `["a","d","e"]`)
|
||||
t.Assert(array1.String(), `["a","d","e"]`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_CountValues(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "a", "c"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
m1 := array1.CountValues()
|
||||
gtest.Assert(m1["a"], 2)
|
||||
gtest.Assert(m1["d"], 1)
|
||||
t.Assert(m1["a"], 2)
|
||||
t.Assert(m1["d"], 1)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Chunk(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "a", "c"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array2 := array1.Chunk(2)
|
||||
gtest.Assert(len(array2), 3)
|
||||
gtest.Assert(len(array2[0]), 2)
|
||||
gtest.Assert(array2[1], []string{"c", "d"})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
t.Assert(len(array2), 3)
|
||||
t.Assert(len(array2[0]), 2)
|
||||
t.Assert(array2[1], []string{"c", "d"})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []string{"1", "2", "3"})
|
||||
t.Assert(chunks[1], []string{"4", "5"})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
t.Assert(len(chunks), 3)
|
||||
t.Assert(chunks[0], []string{"1", "2"})
|
||||
t.Assert(chunks[1], []string{"3", "4"})
|
||||
t.Assert(chunks[2], []string{"5", "6"})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
chunks := array1.Chunk(3)
|
||||
t.Assert(len(chunks), 2)
|
||||
t.Assert(chunks[0], []string{"1", "2", "3"})
|
||||
t.Assert(chunks[1], []string{"4", "5", "6"})
|
||||
t.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_SetUnique(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "a", "c"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array2 := array1.SetUnique(true)
|
||||
gtest.Assert(array2.Len(), 4)
|
||||
gtest.Assert(array2, []string{"a", "c", "d", "e"})
|
||||
t.Assert(array2.Len(), 4)
|
||||
t.Assert(array2, []string{"a", "c", "d", "e"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
a1 := garray.NewSortedStrArrayFrom(s1, true)
|
||||
|
||||
@ -369,13 +489,13 @@ func TestSortedStrArray_LockFunc(t *testing.T) {
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
t.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_RLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
a1 := garray.NewSortedStrArrayFrom(s1, true)
|
||||
|
||||
@ -401,13 +521,13 @@ func TestSortedStrArray_RLockFunc(t *testing.T) {
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
t.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
t.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
@ -425,39 +545,61 @@ func TestSortedStrArray_Merge(t *testing.T) {
|
||||
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
a1 := garray.NewSortedStrArrayFrom(s1)
|
||||
|
||||
gtest.Assert(a1.Merge(s2).Len(), 6)
|
||||
gtest.Assert(a1.Merge(i1).Len(), 9)
|
||||
gtest.Assert(a1.Merge(i2).Len(), 10)
|
||||
gtest.Assert(a1.Merge(s3).Len(), 12)
|
||||
gtest.Assert(a1.Merge(s4).Len(), 14)
|
||||
gtest.Assert(a1.Merge(s5).Len(), 16)
|
||||
gtest.Assert(a1.Merge(s6).Len(), 19)
|
||||
t.Assert(a1.Merge(s2).Len(), 6)
|
||||
t.Assert(a1.Merge(i1).Len(), 9)
|
||||
t.Assert(a1.Merge(i2).Len(), 10)
|
||||
t.Assert(a1.Merge(s3).Len(), 12)
|
||||
t.Assert(a1.Merge(s4).Len(), 14)
|
||||
t.Assert(a1.Merge(s5).Len(), 16)
|
||||
t.Assert(a1.Merge(s6).Len(), 19)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Json(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "d", "c"}
|
||||
s2 := []string{"a", "b", "c", "d"}
|
||||
a1 := garray.NewSortedStrArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
gtest.Assert(b1, b2)
|
||||
gtest.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedStrArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
gtest.Assert(a2.Slice(), s2)
|
||||
gtest.Assert(a2.Interfaces(), s2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
t.Assert(a2.Interfaces(), s2)
|
||||
|
||||
var a3 garray.SortedStrArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(a3.Slice(), s1)
|
||||
gtest.Assert(a3.Interfaces(), s1)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "d", "c"}
|
||||
s2 := []string{"a", "b", "c", "d"}
|
||||
a1 := *garray.NewSortedStrArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
a2 := garray.NewSortedStrArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
t.Assert(a2.Interfaces(), s2)
|
||||
|
||||
var a3 garray.SortedStrArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores *garray.SortedStrArray
|
||||
@ -467,59 +609,139 @@ func TestSortedStrArray_Json(t *testing.T) {
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.Name, data["Name"])
|
||||
gtest.Assert(user.Scores, []string{"A", "A", "A+"})
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []string{"A", "A", "A+"})
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.SortedStrArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []string{"A", "A", "A+"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Iterator(t *testing.T) {
|
||||
slice := g.SliceStr{"a", "b", "d", "c"}
|
||||
array := garray.NewSortedStrArrayFrom(slice)
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.Iterator(func(k int, v string) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorAsc(func(k int, v string) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array.IteratorDesc(func(k int, v string) bool {
|
||||
gtest.Assert(v, slice[k])
|
||||
t.Assert(v, slice[k])
|
||||
return true
|
||||
})
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.Iterator(func(k int, v string) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorAsc(func(k int, v string) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
index := 0
|
||||
array.IteratorDesc(func(k int, v string) bool {
|
||||
index++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(index, 1)
|
||||
t.Assert(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_RemoveValue(t *testing.T) {
|
||||
slice := g.SliceStr{"a", "b", "d", "c"}
|
||||
array := garray.NewSortedStrArrayFrom(slice)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(array.RemoveValue("e"), false)
|
||||
t.Assert(array.RemoveValue("b"), true)
|
||||
t.Assert(array.RemoveValue("a"), true)
|
||||
t.Assert(array.RemoveValue("c"), true)
|
||||
t.Assert(array.RemoveValue("f"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Array *garray.SortedStrArray
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": []byte(`["1","3","2"]`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(g.Map{
|
||||
"name": "john",
|
||||
"array": g.SliceStr{"1", "3", "2"},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Array.Slice(), g.SliceStr{"1", "2", "3"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_FilterEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"", "1", "2", "0"})
|
||||
t.Assert(array.FilterEmpty(), g.SliceStr{"0", "1", "2"})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2"})
|
||||
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2"})
|
||||
t.Assert(array.Walk(func(value string) string {
|
||||
return "key-" + value
|
||||
}), g.Slice{"key-1", "key-2"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gchan provides graceful channel for no panic operations.
|
||||
//
|
||||
// It's safe to call Chan.Push/Close functions repeatedly.
|
||||
package gchan
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
)
|
||||
|
||||
// Graceful channel.
|
||||
type Chan struct {
|
||||
channel chan interface{}
|
||||
closed *gtype.Bool
|
||||
}
|
||||
|
||||
// New creates a graceful channel with given <limit>.
|
||||
func New(limit int) *Chan {
|
||||
return &Chan{
|
||||
channel: make(chan interface{}, limit),
|
||||
closed: gtype.NewBool(),
|
||||
}
|
||||
}
|
||||
|
||||
// Push pushes <value> to channel.
|
||||
// It is safe to be called repeatedly.
|
||||
func (c *Chan) Push(value interface{}) error {
|
||||
if c.closed.Val() {
|
||||
return errors.New("channel is closed")
|
||||
}
|
||||
c.channel <- value
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pop pops value from channel.
|
||||
// If there's no value in channel, it would block to wait.
|
||||
// If the channel is closed, it will return a nil value immediately.
|
||||
func (c *Chan) Pop() interface{} {
|
||||
return <-c.channel
|
||||
}
|
||||
|
||||
// Close closes the channel.
|
||||
// It is safe to be called repeatedly.
|
||||
func (c *Chan) Close() {
|
||||
if !c.closed.Set(true) {
|
||||
close(c.channel)
|
||||
}
|
||||
}
|
||||
|
||||
// See Len.
|
||||
func (c *Chan) Size() int {
|
||||
return c.Len()
|
||||
}
|
||||
|
||||
// Len returns the length of the channel.
|
||||
func (c *Chan) Len() int {
|
||||
return len(c.channel)
|
||||
}
|
||||
|
||||
// Cap returns the capacity of the channel.
|
||||
func (c *Chan) Cap() int {
|
||||
return cap(c.channel)
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gchan_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/container/gchan"
|
||||
)
|
||||
|
||||
func Example_basic() {
|
||||
n := 10
|
||||
c := gchan.New(n)
|
||||
for i := 0; i < n; i++ {
|
||||
c.Push(i)
|
||||
}
|
||||
fmt.Println(c.Len(), c.Cap())
|
||||
for i := 0; i < n; i++ {
|
||||
fmt.Print(c.Pop())
|
||||
}
|
||||
c.Close()
|
||||
|
||||
// Output:
|
||||
//10 10
|
||||
//0123456789
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
package gchan_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/container/gchan"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Gchan(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
ch := gchan.New(10)
|
||||
|
||||
gtest.Assert(ch.Cap(), 10)
|
||||
gtest.Assert(ch.Push(1), nil)
|
||||
gtest.Assert(ch.Len(), 1)
|
||||
gtest.Assert(ch.Size(), 1)
|
||||
ch.Pop()
|
||||
gtest.Assert(ch.Len(), 0)
|
||||
gtest.Assert(ch.Size(), 0)
|
||||
ch.Close()
|
||||
gtest.Assert(ch.Push(1), errors.New("channel is closed"))
|
||||
|
||||
ch = gchan.New(0)
|
||||
ch1 := gchan.New(0)
|
||||
go func() {
|
||||
var i = 0
|
||||
for {
|
||||
v := ch.Pop()
|
||||
if v == nil {
|
||||
ch1.Push(i)
|
||||
break
|
||||
}
|
||||
gtest.Assert(v, i)
|
||||
i++
|
||||
}
|
||||
}()
|
||||
|
||||
for index := 0; index < 10; index++ {
|
||||
ch.Push(index)
|
||||
}
|
||||
ch.Close()
|
||||
gtest.Assert(ch1.Pop(), 10)
|
||||
ch1.Close()
|
||||
})
|
||||
}
|
||||
@ -5,32 +5,33 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
// Package glist provides a concurrent-safe/unsafe doubly linked list.
|
||||
// Package glist provides most commonly used doubly linked list container which also supports concurrent-safe/unsafe switch feature.
|
||||
package glist
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
)
|
||||
|
||||
type (
|
||||
// List is a doubly linked list containing a concurrent-safe/unsafe switch.
|
||||
// The switch should be set when its initialization and cannot be changed then.
|
||||
List struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
list *list.List
|
||||
}
|
||||
|
||||
// Element the item type of the list.
|
||||
Element = list.Element
|
||||
)
|
||||
|
||||
// New creates and returns a new empty doubly linked list.
|
||||
func New(safe ...bool) *List {
|
||||
return &List{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
list: list.New(),
|
||||
}
|
||||
}
|
||||
@ -44,7 +45,7 @@ func NewFrom(array []interface{}, safe ...bool) *List {
|
||||
l.PushBack(v)
|
||||
}
|
||||
return &List{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
list: l,
|
||||
}
|
||||
}
|
||||
@ -52,6 +53,9 @@ func NewFrom(array []interface{}, safe ...bool) *List {
|
||||
// PushFront inserts a new element <e> with value <v> at the front of list <l> and returns <e>.
|
||||
func (l *List) PushFront(v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.PushFront(v)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
@ -60,6 +64,9 @@ func (l *List) PushFront(v interface{}) (e *Element) {
|
||||
// PushBack inserts a new element <e> with value <v> at the back of list <l> and returns <e>.
|
||||
func (l *List) PushBack(v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.PushBack(v)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
@ -68,6 +75,9 @@ func (l *List) PushBack(v interface{}) (e *Element) {
|
||||
// PushFronts inserts multiple new elements with values <values> at the front of list <l>.
|
||||
func (l *List) PushFronts(values []interface{}) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
for _, v := range values {
|
||||
l.list.PushFront(v)
|
||||
}
|
||||
@ -77,6 +87,9 @@ func (l *List) PushFronts(values []interface{}) {
|
||||
// PushBacks inserts multiple new elements with values <values> at the back of list <l>.
|
||||
func (l *List) PushBacks(values []interface{}) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
for _, v := range values {
|
||||
l.list.PushBack(v)
|
||||
}
|
||||
@ -86,20 +99,28 @@ func (l *List) PushBacks(values []interface{}) {
|
||||
// PopBack removes the element from back of <l> and returns the value of the element.
|
||||
func (l *List) PopBack() (value interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
if e := l.list.Back(); e != nil {
|
||||
value = l.list.Remove(e)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// PopFront removes the element from front of <l> and returns the value of the element.
|
||||
func (l *List) PopFront() (value interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
if e := l.list.Front(); e != nil {
|
||||
value = l.list.Remove(e)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -107,6 +128,11 @@ func (l *List) PopFront() (value interface{}) {
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *List) PopBacks(max int) (values []interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
if max > 0 && max < length {
|
||||
@ -117,7 +143,6 @@ func (l *List) PopBacks(max int) (values []interface{}) {
|
||||
values[i] = l.list.Remove(l.list.Back())
|
||||
}
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -125,6 +150,11 @@ func (l *List) PopBacks(max int) (values []interface{}) {
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *List) PopFronts(max int) (values []interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
if max > 0 && max < length {
|
||||
@ -135,7 +165,6 @@ func (l *List) PopFronts(max int) (values []interface{}) {
|
||||
values[i] = l.list.Remove(l.list.Front())
|
||||
}
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -154,6 +183,10 @@ func (l *List) PopFrontAll() []interface{} {
|
||||
// FrontAll copies and returns values of all elements from front of <l> as slice.
|
||||
func (l *List) FrontAll() (values []interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
values = make([]interface{}, length)
|
||||
@ -161,13 +194,16 @@ func (l *List) FrontAll() (values []interface{}) {
|
||||
values[i] = e.Value
|
||||
}
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// BackAll copies and returns values of all elements from back of <l> as slice.
|
||||
func (l *List) BackAll() (values []interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
values = make([]interface{}, length)
|
||||
@ -175,43 +211,54 @@ func (l *List) BackAll() (values []interface{}) {
|
||||
values[i] = e.Value
|
||||
}
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// FrontValue returns value of the first element of <l> or nil if the list is empty.
|
||||
func (l *List) FrontValue() (value interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
if e := l.list.Front(); e != nil {
|
||||
value = e.Value
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// BackValue returns value of the last element of <l> or nil if the list is empty.
|
||||
func (l *List) BackValue() (value interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
if e := l.list.Back(); e != nil {
|
||||
value = e.Value
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Front returns the first element of list <l> or nil if the list is empty.
|
||||
func (l *List) Front() (e *Element) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
e = l.list.Front()
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Back returns the last element of list <l> or nil if the list is empty.
|
||||
func (l *List) Back() (e *Element) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
e = l.list.Back()
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -219,8 +266,11 @@ func (l *List) Back() (e *Element) {
|
||||
// The complexity is O(1).
|
||||
func (l *List) Len() (length int) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length = l.list.Len()
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -234,8 +284,11 @@ func (l *List) Size() int {
|
||||
// The element and <p> must not be nil.
|
||||
func (l *List) MoveBefore(e, p *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveBefore(e, p)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// MoveAfter moves element <e> to its new position after <p>.
|
||||
@ -243,8 +296,11 @@ func (l *List) MoveBefore(e, p *Element) {
|
||||
// The element and <p> must not be nil.
|
||||
func (l *List) MoveAfter(e, p *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveAfter(e, p)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// MoveToFront moves element <e> to the front of list <l>.
|
||||
@ -252,8 +308,11 @@ func (l *List) MoveAfter(e, p *Element) {
|
||||
// The element must not be nil.
|
||||
func (l *List) MoveToFront(e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveToFront(e)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// MoveToBack moves element <e> to the back of list <l>.
|
||||
@ -261,8 +320,11 @@ func (l *List) MoveToFront(e *Element) {
|
||||
// The element must not be nil.
|
||||
func (l *List) MoveToBack(e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveToBack(e)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// PushBackList inserts a copy of an other list at the back of list <l>.
|
||||
@ -273,8 +335,11 @@ func (l *List) PushBackList(other *List) {
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.PushBackList(other.list)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// PushFrontList inserts a copy of an other list at the front of list <l>.
|
||||
@ -285,8 +350,11 @@ func (l *List) PushFrontList(other *List) {
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.PushFrontList(other.list)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// InsertAfter inserts a new element <e> with value <v> immediately after <p> and returns <e>.
|
||||
@ -294,8 +362,11 @@ func (l *List) PushFrontList(other *List) {
|
||||
// The <p> must not be nil.
|
||||
func (l *List) InsertAfter(p *Element, v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.InsertAfter(v, p)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -304,8 +375,11 @@ func (l *List) InsertAfter(p *Element, v interface{}) (e *Element) {
|
||||
// The <p> must not be nil.
|
||||
func (l *List) InsertBefore(p *Element, v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.InsertBefore(v, p)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -314,18 +388,24 @@ func (l *List) InsertBefore(p *Element, v interface{}) (e *Element) {
|
||||
// The element must not be nil.
|
||||
func (l *List) Remove(e *Element) (value interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
value = l.list.Remove(e)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Removes removes multiple elements <es> from <l> if <es> are elements of list <l>.
|
||||
func (l *List) Removes(es []*Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
for _, e := range es {
|
||||
l.list.Remove(e)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -345,13 +425,18 @@ func (l *List) Clear() {
|
||||
func (l *List) RLockFunc(f func(list *list.List)) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
f(l.list)
|
||||
if l.list != nil {
|
||||
f(l.list)
|
||||
}
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (l *List) LockFunc(f func(list *list.List)) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
f(l.list)
|
||||
}
|
||||
|
||||
@ -360,11 +445,14 @@ func (l *List) Iterator(f func(e *Element) bool) {
|
||||
l.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the list in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the list readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (l *List) IteratorAsc(f func(e *Element) bool) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
|
||||
@ -375,11 +463,14 @@ func (l *List) IteratorAsc(f func(e *Element) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the list in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the list readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (l *List) IteratorDesc(f func(e *Element) bool) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, l.list.Back(); i < length; i, e = i+1, e.Prev() {
|
||||
@ -394,17 +485,14 @@ func (l *List) IteratorDesc(f func(e *Element) bool) {
|
||||
func (l *List) Join(glue string) string {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
s := ""
|
||||
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
|
||||
s = gconv.String(e.Value)
|
||||
if gstr.IsNumeric(s) {
|
||||
buffer.WriteString(s)
|
||||
} else {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
|
||||
}
|
||||
buffer.WriteString(gconv.String(e.Value))
|
||||
if i != length-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
@ -425,12 +513,11 @@ func (l *List) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (l *List) UnmarshalJSON(b []byte) error {
|
||||
if l.mu == nil {
|
||||
l.mu = rwmutex.New()
|
||||
l.list = list.New()
|
||||
}
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
var array []interface{}
|
||||
if err := json.Unmarshal(b, &array); err != nil {
|
||||
return err
|
||||
@ -438,3 +525,21 @@ func (l *List) UnmarshalJSON(b []byte) error {
|
||||
l.PushBacks(array)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for list.
|
||||
func (l *List) UnmarshalValue(value interface{}) (err error) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
var array []interface{}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.Unmarshal(gconv.Bytes(value), &array)
|
||||
default:
|
||||
array = gconv.SliceAny(value)
|
||||
}
|
||||
l.PushBacks(array)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
|
||||
"github.com/gogf/gf/container/glist"
|
||||
)
|
||||
@ -96,5 +97,30 @@ func Example_iterate() {
|
||||
//10987654321
|
||||
//12345678910
|
||||
//10987654321
|
||||
//[1,2,3,4,5,"M",7,8,9,10]
|
||||
//[1,2,3,4,5,M,7,8,9,10]
|
||||
}
|
||||
|
||||
func Example_popItem() {
|
||||
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
fmt.Println(l.PopBack())
|
||||
fmt.Println(l.PopBacks(2))
|
||||
fmt.Println(l.PopFront())
|
||||
fmt.Println(l.PopFronts(2))
|
||||
|
||||
// Output:
|
||||
// 9
|
||||
// [8 7]
|
||||
// 1
|
||||
// [2 3]
|
||||
}
|
||||
|
||||
func Example_join() {
|
||||
var l glist.List
|
||||
l.PushBacks(g.Slice{"a", "b", "c", "d"})
|
||||
|
||||
fmt.Println(l.Join(","))
|
||||
|
||||
// Output:
|
||||
// a,b,c,d
|
||||
}
|
||||
|
||||
@ -15,8 +15,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 检查链表长度
|
||||
func checkListLen(t *testing.T, l *List, len int) bool {
|
||||
func checkListLen(t *gtest.T, l *List, len int) bool {
|
||||
if n := l.Len(); n != len {
|
||||
t.Errorf("l.Len() = %d, want %d", n, len)
|
||||
return false
|
||||
@ -24,8 +23,7 @@ func checkListLen(t *testing.T, l *List, len int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查指针地址
|
||||
func checkListPointers(t *testing.T, l *List, es []*Element) {
|
||||
func checkListPointers(t *gtest.T, l *List, es []*Element) {
|
||||
if !checkListLen(t, l, len(es)) {
|
||||
return
|
||||
}
|
||||
@ -41,6 +39,44 @@ func checkListPointers(t *testing.T, l *List, es []*Element) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar(t *testing.T) {
|
||||
var l List
|
||||
l.PushFront(1)
|
||||
l.PushFront(2)
|
||||
if v := l.PopBack(); v != 1 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 1, v)
|
||||
} else {
|
||||
//fmt.Println(v)
|
||||
}
|
||||
if v := l.PopBack(); v != 2 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 2, v)
|
||||
} else {
|
||||
//fmt.Println(v)
|
||||
}
|
||||
if v := l.PopBack(); v != nil {
|
||||
t.Errorf("EXPECT %v, GOT %v", nil, v)
|
||||
} else {
|
||||
//fmt.Println(v)
|
||||
}
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
if v := l.PopFront(); v != 1 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 1, v)
|
||||
} else {
|
||||
//fmt.Println(v)
|
||||
}
|
||||
if v := l.PopFront(); v != 2 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 2, v)
|
||||
} else {
|
||||
//fmt.Println(v)
|
||||
}
|
||||
if v := l.PopFront(); v != nil {
|
||||
t.Errorf("EXPECT %v, GOT %v", nil, v)
|
||||
} else {
|
||||
//fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
l := New()
|
||||
l.PushFront(1)
|
||||
@ -80,87 +116,89 @@ func TestBasic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
l := New()
|
||||
checkListPointers(t, l, []*Element{})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
checkListPointers(t, l, []*Element{})
|
||||
|
||||
// Single element list
|
||||
e := l.PushFront("a")
|
||||
checkListPointers(t, l, []*Element{e})
|
||||
l.MoveToFront(e)
|
||||
checkListPointers(t, l, []*Element{e})
|
||||
l.MoveToBack(e)
|
||||
checkListPointers(t, l, []*Element{e})
|
||||
l.Remove(e)
|
||||
checkListPointers(t, l, []*Element{})
|
||||
|
||||
// Bigger list
|
||||
e2 := l.PushFront(2)
|
||||
e1 := l.PushFront(1)
|
||||
e3 := l.PushBack(3)
|
||||
e4 := l.PushBack("banana")
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
|
||||
l.Remove(e2)
|
||||
checkListPointers(t, l, []*Element{e1, e3, e4})
|
||||
|
||||
l.MoveToFront(e3) // move from middle
|
||||
checkListPointers(t, l, []*Element{e3, e1, e4})
|
||||
|
||||
l.MoveToFront(e1)
|
||||
l.MoveToBack(e3) // move from middle
|
||||
checkListPointers(t, l, []*Element{e1, e4, e3})
|
||||
|
||||
l.MoveToFront(e3) // move from back
|
||||
checkListPointers(t, l, []*Element{e3, e1, e4})
|
||||
l.MoveToFront(e3) // should be no-op
|
||||
checkListPointers(t, l, []*Element{e3, e1, e4})
|
||||
|
||||
l.MoveToBack(e3) // move from front
|
||||
checkListPointers(t, l, []*Element{e1, e4, e3})
|
||||
l.MoveToBack(e3) // should be no-op
|
||||
checkListPointers(t, l, []*Element{e1, e4, e3})
|
||||
|
||||
e2 = l.InsertBefore(e1, 2) // insert before front
|
||||
checkListPointers(t, l, []*Element{e2, e1, e4, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertBefore(e4, 2) // insert before middle
|
||||
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertBefore(e3, 2) // insert before back
|
||||
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
|
||||
l.Remove(e2)
|
||||
|
||||
e2 = l.InsertAfter(e1, 2) // insert after front
|
||||
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertAfter(e4, 2) // insert after middle
|
||||
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertAfter(e3, 2) // insert after back
|
||||
checkListPointers(t, l, []*Element{e1, e4, e3, e2})
|
||||
l.Remove(e2)
|
||||
|
||||
// Check standard iteration.
|
||||
sum := 0
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
if i, ok := e.Value.(int); ok {
|
||||
sum += i
|
||||
}
|
||||
}
|
||||
if sum != 4 {
|
||||
t.Errorf("sum over l = %d, want 4", sum)
|
||||
}
|
||||
|
||||
// Clear all elements by iterating
|
||||
var next *Element
|
||||
for e := l.Front(); e != nil; e = next {
|
||||
next = e.Next()
|
||||
// Single element list
|
||||
e := l.PushFront("a")
|
||||
checkListPointers(t, l, []*Element{e})
|
||||
l.MoveToFront(e)
|
||||
checkListPointers(t, l, []*Element{e})
|
||||
l.MoveToBack(e)
|
||||
checkListPointers(t, l, []*Element{e})
|
||||
l.Remove(e)
|
||||
}
|
||||
checkListPointers(t, l, []*Element{})
|
||||
checkListPointers(t, l, []*Element{})
|
||||
|
||||
// Bigger list
|
||||
e2 := l.PushFront(2)
|
||||
e1 := l.PushFront(1)
|
||||
e3 := l.PushBack(3)
|
||||
e4 := l.PushBack("banana")
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
|
||||
l.Remove(e2)
|
||||
checkListPointers(t, l, []*Element{e1, e3, e4})
|
||||
|
||||
l.MoveToFront(e3) // move from middle
|
||||
checkListPointers(t, l, []*Element{e3, e1, e4})
|
||||
|
||||
l.MoveToFront(e1)
|
||||
l.MoveToBack(e3) // move from middle
|
||||
checkListPointers(t, l, []*Element{e1, e4, e3})
|
||||
|
||||
l.MoveToFront(e3) // move from back
|
||||
checkListPointers(t, l, []*Element{e3, e1, e4})
|
||||
l.MoveToFront(e3) // should be no-op
|
||||
checkListPointers(t, l, []*Element{e3, e1, e4})
|
||||
|
||||
l.MoveToBack(e3) // move from front
|
||||
checkListPointers(t, l, []*Element{e1, e4, e3})
|
||||
l.MoveToBack(e3) // should be no-op
|
||||
checkListPointers(t, l, []*Element{e1, e4, e3})
|
||||
|
||||
e2 = l.InsertBefore(e1, 2) // insert before front
|
||||
checkListPointers(t, l, []*Element{e2, e1, e4, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertBefore(e4, 2) // insert before middle
|
||||
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertBefore(e3, 2) // insert before back
|
||||
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
|
||||
l.Remove(e2)
|
||||
|
||||
e2 = l.InsertAfter(e1, 2) // insert after front
|
||||
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertAfter(e4, 2) // insert after middle
|
||||
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
|
||||
l.Remove(e2)
|
||||
e2 = l.InsertAfter(e3, 2) // insert after back
|
||||
checkListPointers(t, l, []*Element{e1, e4, e3, e2})
|
||||
l.Remove(e2)
|
||||
|
||||
// Check standard iteration.
|
||||
sum := 0
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
if i, ok := e.Value.(int); ok {
|
||||
sum += i
|
||||
}
|
||||
}
|
||||
if sum != 4 {
|
||||
t.Errorf("sum over l = %d, want 4", sum)
|
||||
}
|
||||
|
||||
// Clear all elements by iterating
|
||||
var next *Element
|
||||
for e := l.Front(); e != nil; e = next {
|
||||
next = e.Next()
|
||||
l.Remove(e)
|
||||
}
|
||||
checkListPointers(t, l, []*Element{})
|
||||
})
|
||||
}
|
||||
|
||||
func checkList(t *testing.T, l *List, es []interface{}) {
|
||||
func checkList(t *gtest.T, l *List, es []interface{}) {
|
||||
if !checkListLen(t, l, len(es)) {
|
||||
return
|
||||
}
|
||||
@ -193,60 +231,64 @@ func checkList(t *testing.T, l *List, es []interface{}) {
|
||||
}
|
||||
|
||||
func TestExtending(t *testing.T) {
|
||||
l1 := New()
|
||||
l2 := New()
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l1 := New()
|
||||
l2 := New()
|
||||
|
||||
l1.PushBack(1)
|
||||
l1.PushBack(2)
|
||||
l1.PushBack(3)
|
||||
l1.PushBack(1)
|
||||
l1.PushBack(2)
|
||||
l1.PushBack(3)
|
||||
|
||||
l2.PushBack(4)
|
||||
l2.PushBack(5)
|
||||
l2.PushBack(4)
|
||||
l2.PushBack(5)
|
||||
|
||||
l3 := New()
|
||||
l3.PushBackList(l1)
|
||||
checkList(t, l3, []interface{}{1, 2, 3})
|
||||
l3.PushBackList(l2)
|
||||
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
|
||||
l3 := New()
|
||||
l3.PushBackList(l1)
|
||||
checkList(t, l3, []interface{}{1, 2, 3})
|
||||
l3.PushBackList(l2)
|
||||
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
|
||||
|
||||
l3 = New()
|
||||
l3.PushFrontList(l2)
|
||||
checkList(t, l3, []interface{}{4, 5})
|
||||
l3.PushFrontList(l1)
|
||||
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
|
||||
l3 = New()
|
||||
l3.PushFrontList(l2)
|
||||
checkList(t, l3, []interface{}{4, 5})
|
||||
l3.PushFrontList(l1)
|
||||
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
|
||||
|
||||
checkList(t, l1, []interface{}{1, 2, 3})
|
||||
checkList(t, l2, []interface{}{4, 5})
|
||||
checkList(t, l1, []interface{}{1, 2, 3})
|
||||
checkList(t, l2, []interface{}{4, 5})
|
||||
|
||||
l3 = New()
|
||||
l3.PushBackList(l1)
|
||||
checkList(t, l3, []interface{}{1, 2, 3})
|
||||
l3.PushBackList(l3)
|
||||
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
|
||||
l3 = New()
|
||||
l3.PushBackList(l1)
|
||||
checkList(t, l3, []interface{}{1, 2, 3})
|
||||
l3.PushBackList(l3)
|
||||
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
|
||||
|
||||
l3 = New()
|
||||
l3.PushFrontList(l1)
|
||||
checkList(t, l3, []interface{}{1, 2, 3})
|
||||
l3.PushFrontList(l3)
|
||||
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
|
||||
l3 = New()
|
||||
l3.PushFrontList(l1)
|
||||
checkList(t, l3, []interface{}{1, 2, 3})
|
||||
l3.PushFrontList(l3)
|
||||
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
|
||||
|
||||
l3 = New()
|
||||
l1.PushBackList(l3)
|
||||
checkList(t, l1, []interface{}{1, 2, 3})
|
||||
l1.PushFrontList(l3)
|
||||
checkList(t, l1, []interface{}{1, 2, 3})
|
||||
l3 = New()
|
||||
l1.PushBackList(l3)
|
||||
checkList(t, l1, []interface{}{1, 2, 3})
|
||||
l1.PushFrontList(l3)
|
||||
checkList(t, l1, []interface{}{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
l := New()
|
||||
e1 := l.PushBack(1)
|
||||
e2 := l.PushBack(2)
|
||||
checkListPointers(t, l, []*Element{e1, e2})
|
||||
//e := l.Front()
|
||||
//l.Remove(e)
|
||||
//checkListPointers(t, l, []*Element{e2})
|
||||
//l.Remove(e)
|
||||
//checkListPointers(t, l, []*Element{e2})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
e1 := l.PushBack(1)
|
||||
e2 := l.PushBack(2)
|
||||
checkListPointers(t, l, []*Element{e1, e2})
|
||||
//e := l.Front()
|
||||
//l.Remove(e)
|
||||
//checkListPointers(t, l, []*Element{e2})
|
||||
//l.Remove(e)
|
||||
//checkListPointers(t, l, []*Element{e2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIssue4103(t *testing.T) {
|
||||
@ -289,346 +331,427 @@ func TestIssue6349(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMove(t *testing.T) {
|
||||
l := New()
|
||||
e1 := l.PushBack(1)
|
||||
e2 := l.PushBack(2)
|
||||
e3 := l.PushBack(3)
|
||||
e4 := l.PushBack(4)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
e1 := l.PushBack(1)
|
||||
e2 := l.PushBack(2)
|
||||
e3 := l.PushBack(3)
|
||||
e4 := l.PushBack(4)
|
||||
|
||||
l.MoveAfter(e3, e3)
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
l.MoveBefore(e2, e2)
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
l.MoveAfter(e3, e3)
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
l.MoveBefore(e2, e2)
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
|
||||
l.MoveAfter(e3, e2)
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
l.MoveBefore(e2, e3)
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
l.MoveAfter(e3, e2)
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
l.MoveBefore(e2, e3)
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
|
||||
l.MoveBefore(e2, e4)
|
||||
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
|
||||
e2, e3 = e3, e2
|
||||
l.MoveBefore(e2, e4)
|
||||
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
|
||||
e2, e3 = e3, e2
|
||||
|
||||
l.MoveBefore(e4, e1)
|
||||
checkListPointers(t, l, []*Element{e4, e1, e2, e3})
|
||||
e1, e2, e3, e4 = e4, e1, e2, e3
|
||||
l.MoveBefore(e4, e1)
|
||||
checkListPointers(t, l, []*Element{e4, e1, e2, e3})
|
||||
e1, e2, e3, e4 = e4, e1, e2, e3
|
||||
|
||||
l.MoveAfter(e4, e1)
|
||||
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
|
||||
e2, e3, e4 = e4, e2, e3
|
||||
l.MoveAfter(e4, e1)
|
||||
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
|
||||
e2, e3, e4 = e4, e2, e3
|
||||
|
||||
l.MoveAfter(e2, e3)
|
||||
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
|
||||
e2, e3 = e3, e2
|
||||
l.MoveAfter(e2, e3)
|
||||
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
|
||||
e2, e3 = e3, e2
|
||||
})
|
||||
}
|
||||
|
||||
// Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized List
|
||||
func TestZeroList(t *testing.T) {
|
||||
var l1 = New()
|
||||
l1.PushFront(1)
|
||||
checkList(t, l1, []interface{}{1})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var l1 = New()
|
||||
l1.PushFront(1)
|
||||
checkList(t, l1, []interface{}{1})
|
||||
|
||||
var l2 = New()
|
||||
l2.PushBack(1)
|
||||
checkList(t, l2, []interface{}{1})
|
||||
var l2 = New()
|
||||
l2.PushBack(1)
|
||||
checkList(t, l2, []interface{}{1})
|
||||
|
||||
var l3 = New()
|
||||
l3.PushFrontList(l1)
|
||||
checkList(t, l3, []interface{}{1})
|
||||
var l3 = New()
|
||||
l3.PushFrontList(l1)
|
||||
checkList(t, l3, []interface{}{1})
|
||||
|
||||
var l4 = New()
|
||||
l4.PushBackList(l2)
|
||||
checkList(t, l4, []interface{}{1})
|
||||
var l4 = New()
|
||||
l4.PushBackList(l2)
|
||||
checkList(t, l4, []interface{}{1})
|
||||
})
|
||||
}
|
||||
|
||||
// Test that a list l is not modified when calling InsertBefore with a mark that is not an element of l.
|
||||
func TestInsertBeforeUnknownMark(t *testing.T) {
|
||||
l := New()
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
l.PushBack(3)
|
||||
l.InsertBefore(new(Element), 1)
|
||||
checkList(t, l, []interface{}{1, 2, 3})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
l.PushBack(3)
|
||||
l.InsertBefore(new(Element), 1)
|
||||
checkList(t, l, []interface{}{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
// Test that a list l is not modified when calling InsertAfter with a mark that is not an element of l.
|
||||
func TestInsertAfterUnknownMark(t *testing.T) {
|
||||
l := New()
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
l.PushBack(3)
|
||||
l.InsertAfter(new(Element), 1)
|
||||
checkList(t, l, []interface{}{1, 2, 3})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
l.PushBack(3)
|
||||
l.InsertAfter(new(Element), 1)
|
||||
checkList(t, l, []interface{}{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
// Test that a list l is not modified when calling MoveAfter or MoveBefore with a mark that is not an element of l.
|
||||
func TestMoveUnknownMark(t *testing.T) {
|
||||
l1 := New()
|
||||
e1 := l1.PushBack(1)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l1 := New()
|
||||
e1 := l1.PushBack(1)
|
||||
|
||||
l2 := New()
|
||||
e2 := l2.PushBack(2)
|
||||
l2 := New()
|
||||
e2 := l2.PushBack(2)
|
||||
|
||||
l1.MoveAfter(e1, e2)
|
||||
checkList(t, l1, []interface{}{1})
|
||||
checkList(t, l2, []interface{}{2})
|
||||
l1.MoveAfter(e1, e2)
|
||||
checkList(t, l1, []interface{}{1})
|
||||
checkList(t, l2, []interface{}{2})
|
||||
|
||||
l1.MoveBefore(e1, e2)
|
||||
checkList(t, l1, []interface{}{1})
|
||||
checkList(t, l2, []interface{}{2})
|
||||
l1.MoveBefore(e1, e2)
|
||||
checkList(t, l1, []interface{}{1})
|
||||
checkList(t, l2, []interface{}{2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_RemoveAll(t *testing.T) {
|
||||
l := New()
|
||||
l.PushBack(1)
|
||||
l.RemoveAll()
|
||||
checkList(t, l, []interface{}{})
|
||||
l.PushBack(2)
|
||||
checkList(t, l, []interface{}{2})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
l.PushBack(1)
|
||||
l.RemoveAll()
|
||||
checkList(t, l, []interface{}{})
|
||||
l.PushBack(2)
|
||||
checkList(t, l, []interface{}{2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_PushFronts(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2}
|
||||
l.PushFronts(a1)
|
||||
checkList(t, l, []interface{}{2, 1})
|
||||
a1 = []interface{}{3, 4, 5}
|
||||
l.PushFronts(a1)
|
||||
checkList(t, l, []interface{}{5, 4, 3, 2, 1})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2}
|
||||
l.PushFronts(a1)
|
||||
checkList(t, l, []interface{}{2, 1})
|
||||
a1 = []interface{}{3, 4, 5}
|
||||
l.PushFronts(a1)
|
||||
checkList(t, l, []interface{}{5, 4, 3, 2, 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_PushBacks(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2}
|
||||
l.PushBacks(a1)
|
||||
checkList(t, l, []interface{}{1, 2})
|
||||
a1 = []interface{}{3, 4, 5}
|
||||
l.PushBacks(a1)
|
||||
checkList(t, l, []interface{}{1, 2, 3, 4, 5})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2}
|
||||
l.PushBacks(a1)
|
||||
checkList(t, l, []interface{}{1, 2})
|
||||
a1 = []interface{}{3, 4, 5}
|
||||
l.PushBacks(a1)
|
||||
checkList(t, l, []interface{}{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_PopBacks(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
a2 := []interface{}{"a", "c", "b", "e"}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopBacks(2)
|
||||
gtest.Assert(i1, []interface{}{1, 2})
|
||||
t.Assert(i1, []interface{}{1, 2})
|
||||
|
||||
l.PushBacks(a2) //4.3,a,c,b,e
|
||||
i1 = l.PopBacks(3)
|
||||
gtest.Assert(i1, []interface{}{"e", "b", "c"})
|
||||
t.Assert(i1, []interface{}{"e", "b", "c"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_PopFronts(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopFronts(2)
|
||||
gtest.Assert(i1, []interface{}{4, 3})
|
||||
gtest.Assert(l.Len(), 2)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopFronts(2)
|
||||
t.Assert(i1, []interface{}{4, 3})
|
||||
t.Assert(l.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_PopBackAll(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopBackAll()
|
||||
gtest.Assert(i1, []interface{}{1, 2, 3, 4})
|
||||
gtest.Assert(l.Len(), 0)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopBackAll()
|
||||
t.Assert(i1, []interface{}{1, 2, 3, 4})
|
||||
t.Assert(l.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_PopFrontAll(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopFrontAll()
|
||||
gtest.Assert(i1, []interface{}{4, 3, 2, 1})
|
||||
gtest.Assert(l.Len(), 0)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopFrontAll()
|
||||
t.Assert(i1, []interface{}{4, 3, 2, 1})
|
||||
t.Assert(l.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_FrontAll(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.FrontAll()
|
||||
gtest.Assert(i1, []interface{}{4, 3, 2, 1})
|
||||
gtest.Assert(l.Len(), 4)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.FrontAll()
|
||||
t.Assert(i1, []interface{}{4, 3, 2, 1})
|
||||
t.Assert(l.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_BackAll(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.BackAll()
|
||||
gtest.Assert(i1, []interface{}{1, 2, 3, 4})
|
||||
gtest.Assert(l.Len(), 4)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.BackAll()
|
||||
t.Assert(i1, []interface{}{1, 2, 3, 4})
|
||||
t.Assert(l.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_FrontValue(t *testing.T) {
|
||||
l := New()
|
||||
l2 := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.FrontValue()
|
||||
gtest.Assert(gconv.Int(i1), 4)
|
||||
gtest.Assert(l.Len(), 4)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
l2 := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.FrontValue()
|
||||
t.Assert(gconv.Int(i1), 4)
|
||||
t.Assert(l.Len(), 4)
|
||||
|
||||
i1 = l2.FrontValue()
|
||||
gtest.Assert(i1, nil)
|
||||
i1 = l2.FrontValue()
|
||||
t.Assert(i1, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_BackValue(t *testing.T) {
|
||||
l := New()
|
||||
l2 := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.BackValue()
|
||||
gtest.Assert(gconv.Int(i1), 1)
|
||||
gtest.Assert(l.Len(), 4)
|
||||
|
||||
i1 = l2.FrontValue()
|
||||
gtest.Assert(i1, nil)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
l2 := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.BackValue()
|
||||
t.Assert(gconv.Int(i1), 1)
|
||||
t.Assert(l.Len(), 4)
|
||||
|
||||
i1 = l2.FrontValue()
|
||||
t.Assert(i1, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Back(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
gtest.Assert(e1.Value, 1)
|
||||
gtest.Assert(l.Len(), 4)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
t.Assert(e1.Value, 1)
|
||||
t.Assert(l.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Size(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
gtest.Assert(l.Size(), 4)
|
||||
l.PopFront()
|
||||
gtest.Assert(l.Size(), 3)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
t.Assert(l.Size(), 4)
|
||||
l.PopFront()
|
||||
t.Assert(l.Size(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Removes(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
l.Removes([]*Element{e1})
|
||||
gtest.Assert(l.Len(), 3)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
l.Removes([]*Element{e1})
|
||||
t.Assert(l.Len(), 3)
|
||||
|
||||
e2 := l.Back()
|
||||
l.Removes([]*Element{e2})
|
||||
gtest.Assert(l.Len(), 2)
|
||||
checkList(t, l, []interface{}{4, 3})
|
||||
e2 := l.Back()
|
||||
l.Removes([]*Element{e2})
|
||||
t.Assert(l.Len(), 2)
|
||||
checkList(t, l, []interface{}{4, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Pop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
t.Assert(l.PopBack(), 9)
|
||||
t.Assert(l.PopBacks(2), []interface{}{8, 7})
|
||||
t.Assert(l.PopFront(), 1)
|
||||
t.Assert(l.PopFronts(2), []interface{}{2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Clear(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
l.Clear()
|
||||
gtest.Assert(l.Len(), 0)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
l.Clear()
|
||||
t.Assert(l.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_IteratorAsc(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 5, 6, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
fun1 := func(e *Element) bool {
|
||||
if gconv.Int(e1.Value) > 2 {
|
||||
return true
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 5, 6, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
fun1 := func(e *Element) bool {
|
||||
if gconv.Int(e1.Value) > 2 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
checkList(t, l, []interface{}{4, 3, 6, 5, 2, 1})
|
||||
l.IteratorAsc(fun1)
|
||||
checkList(t, l, []interface{}{4, 3, 6, 5, 2, 1})
|
||||
checkList(t, l, []interface{}{4, 3, 6, 5, 2, 1})
|
||||
l.IteratorAsc(fun1)
|
||||
checkList(t, l, []interface{}{4, 3, 6, 5, 2, 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_IteratorDesc(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
fun1 := func(e *Element) bool {
|
||||
if gconv.Int(e1.Value) > 6 {
|
||||
return true
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
fun1 := func(e *Element) bool {
|
||||
if gconv.Int(e1.Value) > 6 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
l.IteratorDesc(fun1)
|
||||
gtest.Assert(l.Len(), 4)
|
||||
checkList(t, l, []interface{}{4, 3, 2, 1})
|
||||
l.IteratorDesc(fun1)
|
||||
t.Assert(l.Len(), 4)
|
||||
checkList(t, l, []interface{}{4, 3, 2, 1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Iterator(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{"a", "b", "c", "d", "e"}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
fun1 := func(e *Element) bool {
|
||||
if gconv.String(e1.Value) > "c" {
|
||||
return true
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{"a", "b", "c", "d", "e"}
|
||||
l.PushFronts(a1)
|
||||
e1 := l.Back()
|
||||
fun1 := func(e *Element) bool {
|
||||
if gconv.String(e1.Value) > "c" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
checkList(t, l, []interface{}{"e", "d", "c", "b", "a"})
|
||||
l.Iterator(fun1)
|
||||
checkList(t, l, []interface{}{"e", "d", "c", "b", "a"})
|
||||
checkList(t, l, []interface{}{"e", "d", "c", "b", "a"})
|
||||
l.Iterator(fun1)
|
||||
checkList(t, l, []interface{}{"e", "d", "c", "b", "a"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Join(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
|
||||
gtest.Assert(l.Join(","), `1,2,"a","\"b\"","\\c"`)
|
||||
gtest.Assert(l.Join("."), `1.2."a"."\"b\""."\\c"`)
|
||||
t.Assert(l.Join(","), `1,2,a,"b",\c`)
|
||||
t.Assert(l.Join("."), `1.2.a."b".\c`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_String(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
|
||||
gtest.Assert(l.String(), `[1,2,"a","\"b\"","\\c"]`)
|
||||
t.Assert(l.String(), `[1,2,a,"b",\c]`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := []interface{}{"a", "b", "c"}
|
||||
l := New()
|
||||
l.PushBacks(a)
|
||||
b1, err1 := json.Marshal(l)
|
||||
b2, err2 := json.Marshal(a)
|
||||
gtest.Assert(err1, err2)
|
||||
gtest.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := []interface{}{"a", "b", "c"}
|
||||
l := New()
|
||||
b, err := json.Marshal(a)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
err = json.Unmarshal(b, l)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(l.FrontAll(), a)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(l.FrontAll(), a)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var l List
|
||||
a := []interface{}{"a", "b", "c"}
|
||||
b, err := json.Marshal(a)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
err = json.Unmarshal(b, &l)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(l.FrontAll(), a)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(l.FrontAll(), a)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_UnmarshalValue(t *testing.T) {
|
||||
type TList struct {
|
||||
Name string
|
||||
List *List
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tlist *TList
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"list": []byte(`[1,2,3]`),
|
||||
}, &tlist)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(tlist.Name, "john")
|
||||
t.Assert(tlist.List.FrontAll(), []interface{}{1, 2, 3})
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tlist *TList
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"list": []interface{}{1, 2, 3},
|
||||
}, &tlist)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(tlist.Name, "john")
|
||||
t.Assert(tlist.List.FrontAll(), []interface{}{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
@ -4,12 +4,13 @@
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gmap provides concurrent-safe/unsafe map containers.
|
||||
// Package gmap provides most commonly used map container which also support concurrent-safe/unsafe switch feature.
|
||||
package gmap
|
||||
|
||||
// Map based on hash table, alias of AnyAnyMap.
|
||||
type Map = AnyAnyMap
|
||||
type HashMap = AnyAnyMap
|
||||
type (
|
||||
Map = AnyAnyMap // Map is alias of AnyAnyMap.
|
||||
HashMap = AnyAnyMap // HashMap is alias of AnyAnyMap.
|
||||
)
|
||||
|
||||
// New creates and returns an empty hash map.
|
||||
// The parameter <safe> is used to specify whether using map in concurrent-safety,
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
type AnyAnyMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[interface{}]interface{}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ type AnyAnyMap struct {
|
||||
// which is false in default.
|
||||
func NewAnyAnyMap(safe ...bool) *AnyAnyMap {
|
||||
return &AnyAnyMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[interface{}]interface{}),
|
||||
}
|
||||
}
|
||||
@ -37,12 +37,12 @@ func NewAnyAnyMap(safe ...bool) *AnyAnyMap {
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewAnyAnyMapFrom(data map[interface{}]interface{}, safe ...bool) *AnyAnyMap {
|
||||
return &AnyAnyMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map readonly with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *AnyAnyMap) Iterator(f func(k interface{}, v interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
@ -89,37 +89,56 @@ func (m *AnyAnyMap) MapCopy() map[interface{}]interface{} {
|
||||
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
|
||||
func (m *AnyAnyMap) MapStrAny() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[gconv.String(k)] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *AnyAnyMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsEmpty(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// FilterNil deletes all key-value pair of which the value is nil.
|
||||
func (m *AnyAnyMap) FilterNil() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsNil(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *AnyAnyMap) Set(key interface{}, val interface{}) {
|
||||
func (m *AnyAnyMap) Set(key interface{}, value interface{}) {
|
||||
m.mu.Lock()
|
||||
m.data[key] = val
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
m.data[key] = value
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *AnyAnyMap) Sets(data map[interface{}]interface{}) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -128,17 +147,21 @@ func (m *AnyAnyMap) Sets(data map[interface{}]interface{}) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *AnyAnyMap) Search(key interface{}) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *AnyAnyMap) Get(key interface{}) interface{} {
|
||||
func (m *AnyAnyMap) Get(key interface{}) (value interface{}) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
@ -163,8 +186,10 @@ func (m *AnyAnyMap) Pops(size int) map[interface{}]interface{} {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[interface{}]interface{}, size)
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[interface{}]interface{}, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
@ -188,6 +213,9 @@ func (m *AnyAnyMap) Pops(size int) map[interface{}]interface{} {
|
||||
func (m *AnyAnyMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
@ -235,26 +263,26 @@ func (m *AnyAnyMap) GetOrSetFuncLock(key interface{}, f func() interface{}) inte
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVar returns a Var with the value by given <key>.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVar(key interface{}) *gvar.Var {
|
||||
return gvar.New(m.Get(key))
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSet returns a Var with result from GetVarOrSet.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value))
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f))
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f))
|
||||
}
|
||||
@ -293,21 +321,25 @@ func (m *AnyAnyMap) SetIfNotExistFuncLock(key interface{}, f func() interface{})
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *AnyAnyMap) Remove(key interface{}) interface{} {
|
||||
func (m *AnyAnyMap) Remove(key interface{}) (value interface{}) {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *AnyAnyMap) Removes(keys []interface{}) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -315,36 +347,43 @@ func (m *AnyAnyMap) Removes(keys []interface{}) {
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *AnyAnyMap) Keys() []interface{} {
|
||||
m.mu.RLock()
|
||||
keys := make([]interface{}, len(m.data))
|
||||
index := 0
|
||||
defer m.mu.RUnlock()
|
||||
var (
|
||||
keys = make([]interface{}, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *AnyAnyMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
values := make([]interface{}, len(m.data))
|
||||
index := 0
|
||||
defer m.mu.RUnlock()
|
||||
var (
|
||||
values = make([]interface{}, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *AnyAnyMap) Contains(key interface{}) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
@ -405,6 +444,10 @@ func (m *AnyAnyMap) Flip() {
|
||||
func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -421,12 +464,11 @@ func (m *AnyAnyMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *AnyAnyMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal(b, &data); err != nil {
|
||||
return err
|
||||
@ -436,3 +478,16 @@ func (m *AnyAnyMap) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *AnyAnyMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[k] = v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
type IntAnyMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[int]interface{}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ type IntAnyMap struct {
|
||||
// which is false in default.
|
||||
func NewIntAnyMap(safe ...bool) *IntAnyMap {
|
||||
return &IntAnyMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[int]interface{}),
|
||||
}
|
||||
}
|
||||
@ -37,12 +37,12 @@ func NewIntAnyMap(safe ...bool) *IntAnyMap {
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntAnyMapFrom(data map[int]interface{}, safe ...bool) *IntAnyMap {
|
||||
return &IntAnyMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map readonly with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *IntAnyMap) Iterator(f func(k int, v interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
@ -98,6 +98,7 @@ func (m *IntAnyMap) MapCopy() map[int]interface{} {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *IntAnyMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
@ -108,9 +109,23 @@ func (m *IntAnyMap) FilterEmpty() {
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// FilterNil deletes all key-value pair of which the value is nil.
|
||||
func (m *IntAnyMap) FilterNil() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsNil(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *IntAnyMap) Set(key int, val interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -118,8 +133,12 @@ func (m *IntAnyMap) Set(key int, val interface{}) {
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *IntAnyMap) Sets(data map[int]interface{}) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -128,17 +147,21 @@ func (m *IntAnyMap) Sets(data map[int]interface{}) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *IntAnyMap) Search(key int) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *IntAnyMap) Get(key int) interface{} {
|
||||
func (m *IntAnyMap) Get(key int) (value interface{}) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
@ -163,8 +186,10 @@ func (m *IntAnyMap) Pops(size int) map[int]interface{} {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[int]interface{}, size)
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[int]interface{}, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
@ -188,6 +213,9 @@ func (m *IntAnyMap) Pops(size int) map[int]interface{} {
|
||||
func (m *IntAnyMap) doSetWithLockCheck(key int, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
@ -233,26 +261,26 @@ func (m *IntAnyMap) GetOrSetFuncLock(key int, f func() interface{}) interface{}
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVar returns a Var with the value by given <key>.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVar(key int) *gvar.Var {
|
||||
return gvar.New(m.Get(key))
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSet returns a Var with result from GetVarOrSet.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVarOrSet(key int, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value))
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVarOrSetFunc(key int, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f))
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVarOrSetFuncLock(key int, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f))
|
||||
}
|
||||
@ -293,28 +321,34 @@ func (m *IntAnyMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool {
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *IntAnyMap) Removes(keys []int) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *IntAnyMap) Remove(key int) interface{} {
|
||||
func (m *IntAnyMap) Remove(key int) (value interface{}) {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *IntAnyMap) Keys() []int {
|
||||
m.mu.RLock()
|
||||
keys := make([]int, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
keys = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
@ -326,8 +360,10 @@ func (m *IntAnyMap) Keys() []int {
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *IntAnyMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
values := make([]interface{}, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
values = make([]interface{}, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
@ -339,10 +375,13 @@ func (m *IntAnyMap) Values() []interface{} {
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *IntAnyMap) Contains(key int) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
@ -403,6 +442,10 @@ func (m *IntAnyMap) Flip() {
|
||||
func (m *IntAnyMap) Merge(other *IntAnyMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -421,14 +464,31 @@ func (m *IntAnyMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *IntAnyMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *IntAnyMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &m.data)
|
||||
default:
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[gconv.Int(k)] = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
type IntIntMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[int]int
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ type IntIntMap struct {
|
||||
// which is false in default.
|
||||
func NewIntIntMap(safe ...bool) *IntIntMap {
|
||||
return &IntIntMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[int]int),
|
||||
}
|
||||
}
|
||||
@ -35,12 +35,12 @@ func NewIntIntMap(safe ...bool) *IntIntMap {
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntIntMapFrom(data map[int]int, safe ...bool) *IntIntMap {
|
||||
return &IntIntMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map readonly with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *IntIntMap) Iterator(f func(k int, v int) bool) {
|
||||
m.mu.RLock()
|
||||
@ -96,6 +96,7 @@ func (m *IntIntMap) MapCopy() map[int]int {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *IntIntMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
@ -109,6 +110,9 @@ func (m *IntIntMap) FilterEmpty() {
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *IntIntMap) Set(key int, val int) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -116,8 +120,12 @@ func (m *IntIntMap) Set(key int, val int) {
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *IntIntMap) Sets(data map[int]int) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -126,17 +134,21 @@ func (m *IntIntMap) Sets(data map[int]int) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *IntIntMap) Search(key int) (value int, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *IntIntMap) Get(key int) int {
|
||||
func (m *IntIntMap) Get(key int) (value int) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
@ -161,8 +173,10 @@ func (m *IntIntMap) Pops(size int) map[int]int {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[int]int, size)
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[int]int, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
@ -181,12 +195,14 @@ func (m *IntIntMap) Pops(size int) map[int]int {
|
||||
// It returns value with given <key>.
|
||||
func (m *IntIntMap) doSetWithLockCheck(key int, value int) int {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
m.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
m.data[key] = value
|
||||
m.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
@ -219,6 +235,9 @@ func (m *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
@ -259,6 +278,9 @@ func (m *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
@ -270,28 +292,34 @@ func (m *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *IntIntMap) Removes(keys []int) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *IntIntMap) Remove(key int) int {
|
||||
func (m *IntIntMap) Remove(key int) (value int) {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *IntIntMap) Keys() []int {
|
||||
m.mu.RLock()
|
||||
keys := make([]int, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
keys = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
@ -303,8 +331,10 @@ func (m *IntIntMap) Keys() []int {
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *IntIntMap) Values() []int {
|
||||
m.mu.RLock()
|
||||
values := make([]int, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
values = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
@ -316,10 +346,13 @@ func (m *IntIntMap) Values() []int {
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *IntIntMap) Contains(key int) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
@ -380,6 +413,10 @@ func (m *IntIntMap) Flip() {
|
||||
func (m *IntIntMap) Merge(other *IntIntMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -398,14 +435,31 @@ func (m *IntIntMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *IntIntMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *IntIntMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &m.data)
|
||||
default:
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[gconv.Int(k)] = gconv.Int(v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
type IntStrMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[int]string
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ type IntStrMap struct {
|
||||
// which is false in default.
|
||||
func NewIntStrMap(safe ...bool) *IntStrMap {
|
||||
return &IntStrMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[int]string),
|
||||
}
|
||||
}
|
||||
@ -35,12 +35,12 @@ func NewIntStrMap(safe ...bool) *IntStrMap {
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntStrMapFrom(data map[int]string, safe ...bool) *IntStrMap {
|
||||
return &IntStrMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map readonly with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *IntStrMap) Iterator(f func(k int, v string) bool) {
|
||||
m.mu.RLock()
|
||||
@ -96,6 +96,7 @@ func (m *IntStrMap) MapCopy() map[int]string {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *IntStrMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
@ -109,6 +110,9 @@ func (m *IntStrMap) FilterEmpty() {
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *IntStrMap) Set(key int, val string) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -116,8 +120,12 @@ func (m *IntStrMap) Set(key int, val string) {
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *IntStrMap) Sets(data map[int]string) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -126,17 +134,21 @@ func (m *IntStrMap) Sets(data map[int]string) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *IntStrMap) Search(key int) (value string, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *IntStrMap) Get(key int) string {
|
||||
func (m *IntStrMap) Get(key int) (value string) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
@ -161,8 +173,10 @@ func (m *IntStrMap) Pops(size int) map[int]string {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[int]string, size)
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[int]string, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
@ -182,6 +196,9 @@ func (m *IntStrMap) Pops(size int) map[int]string {
|
||||
func (m *IntStrMap) doSetWithLockCheck(key int, value string) string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
@ -218,13 +235,14 @@ func (m *IntStrMap) GetOrSetFuncLock(key int, f func() string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
v = f()
|
||||
if v != "" {
|
||||
m.data[key] = v
|
||||
}
|
||||
m.data[key] = v
|
||||
return v
|
||||
} else {
|
||||
return v
|
||||
@ -260,6 +278,9 @@ func (m *IntStrMap) SetIfNotExistFuncLock(key int, f func() string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
@ -271,28 +292,34 @@ func (m *IntStrMap) SetIfNotExistFuncLock(key int, f func() string) bool {
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *IntStrMap) Removes(keys []int) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *IntStrMap) Remove(key int) string {
|
||||
func (m *IntStrMap) Remove(key int) (value string) {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *IntStrMap) Keys() []int {
|
||||
m.mu.RLock()
|
||||
keys := make([]int, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
keys = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
@ -304,8 +331,10 @@ func (m *IntStrMap) Keys() []int {
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *IntStrMap) Values() []string {
|
||||
m.mu.RLock()
|
||||
values := make([]string, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
values = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
@ -317,10 +346,13 @@ func (m *IntStrMap) Values() []string {
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *IntStrMap) Contains(key int) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
@ -381,6 +413,10 @@ func (m *IntStrMap) Flip() {
|
||||
func (m *IntStrMap) Merge(other *IntStrMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -399,14 +435,31 @@ func (m *IntStrMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *IntStrMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *IntStrMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &m.data)
|
||||
default:
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[gconv.Int(k)] = gconv.String(v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
type StrAnyMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[string]interface{}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ type StrAnyMap struct {
|
||||
// which is false in default.
|
||||
func NewStrAnyMap(safe ...bool) *StrAnyMap {
|
||||
return &StrAnyMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
@ -37,12 +37,12 @@ func NewStrAnyMap(safe ...bool) *StrAnyMap {
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStrAnyMapFrom(data map[string]interface{}, safe ...bool) *StrAnyMap {
|
||||
return &StrAnyMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map readonly with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *StrAnyMap) Iterator(f func(k string, v interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
@ -92,6 +92,7 @@ func (m *StrAnyMap) MapCopy() map[string]interface{} {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *StrAnyMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
@ -102,9 +103,23 @@ func (m *StrAnyMap) FilterEmpty() {
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// FilterNil deletes all key-value pair of which the value is nil.
|
||||
func (m *StrAnyMap) FilterNil() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsNil(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *StrAnyMap) Set(key string, val interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]interface{})
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -112,8 +127,12 @@ func (m *StrAnyMap) Set(key string, val interface{}) {
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *StrAnyMap) Sets(data map[string]interface{}) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -122,17 +141,21 @@ func (m *StrAnyMap) Sets(data map[string]interface{}) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *StrAnyMap) Search(key string) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *StrAnyMap) Get(key string) interface{} {
|
||||
func (m *StrAnyMap) Get(key string) (value interface{}) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
@ -157,8 +180,10 @@ func (m *StrAnyMap) Pops(size int) map[string]interface{} {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[string]interface{}, size)
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[string]interface{}, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
@ -182,6 +207,9 @@ func (m *StrAnyMap) Pops(size int) map[string]interface{} {
|
||||
func (m *StrAnyMap) doSetWithLockCheck(key string, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]interface{})
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
@ -229,26 +257,26 @@ func (m *StrAnyMap) GetOrSetFuncLock(key string, f func() interface{}) interface
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVar returns a Var with the value by given <key>.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVar(key string) *gvar.Var {
|
||||
return gvar.New(m.Get(key))
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSet returns a Var with result from GetVarOrSet.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVarOrSet(key string, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value))
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVarOrSetFunc(key string, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f))
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVarOrSetFuncLock(key string, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f))
|
||||
}
|
||||
@ -289,28 +317,34 @@ func (m *StrAnyMap) SetIfNotExistFuncLock(key string, f func() interface{}) bool
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *StrAnyMap) Removes(keys []string) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *StrAnyMap) Remove(key string) interface{} {
|
||||
func (m *StrAnyMap) Remove(key string) (value interface{}) {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *StrAnyMap) Keys() []string {
|
||||
m.mu.RLock()
|
||||
keys := make([]string, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
keys = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
@ -322,8 +356,10 @@ func (m *StrAnyMap) Keys() []string {
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *StrAnyMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
values := make([]interface{}, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
values = make([]interface{}, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
@ -335,10 +371,13 @@ func (m *StrAnyMap) Values() []interface{} {
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *StrAnyMap) Contains(key string) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
@ -399,6 +438,10 @@ func (m *StrAnyMap) Flip() {
|
||||
func (m *StrAnyMap) Merge(other *StrAnyMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -417,14 +460,21 @@ func (m *StrAnyMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *StrAnyMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[string]interface{})
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]interface{})
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *StrAnyMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.data = gconv.Map(value)
|
||||
return
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
type StrIntMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[string]int
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ type StrIntMap struct {
|
||||
// which is false in default.
|
||||
func NewStrIntMap(safe ...bool) *StrIntMap {
|
||||
return &StrIntMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[string]int),
|
||||
}
|
||||
}
|
||||
@ -35,12 +35,12 @@ func NewStrIntMap(safe ...bool) *StrIntMap {
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStrIntMapFrom(data map[string]int, safe ...bool) *StrIntMap {
|
||||
return &StrIntMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map readonly with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *StrIntMap) Iterator(f func(k string, v int) bool) {
|
||||
m.mu.RLock()
|
||||
@ -76,11 +76,11 @@ func (m *StrIntMap) Map() map[string]int {
|
||||
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
|
||||
func (m *StrIntMap) MapStrAny() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
@ -96,6 +96,7 @@ func (m *StrIntMap) MapCopy() map[string]int {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *StrIntMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
@ -109,6 +110,9 @@ func (m *StrIntMap) FilterEmpty() {
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *StrIntMap) Set(key string, val int) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -116,8 +120,12 @@ func (m *StrIntMap) Set(key string, val int) {
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *StrIntMap) Sets(data map[string]int) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -126,17 +134,21 @@ func (m *StrIntMap) Sets(data map[string]int) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *StrIntMap) Search(key string) (value int, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *StrIntMap) Get(key string) int {
|
||||
func (m *StrIntMap) Get(key string) (value int) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
@ -161,8 +173,10 @@ func (m *StrIntMap) Pops(size int) map[string]int {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[string]int, size)
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[string]int, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
@ -181,6 +195,9 @@ func (m *StrIntMap) Pops(size int) map[string]int {
|
||||
// It returns value with given <key>.
|
||||
func (m *StrIntMap) doSetWithLockCheck(key string, value int) int {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
m.mu.Unlock()
|
||||
return v
|
||||
@ -221,6 +238,9 @@ func (m *StrIntMap) GetOrSetFuncLock(key string, f func() int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
@ -261,6 +281,9 @@ func (m *StrIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
@ -272,28 +295,34 @@ func (m *StrIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *StrIntMap) Removes(keys []string) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *StrIntMap) Remove(key string) int {
|
||||
func (m *StrIntMap) Remove(key string) (value int) {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *StrIntMap) Keys() []string {
|
||||
m.mu.RLock()
|
||||
keys := make([]string, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
keys = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
@ -305,8 +334,10 @@ func (m *StrIntMap) Keys() []string {
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *StrIntMap) Values() []int {
|
||||
m.mu.RLock()
|
||||
values := make([]int, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
values = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
@ -318,10 +349,13 @@ func (m *StrIntMap) Values() []int {
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *StrIntMap) Contains(key string) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
@ -382,6 +416,10 @@ func (m *StrIntMap) Flip() {
|
||||
func (m *StrIntMap) Merge(other *StrIntMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -400,14 +438,31 @@ func (m *StrIntMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *StrIntMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *StrIntMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &m.data)
|
||||
default:
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[k] = gconv.Int(v)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
|
||||
@ -16,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
type StrStrMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[string]string
|
||||
}
|
||||
|
||||
@ -26,7 +27,7 @@ type StrStrMap struct {
|
||||
func NewStrStrMap(safe ...bool) *StrStrMap {
|
||||
return &StrStrMap{
|
||||
data: make(map[string]string),
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,12 +36,12 @@ func NewStrStrMap(safe ...bool) *StrStrMap {
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStrStrMapFrom(data map[string]string, safe ...bool) *StrStrMap {
|
||||
return &StrStrMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map readonly with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *StrStrMap) Iterator(f func(k string, v string) bool) {
|
||||
m.mu.RLock()
|
||||
@ -96,6 +97,7 @@ func (m *StrStrMap) MapCopy() map[string]string {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *StrStrMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
@ -109,6 +111,9 @@ func (m *StrStrMap) FilterEmpty() {
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *StrStrMap) Set(key string, val string) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -116,8 +121,12 @@ func (m *StrStrMap) Set(key string, val string) {
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *StrStrMap) Sets(data map[string]string) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -126,17 +135,21 @@ func (m *StrStrMap) Sets(data map[string]string) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *StrStrMap) Search(key string) (value string, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *StrStrMap) Get(key string) string {
|
||||
func (m *StrStrMap) Get(key string) (value string) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
@ -161,8 +174,10 @@ func (m *StrStrMap) Pops(size int) map[string]string {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[string]string, size)
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[string]string, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
@ -182,6 +197,9 @@ func (m *StrStrMap) Pops(size int) map[string]string {
|
||||
func (m *StrStrMap) doSetWithLockCheck(key string, value string) string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
@ -220,13 +238,14 @@ func (m *StrStrMap) GetOrSetFuncLock(key string, f func() string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
v = f()
|
||||
if v != "" {
|
||||
m.data[key] = v
|
||||
}
|
||||
m.data[key] = v
|
||||
return v
|
||||
} else {
|
||||
return v
|
||||
@ -262,6 +281,9 @@ func (m *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
@ -273,28 +295,34 @@ func (m *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool {
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *StrStrMap) Removes(keys []string) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *StrStrMap) Remove(key string) string {
|
||||
func (m *StrStrMap) Remove(key string) (value string) {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *StrStrMap) Keys() []string {
|
||||
m.mu.RLock()
|
||||
keys := make([]string, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
keys = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
@ -306,8 +334,10 @@ func (m *StrStrMap) Keys() []string {
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *StrStrMap) Values() []string {
|
||||
m.mu.RLock()
|
||||
values := make([]string, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
values = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
@ -319,10 +349,13 @@ func (m *StrStrMap) Values() []string {
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *StrStrMap) Contains(key string) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
@ -383,6 +416,10 @@ func (m *StrStrMap) Flip() {
|
||||
func (m *StrStrMap) Merge(other *StrStrMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -401,14 +438,21 @@ func (m *StrStrMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *StrStrMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *StrStrMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.data = gconv.MapStrStr(value)
|
||||
return
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ import (
|
||||
)
|
||||
|
||||
type ListMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[interface{}]*glist.Element
|
||||
list *glist.List
|
||||
}
|
||||
@ -35,7 +35,7 @@ type gListMapNode struct {
|
||||
// which is false in default.
|
||||
func NewListMap(safe ...bool) *ListMap {
|
||||
return &ListMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[interface{}]*glist.Element),
|
||||
list: glist.New(),
|
||||
}
|
||||
@ -55,28 +55,32 @@ func (m *ListMap) Iterator(f func(key, value interface{}) bool) {
|
||||
m.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the map in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the map readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *ListMap) IteratorAsc(f func(key interface{}, value interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
node := (*gListMapNode)(nil)
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
})
|
||||
if m.list != nil {
|
||||
node := (*gListMapNode)(nil)
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the map in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the map readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *ListMap) IteratorDesc(f func(key interface{}, value interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
node := (*gListMapNode)(nil)
|
||||
m.list.IteratorDesc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
})
|
||||
if m.list != nil {
|
||||
node := (*gListMapNode)(nil)
|
||||
m.list.IteratorDesc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new link map with copy of current map data.
|
||||
@ -110,13 +114,16 @@ func (m *ListMap) Replace(data map[interface{}]interface{}) {
|
||||
// Map returns a copy of the underlying data of the map.
|
||||
func (m *ListMap) Map() map[interface{}]interface{} {
|
||||
m.mu.RLock()
|
||||
node := (*gListMapNode)(nil)
|
||||
data := make(map[interface{}]interface{}, len(m.data))
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
data[node.key] = node.value
|
||||
return true
|
||||
})
|
||||
var node *gListMapNode
|
||||
var data map[interface{}]interface{}
|
||||
if m.list != nil {
|
||||
data = make(map[interface{}]interface{}, len(m.data))
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
data[node.key] = node.value
|
||||
return true
|
||||
})
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
@ -124,13 +131,16 @@ func (m *ListMap) Map() map[interface{}]interface{} {
|
||||
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
|
||||
func (m *ListMap) MapStrAny() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
node := (*gListMapNode)(nil)
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
data[gconv.String(node.key)] = node.value
|
||||
return true
|
||||
})
|
||||
var node *gListMapNode
|
||||
var data map[string]interface{}
|
||||
if m.list != nil {
|
||||
data = make(map[string]interface{}, len(m.data))
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
data[gconv.String(node.key)] = node.value
|
||||
return true
|
||||
})
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
@ -138,20 +148,22 @@ func (m *ListMap) MapStrAny() map[string]interface{} {
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
func (m *ListMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
keys := make([]interface{}, 0)
|
||||
node := (*gListMapNode)(nil)
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
if empty.IsEmpty(node.value) {
|
||||
keys = append(keys, node.key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
if len(keys) > 0 {
|
||||
for _, key := range keys {
|
||||
if e, ok := m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
if m.list != nil {
|
||||
keys := make([]interface{}, 0)
|
||||
node := (*gListMapNode)(nil)
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
if empty.IsEmpty(node.value) {
|
||||
keys = append(keys, node.key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
if len(keys) > 0 {
|
||||
for _, key := range keys {
|
||||
if e, ok := m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -161,6 +173,10 @@ func (m *ListMap) FilterEmpty() {
|
||||
// Set sets key-value to the map.
|
||||
func (m *ListMap) Set(key interface{}, value interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
if e, ok := m.data[key]; !ok {
|
||||
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
|
||||
} else {
|
||||
@ -172,6 +188,10 @@ func (m *ListMap) Set(key interface{}, value interface{}) {
|
||||
// Sets batch sets key-values to the map.
|
||||
func (m *ListMap) Sets(data map[interface{}]interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
for key, value := range data {
|
||||
if e, ok := m.data[key]; !ok {
|
||||
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
|
||||
@ -186,9 +206,11 @@ func (m *ListMap) Sets(data map[interface{}]interface{}) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *ListMap) Search(key interface{}) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
found = ok
|
||||
if m.data != nil {
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
found = ok
|
||||
}
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
@ -197,8 +219,10 @@ func (m *ListMap) Search(key interface{}) (value interface{}, found bool) {
|
||||
// Get returns the value by given <key>.
|
||||
func (m *ListMap) Get(key interface{}) (value interface{}) {
|
||||
m.mu.RLock()
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
if m.data != nil {
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
}
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
@ -255,6 +279,10 @@ func (m *ListMap) Pops(size int) map[interface{}]interface{} {
|
||||
func (m *ListMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
if e, ok := m.data[key]; ok {
|
||||
return e.Value.(*gListMapNode).value
|
||||
}
|
||||
@ -302,26 +330,26 @@ func (m *ListMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interf
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVar returns a Var with the value by given <key>.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVar(key interface{}) *gvar.Var {
|
||||
return gvar.New(m.Get(key))
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSet returns a Var with result from GetVarOrSet.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value))
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f))
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f))
|
||||
}
|
||||
@ -362,10 +390,12 @@ func (m *ListMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) b
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *ListMap) Remove(key interface{}) (value interface{}) {
|
||||
m.mu.Lock()
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
if m.data != nil {
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return
|
||||
@ -374,10 +404,12 @@ func (m *ListMap) Remove(key interface{}) (value interface{}) {
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *ListMap) Removes(keys []interface{}) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
if e, ok := m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
if e, ok := m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
@ -386,13 +418,17 @@ func (m *ListMap) Removes(keys []interface{}) {
|
||||
// Keys returns all keys of the map as a slice in ascending order.
|
||||
func (m *ListMap) Keys() []interface{} {
|
||||
m.mu.RLock()
|
||||
keys := make([]interface{}, m.list.Len())
|
||||
index := 0
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
keys[index] = e.Value.(*gListMapNode).key
|
||||
index++
|
||||
return true
|
||||
})
|
||||
var (
|
||||
keys = make([]interface{}, m.list.Len())
|
||||
index = 0
|
||||
)
|
||||
if m.list != nil {
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
keys[index] = e.Value.(*gListMapNode).key
|
||||
index++
|
||||
return true
|
||||
})
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
@ -400,13 +436,17 @@ func (m *ListMap) Keys() []interface{} {
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *ListMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
values := make([]interface{}, m.list.Len())
|
||||
index := 0
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
values[index] = e.Value.(*gListMapNode).value
|
||||
index++
|
||||
return true
|
||||
})
|
||||
var (
|
||||
values = make([]interface{}, m.list.Len())
|
||||
index = 0
|
||||
)
|
||||
if m.list != nil {
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
values[index] = e.Value.(*gListMapNode).value
|
||||
index++
|
||||
return true
|
||||
})
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
@ -415,7 +455,9 @@ func (m *ListMap) Values() []interface{} {
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *ListMap) Contains(key interface{}) (ok bool) {
|
||||
m.mu.RLock()
|
||||
_, ok = m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
@ -448,6 +490,10 @@ func (m *ListMap) Flip() {
|
||||
func (m *ListMap) Merge(other *ListMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -471,13 +517,12 @@ func (m *ListMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *ListMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal(b, &data); err != nil {
|
||||
return err
|
||||
@ -491,3 +536,21 @@ func (m *ListMap) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *ListMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
for k, v := range gconv.Map(value) {
|
||||
if e, ok := m.data[k]; !ok {
|
||||
m.data[k] = m.list.PushBack(&gListMapNode{k, v})
|
||||
} else {
|
||||
e.Value = &gListMapNode{k, v}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
@ -17,110 +18,170 @@ func getValue() interface{} {
|
||||
return 3
|
||||
}
|
||||
|
||||
func Test_Map_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.Map
|
||||
m.Set(1, 11)
|
||||
t.Assert(m.Get(1), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntAnyMap
|
||||
m.Set(1, 11)
|
||||
t.Assert(m.Get(1), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntIntMap
|
||||
m.Set(1, 11)
|
||||
t.Assert(m.Get(1), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntStrMap
|
||||
m.Set(1, "11")
|
||||
t.Assert(m.Get(1), "11")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.StrAnyMap
|
||||
m.Set("1", "11")
|
||||
t.Assert(m.Get("1"), "11")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.StrStrMap
|
||||
m.Set("1", "11")
|
||||
t.Assert(m.Get("1"), "11")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.StrIntMap
|
||||
m.Set("1", 11)
|
||||
t.Assert(m.Get("1"), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.ListMap
|
||||
m.Set("1", 11)
|
||||
t.Assert(m.Get("1"), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.TreeMap
|
||||
m.SetComparator(gutil.ComparatorString)
|
||||
m.Set("1", 11)
|
||||
t.Assert(m.Get("1"), 11)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Map_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.New()
|
||||
m.Set("key1", "val1")
|
||||
gtest.Assert(m.Keys(), []interface{}{"key1"})
|
||||
t.Assert(m.Keys(), []interface{}{"key1"})
|
||||
|
||||
gtest.Assert(m.Get("key1"), "val1")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
t.Assert(m.Get("key1"), "val1")
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("key2", "val2"), "val2")
|
||||
gtest.Assert(m.SetIfNotExist("key2", "val2"), false)
|
||||
t.Assert(m.GetOrSet("key2", "val2"), "val2")
|
||||
t.Assert(m.SetIfNotExist("key2", "val2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("key3", "val3"), true)
|
||||
t.Assert(m.SetIfNotExist("key3", "val3"), true)
|
||||
|
||||
gtest.Assert(m.Remove("key2"), "val2")
|
||||
gtest.Assert(m.Contains("key2"), false)
|
||||
t.Assert(m.Remove("key2"), "val2")
|
||||
t.Assert(m.Contains("key2"), false)
|
||||
|
||||
gtest.AssertIN("key3", m.Keys())
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
gtest.AssertIN("val3", m.Values())
|
||||
gtest.AssertIN("val1", m.Values())
|
||||
t.AssertIN("key3", m.Keys())
|
||||
t.AssertIN("key1", m.Keys())
|
||||
t.AssertIN("val3", m.Values())
|
||||
t.AssertIN("val1", m.Values())
|
||||
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
t.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
})
|
||||
}
|
||||
func Test_Map_Set_Fun(t *testing.T) {
|
||||
m := gmap.New()
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
m.GetOrSetFuncLock("funlock", getValue)
|
||||
gtest.Assert(m.Get("funlock"), 3)
|
||||
gtest.Assert(m.Get("fun"), 3)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.New()
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
m.GetOrSetFuncLock("funlock", getValue)
|
||||
t.Assert(m.Get("funlock"), 3)
|
||||
t.Assert(m.Get("fun"), 3)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
t.Assert(m.SetIfNotExistFunc("fun", getValue), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Map_Batch(t *testing.T) {
|
||||
m := gmap.New()
|
||||
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
m.Removes([]interface{}{"key1", 1})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.New()
|
||||
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
m.Removes([]interface{}{"key1", 1})
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
|
||||
})
|
||||
}
|
||||
func Test_Map_Iterator(t *testing.T) {
|
||||
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
|
||||
m := gmap.NewFrom(expect)
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
m := gmap.NewFrom(expect)
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
t.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
t.Assert(i, 2)
|
||||
t.Assert(j, 1)
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
}
|
||||
|
||||
func Test_Map_Lock(t *testing.T) {
|
||||
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
|
||||
m := gmap.NewFrom(expect)
|
||||
m.LockFunc(func(m map[interface{}]interface{}) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[interface{}]interface{}) {
|
||||
gtest.Assert(m, expect)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
m := gmap.NewFrom(expect)
|
||||
m.LockFunc(func(m map[interface{}]interface{}) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[interface{}]interface{}) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Map_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
t.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove("key1")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
m_clone.Remove("key1")
|
||||
//修改clone map,原 map 不影响
|
||||
t.AssertIN("key1", m.Keys())
|
||||
})
|
||||
}
|
||||
func Test_Map_Basic_Merge(t *testing.T) {
|
||||
m1 := gmap.New()
|
||||
m2 := gmap.New()
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.New()
|
||||
m2 := gmap.New()
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,12 +1,19 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
)
|
||||
|
||||
func Example_Normal_Basic() {
|
||||
func Example_normalBasic() {
|
||||
m := gmap.New()
|
||||
|
||||
//Add data
|
||||
@ -59,13 +66,97 @@ func Example_Normal_Basic() {
|
||||
m.Clear()
|
||||
|
||||
fmt.Println(m.Size())
|
||||
|
||||
}
|
||||
func Example_Normal_Merge() {
|
||||
m1 := gmap.New()
|
||||
m2 := gmap.New()
|
||||
|
||||
func Example_keysValues() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Keys())
|
||||
fmt.Println(m.Values())
|
||||
|
||||
// May Output:
|
||||
// [k1 k2 k3 k4]
|
||||
// [v2 v3 v4 v1]
|
||||
}
|
||||
|
||||
func Example_flip() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
m.Flip()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// May Output:
|
||||
// map[v1:k1 v2:k2]
|
||||
}
|
||||
|
||||
func Example_pop() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Pop())
|
||||
fmt.Println(m.Pops(2))
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// May Output:
|
||||
// k1 v1
|
||||
// map[k2:v2 k4:v4]
|
||||
// 1
|
||||
}
|
||||
|
||||
func Example_filter() {
|
||||
m1 := gmap.NewFrom(g.MapAnyAny{
|
||||
"k1": "",
|
||||
"k2": nil,
|
||||
"k3": 0,
|
||||
"k4": 1,
|
||||
})
|
||||
m2 := gmap.NewFrom(g.MapAnyAny{
|
||||
"k1": "",
|
||||
"k2": nil,
|
||||
"k3": 0,
|
||||
"k4": 1,
|
||||
})
|
||||
m1.FilterEmpty()
|
||||
m2.FilterNil()
|
||||
fmt.Println(m1.Map())
|
||||
fmt.Println(m2.Map())
|
||||
|
||||
// Output:
|
||||
// map[k4:1]
|
||||
// map[k1: k3:0 k4:1]
|
||||
}
|
||||
|
||||
func Example_setIfNotExist() {
|
||||
var m gmap.Map
|
||||
fmt.Println(m.SetIfNotExist("k1", "v1"))
|
||||
fmt.Println(m.SetIfNotExist("k1", "v1"))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func Example_normalMerge() {
|
||||
var m1, m2 gmap.Map
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(m2)
|
||||
m1.Merge(&m2)
|
||||
fmt.Println(m1.Map())
|
||||
|
||||
// May Output:
|
||||
// map[key1:val1 key2:val2]
|
||||
}
|
||||
|
||||
@ -9,179 +9,232 @@ package gmap_test
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"time"
|
||||
)
|
||||
|
||||
func anyAnyCallBack(int, interface{}) bool {
|
||||
return true
|
||||
func Test_AnyAnyMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.AnyAnyMap
|
||||
m.Set(1, 1)
|
||||
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, "2"), "2")
|
||||
t.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
t.Assert(m.Remove(2), "2")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 1)
|
||||
|
||||
gtest.Assert(m.Get(1), 1)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet(2, "2"), "2")
|
||||
gtest.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
t.Assert(m.GetOrSet(2, "2"), "2")
|
||||
t.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist(3, 3), true)
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
gtest.Assert(m.Remove(2), "2")
|
||||
gtest.Assert(m.Contains(2), false)
|
||||
t.Assert(m.Remove(2), "2")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
gtest.AssertIN(3, m.Keys())
|
||||
gtest.AssertIN(1, m.Keys())
|
||||
gtest.AssertIN(3, m.Values())
|
||||
gtest.AssertIN(1, m.Values())
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewAnyAnyMapFrom(map[interface{}]interface{}{1: 1, 2: "2"})
|
||||
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, 2: "2"})
|
||||
t.Assert(m2.Map(), map[interface{}]interface{}{1: 1, 2: "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
|
||||
m.GetOrSetFunc(1, getAny)
|
||||
m.GetOrSetFuncLock(2, getAny)
|
||||
gtest.Assert(m.Get(1), 123)
|
||||
gtest.Assert(m.Get(2), 123)
|
||||
m.GetOrSetFunc(1, getAny)
|
||||
m.GetOrSetFuncLock(2, getAny)
|
||||
t.Assert(m.Get(1), 123)
|
||||
t.Assert(m.Get(2), 123)
|
||||
|
||||
gtest.Assert(m.SetIfNotExistFunc(1, getAny), false)
|
||||
gtest.Assert(m.SetIfNotExistFunc(3, getAny), true)
|
||||
t.Assert(m.SetIfNotExistFunc(1, getAny), false)
|
||||
t.Assert(m.SetIfNotExistFunc(3, getAny), true)
|
||||
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(2, getAny), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(4, getAny), true)
|
||||
t.Assert(m.SetIfNotExistFuncLock(2, getAny), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock(4, getAny), true)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Batch(t *testing.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
|
||||
m.Sets(map[interface{}]interface{}{1: 1, 2: "2", 3: 3})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, 2: "2", 3: 3})
|
||||
m.Removes([]interface{}{1, 2})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{3: 3})
|
||||
m.Sets(map[interface{}]interface{}{1: 1, 2: "2", 3: 3})
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{1: 1, 2: "2", 3: 3})
|
||||
m.Removes([]interface{}{1, 2})
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{3: 3})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Iterator(t *testing.T) {
|
||||
expect := map[interface{}]interface{}{1: 1, 2: "2"}
|
||||
m := gmap.NewAnyAnyMapFrom(expect)
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[interface{}]interface{}{1: 1, 2: "2"}
|
||||
m := gmap.NewAnyAnyMapFrom(expect)
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
t.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
t.Assert(i, "2")
|
||||
t.Assert(j, 1)
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, "2")
|
||||
gtest.Assert(j, 1)
|
||||
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Lock(t *testing.T) {
|
||||
expect := map[interface{}]interface{}{1: 1, 2: "2"}
|
||||
m := gmap.NewAnyAnyMapFrom(expect)
|
||||
m.LockFunc(func(m map[interface{}]interface{}) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[interface{}]interface{}) {
|
||||
gtest.Assert(m, expect)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[interface{}]interface{}{1: 1, 2: "2"}
|
||||
m := gmap.NewAnyAnyMapFrom(expect)
|
||||
m.LockFunc(func(m map[interface{}]interface{}) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[interface{}]interface{}) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Clone(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewAnyAnyMapFrom(map[interface{}]interface{}{1: 1, 2: "2"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
t.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN(2, m.Keys())
|
||||
t.AssertIN(2, m.Keys())
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewAnyAnyMap()
|
||||
m2 := gmap.NewAnyAnyMap()
|
||||
m1.Set(1, 1)
|
||||
m2.Set(2, "2")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[interface{}]interface{}{1: 1, 2: "2"})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewAnyAnyMap()
|
||||
m2 := gmap.NewAnyAnyMap()
|
||||
m1.Set(1, 1)
|
||||
m2.Set(2, "2")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[interface{}]interface{}{1: 1, 2: "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Map(t *testing.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
gtest.Assert(m.Get(1), 0)
|
||||
gtest.Assert(m.Get(2), 2)
|
||||
data := m.Map()
|
||||
gtest.Assert(data[1], 0)
|
||||
gtest.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
gtest.Assert(m.Get(3), 3)
|
||||
m.Set(4, 4)
|
||||
gtest.Assert(data[4], 4)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
data := m.Map()
|
||||
t.Assert(data[1], 0)
|
||||
t.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
t.Assert(m.Get(3), 3)
|
||||
m.Set(4, 4)
|
||||
t.Assert(data[4], 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_MapCopy(t *testing.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
gtest.Assert(m.Get(1), 0)
|
||||
gtest.Assert(m.Get(2), 2)
|
||||
data := m.MapCopy()
|
||||
gtest.Assert(data[1], 0)
|
||||
gtest.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
gtest.Assert(m.Get(3), nil)
|
||||
m.Set(4, 4)
|
||||
gtest.Assert(data[4], nil)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
data := m.MapCopy()
|
||||
t.Assert(data[1], 0)
|
||||
t.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
t.Assert(m.Get(3), nil)
|
||||
m.Set(4, 4)
|
||||
t.Assert(data[4], nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_FilterEmpty(t *testing.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
gtest.Assert(m.Get(1), 0)
|
||||
gtest.Assert(m.Get(2), 2)
|
||||
m.FilterEmpty()
|
||||
gtest.Assert(m.Get(1), nil)
|
||||
gtest.Assert(m.Get(2), 2)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
m.FilterEmpty()
|
||||
t.Assert(m.Get(1), nil)
|
||||
t.Assert(m.Get(2), 2)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set("time1", time.Time{})
|
||||
m.Set("time2", time.Now())
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get("time1"), time.Time{})
|
||||
m.FilterEmpty()
|
||||
t.Assert(m.Get(1), nil)
|
||||
t.Assert(m.Get("time1"), nil)
|
||||
t.AssertNE(m.Get("time2"), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
@ -189,89 +242,125 @@ func Test_AnyAnyMap_Json(t *testing.T) {
|
||||
m1 := gmap.NewAnyAnyMapFrom(data)
|
||||
b1, err1 := json.Marshal(m1)
|
||||
b2, err2 := json.Marshal(gconv.Map(data))
|
||||
gtest.Assert(err1, err2)
|
||||
gtest.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(gconv.Map(data))
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.New()
|
||||
err = json.Unmarshal(b, m)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(m.Get("k1"), data["k1"])
|
||||
gtest.Assert(m.Get("k2"), data["k2"])
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(gconv.Map(data))
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
var m gmap.Map
|
||||
err = json.Unmarshal(b, &m)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(m.Get("k1"), data["k1"])
|
||||
gtest.Assert(m.Get("k2"), data["k2"])
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMapFrom(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
gtest.Assert(m.Size(), 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
gtest.AssertIN(k1, g.Slice{"k1", "k2"})
|
||||
gtest.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 1)
|
||||
t.AssertIN(k1, g.Slice{"k1", "k2"})
|
||||
t.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
gtest.AssertIN(k2, g.Slice{"k1", "k2"})
|
||||
gtest.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 0)
|
||||
t.AssertIN(k2, g.Slice{"k1", "k2"})
|
||||
t.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
gtest.AssertNE(k1, k2)
|
||||
gtest.AssertNE(v1, v2)
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMapFrom(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
})
|
||||
gtest.Assert(m.Size(), 3)
|
||||
t.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
t.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
t.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 0)
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
gtest.Assert(kArray.Unique().Len(), 3)
|
||||
gtest.Assert(vArray.Unique().Len(), 3)
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAnyAnyMap_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Map *gmap.Map
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": []byte(`{"k1":"v1","k2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), "v1")
|
||||
t.Assert(v.Map.Get("k2"), "v2")
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": g.Map{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("k1"), "v1")
|
||||
t.Assert(v.Map.Get("k2"), "v2")
|
||||
})
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
@ -19,165 +20,211 @@ import (
|
||||
func getAny() interface{} {
|
||||
return 123
|
||||
}
|
||||
func intAnyCallBack(int, interface{}) bool {
|
||||
return true
|
||||
|
||||
func Test_IntAnyMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntAnyMap
|
||||
m.Set(1, 1)
|
||||
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, "2"), "2")
|
||||
t.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
t.Assert(m.Remove(2), "2")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, 1)
|
||||
|
||||
gtest.Assert(m.Get(1), 1)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet(2, "2"), "2")
|
||||
gtest.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
t.Assert(m.GetOrSet(2, "2"), "2")
|
||||
t.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist(3, 3), true)
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
gtest.Assert(m.Remove(2), "2")
|
||||
gtest.Assert(m.Contains(2), false)
|
||||
t.Assert(m.Remove(2), "2")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
gtest.AssertIN(3, m.Keys())
|
||||
gtest.AssertIN(1, m.Keys())
|
||||
gtest.AssertIN(3, m.Values())
|
||||
gtest.AssertIN(1, m.Values())
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"})
|
||||
gtest.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
t.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
m.GetOrSetFunc(1, getAny)
|
||||
m.GetOrSetFuncLock(2, getAny)
|
||||
gtest.Assert(m.Get(1), 123)
|
||||
gtest.Assert(m.Get(2), 123)
|
||||
m.GetOrSetFunc(1, getAny)
|
||||
m.GetOrSetFuncLock(2, getAny)
|
||||
t.Assert(m.Get(1), 123)
|
||||
t.Assert(m.Get(2), 123)
|
||||
|
||||
gtest.Assert(m.SetIfNotExistFunc(1, getAny), false)
|
||||
gtest.Assert(m.SetIfNotExistFunc(3, getAny), true)
|
||||
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(2, getAny), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(4, getAny), true)
|
||||
t.Assert(m.SetIfNotExistFunc(1, getAny), false)
|
||||
t.Assert(m.SetIfNotExistFunc(3, getAny), true)
|
||||
|
||||
t.Assert(m.SetIfNotExistFuncLock(2, getAny), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock(4, getAny), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Batch(t *testing.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
m.Sets(map[int]interface{}{1: 1, 2: "2", 3: 3})
|
||||
gtest.Assert(m.Map(), map[int]interface{}{1: 1, 2: "2", 3: 3})
|
||||
m.Removes([]int{1, 2})
|
||||
gtest.Assert(m.Map(), map[int]interface{}{3: 3})
|
||||
m.Sets(map[int]interface{}{1: 1, 2: "2", 3: 3})
|
||||
t.Assert(m.Map(), map[int]interface{}{1: 1, 2: "2", 3: 3})
|
||||
m.Removes([]int{1, 2})
|
||||
t.Assert(m.Map(), map[int]interface{}{3: 3})
|
||||
})
|
||||
}
|
||||
func Test_IntAnyMap_Iterator(t *testing.T) {
|
||||
expect := map[int]interface{}{1: 1, 2: "2"}
|
||||
m := gmap.NewIntAnyMapFrom(expect)
|
||||
m.Iterator(func(k int, v interface{}) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[int]interface{}{1: 1, 2: "2"}
|
||||
m := gmap.NewIntAnyMapFrom(expect)
|
||||
m.Iterator(func(k int, v interface{}) bool {
|
||||
t.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k int, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k int, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
t.Assert(i, "2")
|
||||
t.Assert(j, 1)
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k int, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k int, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, "2")
|
||||
gtest.Assert(j, 1)
|
||||
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Lock(t *testing.T) {
|
||||
expect := map[int]interface{}{1: 1, 2: "2"}
|
||||
m := gmap.NewIntAnyMapFrom(expect)
|
||||
m.LockFunc(func(m map[int]interface{}) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[int]interface{}) {
|
||||
gtest.Assert(m, expect)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[int]interface{}{1: 1, 2: "2"}
|
||||
m := gmap.NewIntAnyMapFrom(expect)
|
||||
m.LockFunc(func(m map[int]interface{}) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[int]interface{}) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
})
|
||||
}
|
||||
func Test_IntAnyMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
t.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN(2, m.Keys())
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
t.AssertIN(2, m.Keys())
|
||||
})
|
||||
}
|
||||
func Test_IntAnyMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewIntAnyMap()
|
||||
m2 := gmap.NewIntAnyMap()
|
||||
m1.Set(1, 1)
|
||||
m2.Set(2, "2")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewIntAnyMap()
|
||||
m2 := gmap.NewIntAnyMap()
|
||||
m1.Set(1, 1)
|
||||
m2.Set(2, "2")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Map(t *testing.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
gtest.Assert(m.Get(1), 0)
|
||||
gtest.Assert(m.Get(2), 2)
|
||||
data := m.Map()
|
||||
gtest.Assert(data[1], 0)
|
||||
gtest.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
gtest.Assert(m.Get(3), 3)
|
||||
m.Set(4, 4)
|
||||
gtest.Assert(data[4], 4)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
data := m.Map()
|
||||
t.Assert(data[1], 0)
|
||||
t.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
t.Assert(m.Get(3), 3)
|
||||
m.Set(4, 4)
|
||||
t.Assert(data[4], 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_MapCopy(t *testing.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
gtest.Assert(m.Get(1), 0)
|
||||
gtest.Assert(m.Get(2), 2)
|
||||
data := m.MapCopy()
|
||||
gtest.Assert(data[1], 0)
|
||||
gtest.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
gtest.Assert(m.Get(3), nil)
|
||||
m.Set(4, 4)
|
||||
gtest.Assert(data[4], nil)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
data := m.MapCopy()
|
||||
t.Assert(data[1], 0)
|
||||
t.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
t.Assert(m.Get(3), nil)
|
||||
m.Set(4, 4)
|
||||
t.Assert(data[4], nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_FilterEmpty(t *testing.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
gtest.Assert(m.Size(), 2)
|
||||
gtest.Assert(m.Get(1), 0)
|
||||
gtest.Assert(m.Get(2), 2)
|
||||
m.FilterEmpty()
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.Get(2), 2)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
m.FilterEmpty()
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.Get(2), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
@ -185,75 +232,111 @@ func Test_IntAnyMap_Json(t *testing.T) {
|
||||
m1 := gmap.NewIntAnyMapFrom(data)
|
||||
b1, err1 := json.Marshal(m1)
|
||||
b2, err2 := json.Marshal(data)
|
||||
gtest.Assert(err1, err2)
|
||||
gtest.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewIntAnyMap()
|
||||
err = json.Unmarshal(b, m)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(m.Get(1), data[1])
|
||||
gtest.Assert(m.Get(2), data[2])
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get(1), data[1])
|
||||
t.Assert(m.Get(2), data[2])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMapFrom(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
})
|
||||
gtest.Assert(m.Size(), 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
gtest.AssertIN(k1, g.Slice{1, 2})
|
||||
gtest.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 1)
|
||||
t.AssertIN(k1, g.Slice{1, 2})
|
||||
t.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
gtest.AssertIN(k2, g.Slice{1, 2})
|
||||
gtest.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 0)
|
||||
t.AssertIN(k2, g.Slice{1, 2})
|
||||
t.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
gtest.AssertNE(k1, k2)
|
||||
gtest.AssertNE(v1, v2)
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMapFrom(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
})
|
||||
gtest.Assert(m.Size(), 3)
|
||||
t.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
gtest.AssertIN(k, g.Slice{1, 2, 3})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
t.AssertIN(k, g.Slice{1, 2, 3})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
gtest.AssertIN(k, g.Slice{1, 2, 3})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
t.AssertIN(k, g.Slice{1, 2, 3})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 0)
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
gtest.Assert(kArray.Unique().Len(), 3)
|
||||
gtest.Assert(vArray.Unique().Len(), 3)
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntAnyMap_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Map *gmap.IntAnyMap
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": []byte(`{"1":"v1","2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "v1")
|
||||
t.Assert(v.Map.Get(2), "v2")
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "v1")
|
||||
t.Assert(v.Map.Get(2), "v2")
|
||||
})
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
@ -19,168 +20,217 @@ import (
|
||||
func getInt() int {
|
||||
return 123
|
||||
}
|
||||
|
||||
func intIntCallBack(int, int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntIntMap
|
||||
m.Set(1, 1)
|
||||
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, 2), 2)
|
||||
t.Assert(m.SetIfNotExist(2, 2), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
t.Assert(m.Remove(2), 2)
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[int]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 1)
|
||||
|
||||
gtest.Assert(m.Get(1), 1)
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet(2, 2), 2)
|
||||
gtest.Assert(m.SetIfNotExist(2, 2), false)
|
||||
t.Assert(m.GetOrSet(2, 2), 2)
|
||||
t.Assert(m.SetIfNotExist(2, 2), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist(3, 3), true)
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
gtest.Assert(m.Remove(2), 2)
|
||||
gtest.Assert(m.Contains(2), false)
|
||||
t.Assert(m.Remove(2), 2)
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
gtest.AssertIN(3, m.Keys())
|
||||
gtest.AssertIN(1, m.Keys())
|
||||
gtest.AssertIN(3, m.Values())
|
||||
gtest.AssertIN(1, m.Values())
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
gtest.Assert(m.Map(), map[int]int{1: 1, 3: 3})
|
||||
t.Assert(m.Map(), map[int]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
|
||||
gtest.Assert(m2.Map(), map[int]int{1: 1, 2: 2})
|
||||
t.Assert(m2.Map(), map[int]int{1: 1, 2: 2})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
m.GetOrSetFunc(1, getInt)
|
||||
m.GetOrSetFuncLock(2, getInt)
|
||||
gtest.Assert(m.Get(1), 123)
|
||||
gtest.Assert(m.Get(2), 123)
|
||||
gtest.Assert(m.SetIfNotExistFunc(1, getInt), false)
|
||||
gtest.Assert(m.SetIfNotExistFunc(3, getInt), true)
|
||||
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(2, getInt), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(4, getInt), true)
|
||||
m.GetOrSetFunc(1, getInt)
|
||||
m.GetOrSetFuncLock(2, getInt)
|
||||
t.Assert(m.Get(1), 123)
|
||||
t.Assert(m.Get(2), 123)
|
||||
t.Assert(m.SetIfNotExistFunc(1, getInt), false)
|
||||
t.Assert(m.SetIfNotExistFunc(3, getInt), true)
|
||||
|
||||
t.Assert(m.SetIfNotExistFuncLock(2, getInt), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock(4, getInt), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Batch(t *testing.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
m.Sets(map[int]int{1: 1, 2: 2, 3: 3})
|
||||
m.Iterator(intIntCallBack)
|
||||
gtest.Assert(m.Map(), map[int]int{1: 1, 2: 2, 3: 3})
|
||||
m.Removes([]int{1, 2})
|
||||
gtest.Assert(m.Map(), map[int]int{3: 3})
|
||||
m.Sets(map[int]int{1: 1, 2: 2, 3: 3})
|
||||
m.Iterator(intIntCallBack)
|
||||
t.Assert(m.Map(), map[int]int{1: 1, 2: 2, 3: 3})
|
||||
m.Removes([]int{1, 2})
|
||||
t.Assert(m.Map(), map[int]int{3: 3})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Iterator(t *testing.T) {
|
||||
expect := map[int]int{1: 1, 2: 2}
|
||||
m := gmap.NewIntIntMapFrom(expect)
|
||||
m.Iterator(func(k int, v int) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[int]int{1: 1, 2: 2}
|
||||
m := gmap.NewIntIntMapFrom(expect)
|
||||
m.Iterator(func(k int, v int) bool {
|
||||
t.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k int, v int) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k int, v int) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
t.Assert(i, 2)
|
||||
t.Assert(j, 1)
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k int, v int) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k int, v int) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Lock(t *testing.T) {
|
||||
expect := map[int]int{1: 1, 2: 2}
|
||||
m := gmap.NewIntIntMapFrom(expect)
|
||||
m.LockFunc(func(m map[int]int) {
|
||||
gtest.Assert(m, expect)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[int]int{1: 1, 2: 2}
|
||||
m := gmap.NewIntIntMapFrom(expect)
|
||||
m.LockFunc(func(m map[int]int) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[int]int) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
})
|
||||
m.RLockFunc(func(m map[int]int) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
t.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN(2, m.Keys())
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
t.AssertIN(2, m.Keys())
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewIntIntMap()
|
||||
m2 := gmap.NewIntIntMap()
|
||||
m1.Set(1, 1)
|
||||
m2.Set(2, 2)
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[int]int{1: 1, 2: 2})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewIntIntMap()
|
||||
m2 := gmap.NewIntIntMap()
|
||||
m1.Set(1, 1)
|
||||
m2.Set(2, 2)
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[int]int{1: 1, 2: 2})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Map(t *testing.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
gtest.Assert(m.Get(1), 0)
|
||||
gtest.Assert(m.Get(2), 2)
|
||||
data := m.Map()
|
||||
gtest.Assert(data[1], 0)
|
||||
gtest.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
gtest.Assert(m.Get(3), 3)
|
||||
m.Set(4, 4)
|
||||
gtest.Assert(data[4], 4)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
data := m.Map()
|
||||
t.Assert(data[1], 0)
|
||||
t.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
t.Assert(m.Get(3), 3)
|
||||
m.Set(4, 4)
|
||||
t.Assert(data[4], 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_MapCopy(t *testing.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
gtest.Assert(m.Get(1), 0)
|
||||
gtest.Assert(m.Get(2), 2)
|
||||
data := m.MapCopy()
|
||||
gtest.Assert(data[1], 0)
|
||||
gtest.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
gtest.Assert(m.Get(3), 0)
|
||||
m.Set(4, 4)
|
||||
gtest.Assert(data[4], 0)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
data := m.MapCopy()
|
||||
t.Assert(data[1], 0)
|
||||
t.Assert(data[2], 2)
|
||||
data[3] = 3
|
||||
t.Assert(m.Get(3), 0)
|
||||
m.Set(4, 4)
|
||||
t.Assert(data[4], 0)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_FilterEmpty(t *testing.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
gtest.Assert(m.Size(), 2)
|
||||
gtest.Assert(m.Get(1), 0)
|
||||
gtest.Assert(m.Get(2), 2)
|
||||
m.FilterEmpty()
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.Get(2), 2)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
m.Set(1, 0)
|
||||
m.Set(2, 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get(2), 2)
|
||||
m.FilterEmpty()
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.Get(2), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapIntInt{
|
||||
1: 10,
|
||||
2: 20,
|
||||
@ -188,75 +238,111 @@ func Test_IntIntMap_Json(t *testing.T) {
|
||||
m1 := gmap.NewIntIntMapFrom(data)
|
||||
b1, err1 := json.Marshal(m1)
|
||||
b2, err2 := json.Marshal(data)
|
||||
gtest.Assert(err1, err2)
|
||||
gtest.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapIntInt{
|
||||
1: 10,
|
||||
2: 20,
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewIntIntMap()
|
||||
err = json.Unmarshal(b, m)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(m.Get(1), data[1])
|
||||
gtest.Assert(m.Get(2), data[2])
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get(1), data[1])
|
||||
t.Assert(m.Get(2), data[2])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMapFrom(g.MapIntInt{
|
||||
1: 11,
|
||||
2: 22,
|
||||
})
|
||||
gtest.Assert(m.Size(), 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
gtest.AssertIN(k1, g.Slice{1, 2})
|
||||
gtest.AssertIN(v1, g.Slice{11, 22})
|
||||
gtest.Assert(m.Size(), 1)
|
||||
t.AssertIN(k1, g.Slice{1, 2})
|
||||
t.AssertIN(v1, g.Slice{11, 22})
|
||||
t.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
gtest.AssertIN(k2, g.Slice{1, 2})
|
||||
gtest.AssertIN(v2, g.Slice{11, 22})
|
||||
gtest.Assert(m.Size(), 0)
|
||||
t.AssertIN(k2, g.Slice{1, 2})
|
||||
t.AssertIN(v2, g.Slice{11, 22})
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
gtest.AssertNE(k1, k2)
|
||||
gtest.AssertNE(v1, v2)
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMapFrom(g.MapIntInt{
|
||||
1: 11,
|
||||
2: 22,
|
||||
3: 33,
|
||||
})
|
||||
gtest.Assert(m.Size(), 3)
|
||||
t.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
gtest.AssertIN(k, g.Slice{1, 2, 3})
|
||||
gtest.AssertIN(v, g.Slice{11, 22, 33})
|
||||
t.AssertIN(k, g.Slice{1, 2, 3})
|
||||
t.AssertIN(v, g.Slice{11, 22, 33})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
gtest.AssertIN(k, g.Slice{1, 2, 3})
|
||||
gtest.AssertIN(v, g.Slice{11, 22, 33})
|
||||
t.AssertIN(k, g.Slice{1, 2, 3})
|
||||
t.AssertIN(v, g.Slice{11, 22, 33})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 0)
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
gtest.Assert(kArray.Unique().Len(), 3)
|
||||
gtest.Assert(vArray.Unique().Len(), 3)
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntIntMap_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Map *gmap.IntIntMap
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": []byte(`{"1":1,"2":2}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "1")
|
||||
t.Assert(v.Map.Get(2), "2")
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": g.MapIntAny{
|
||||
1: 1,
|
||||
2: 2,
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "1")
|
||||
t.Assert(v.Map.Get(2), "2")
|
||||
})
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
@ -19,169 +20,215 @@ import (
|
||||
func getStr() string {
|
||||
return "z"
|
||||
}
|
||||
func intStrCallBack(int, string) bool {
|
||||
return true
|
||||
}
|
||||
func Test_IntStrMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntStrMap()
|
||||
|
||||
func Test_IntStrMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntStrMap
|
||||
m.Set(1, "a")
|
||||
|
||||
gtest.Assert(m.Get(1), "a")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
t.Assert(m.Get(1), "a")
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet(2, "b"), "b")
|
||||
gtest.Assert(m.SetIfNotExist(2, "b"), false)
|
||||
t.Assert(m.GetOrSet(2, "b"), "b")
|
||||
t.Assert(m.SetIfNotExist(2, "b"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist(3, "c"), true)
|
||||
t.Assert(m.SetIfNotExist(3, "c"), true)
|
||||
|
||||
gtest.Assert(m.Remove(2), "b")
|
||||
gtest.Assert(m.Contains(2), false)
|
||||
t.Assert(m.Remove(2), "b")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
gtest.AssertIN(3, m.Keys())
|
||||
gtest.AssertIN(1, m.Keys())
|
||||
gtest.AssertIN("a", m.Values())
|
||||
gtest.AssertIN("c", m.Values())
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN("a", m.Values())
|
||||
t.AssertIN("c", m.Values())
|
||||
|
||||
//反转之后不成为以下 map,flip 操作只是翻转原 map
|
||||
//gtest.Assert(m.Map(), map[string]int{"a": 1, "c": 3})
|
||||
m_f := gmap.NewIntStrMap()
|
||||
m_f.Set(1, "2")
|
||||
m_f.Flip()
|
||||
gtest.Assert(m_f.Map(), map[int]string{2: "1"})
|
||||
t.Assert(m_f.Map(), map[int]string{2: "1"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b"})
|
||||
gtest.Assert(m2.Map(), map[int]string{1: "a", 2: "b"})
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.Set(1, "a")
|
||||
|
||||
t.Assert(m.Get(1), "a")
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, "b"), "b")
|
||||
t.Assert(m.SetIfNotExist(2, "b"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, "c"), true)
|
||||
|
||||
t.Assert(m.Remove(2), "b")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN("a", m.Values())
|
||||
t.AssertIN("c", m.Values())
|
||||
|
||||
//反转之后不成为以下 map,flip 操作只是翻转原 map
|
||||
//t.Assert(m.Map(), map[string]int{"a": 1, "c": 3})
|
||||
m_f := gmap.NewIntStrMap()
|
||||
m_f.Set(1, "2")
|
||||
m_f.Flip()
|
||||
t.Assert(m_f.Map(), map[int]string{2: "1"})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b"})
|
||||
t.Assert(m2.Map(), map[int]string{1: "a", 2: "b"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
|
||||
m.GetOrSetFunc(1, getStr)
|
||||
m.GetOrSetFuncLock(2, getStr)
|
||||
gtest.Assert(m.Get(1), "z")
|
||||
gtest.Assert(m.Get(2), "z")
|
||||
gtest.Assert(m.SetIfNotExistFunc(1, getStr), false)
|
||||
gtest.Assert(m.SetIfNotExistFunc(3, getStr), true)
|
||||
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(2, getStr), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock(4, getStr), true)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.GetOrSetFunc(1, getStr)
|
||||
m.GetOrSetFuncLock(2, getStr)
|
||||
t.Assert(m.Get(1), "z")
|
||||
t.Assert(m.Get(2), "z")
|
||||
t.Assert(m.SetIfNotExistFunc(1, getStr), false)
|
||||
t.Assert(m.SetIfNotExistFunc(3, getStr), true)
|
||||
|
||||
t.Assert(m.SetIfNotExistFuncLock(2, getStr), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock(4, getStr), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Batch(t *testing.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
|
||||
m.Sets(map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
gtest.Assert(m.Map(), map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
m.Removes([]int{1, 2})
|
||||
gtest.Assert(m.Map(), map[int]interface{}{3: "c"})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.Sets(map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
t.Assert(m.Map(), map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
m.Removes([]int{1, 2})
|
||||
t.Assert(m.Map(), map[int]interface{}{3: "c"})
|
||||
})
|
||||
}
|
||||
func Test_IntStrMap_Iterator(t *testing.T) {
|
||||
expect := map[int]string{1: "a", 2: "b"}
|
||||
m := gmap.NewIntStrMapFrom(expect)
|
||||
m.Iterator(func(k int, v string) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[int]string{1: "a", 2: "b"}
|
||||
m := gmap.NewIntStrMapFrom(expect)
|
||||
m.Iterator(func(k int, v string) bool {
|
||||
t.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k int, v string) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k int, v string) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
t.Assert(i, 2)
|
||||
t.Assert(j, 1)
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k int, v string) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k int, v string) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Lock(t *testing.T) {
|
||||
|
||||
expect := map[int]string{1: "a", 2: "b", 3: "c"}
|
||||
m := gmap.NewIntStrMapFrom(expect)
|
||||
m.LockFunc(func(m map[int]string) {
|
||||
gtest.Assert(m, expect)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[int]string{1: "a", 2: "b", 3: "c"}
|
||||
m := gmap.NewIntStrMapFrom(expect)
|
||||
m.LockFunc(func(m map[int]string) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
m.RLockFunc(func(m map[int]string) {
|
||||
t.Assert(m, expect)
|
||||
})
|
||||
})
|
||||
m.RLockFunc(func(m map[int]string) {
|
||||
gtest.Assert(m, expect)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b", 3: "c"})
|
||||
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
t.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN(2, m.Keys())
|
||||
m_clone.Remove(2)
|
||||
//修改clone map,原 map 不影响
|
||||
t.AssertIN(2, m.Keys())
|
||||
})
|
||||
}
|
||||
func Test_IntStrMap_Merge(t *testing.T) {
|
||||
m1 := gmap.NewIntStrMap()
|
||||
m2 := gmap.NewIntStrMap()
|
||||
m1.Set(1, "a")
|
||||
m2.Set(2, "b")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[int]string{1: "a", 2: "b"})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewIntStrMap()
|
||||
m2 := gmap.NewIntStrMap()
|
||||
m1.Set(1, "a")
|
||||
m2.Set(2, "b")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[int]string{1: "a", 2: "b"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Map(t *testing.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.Set(1, "0")
|
||||
m.Set(2, "2")
|
||||
gtest.Assert(m.Get(1), "0")
|
||||
gtest.Assert(m.Get(2), "2")
|
||||
data := m.Map()
|
||||
gtest.Assert(data[1], "0")
|
||||
gtest.Assert(data[2], "2")
|
||||
data[3] = "3"
|
||||
gtest.Assert(m.Get(3), "3")
|
||||
m.Set(4, "4")
|
||||
gtest.Assert(data[4], "4")
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.Set(1, "0")
|
||||
m.Set(2, "2")
|
||||
t.Assert(m.Get(1), "0")
|
||||
t.Assert(m.Get(2), "2")
|
||||
data := m.Map()
|
||||
t.Assert(data[1], "0")
|
||||
t.Assert(data[2], "2")
|
||||
data[3] = "3"
|
||||
t.Assert(m.Get(3), "3")
|
||||
m.Set(4, "4")
|
||||
t.Assert(data[4], "4")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_MapCopy(t *testing.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.Set(1, "0")
|
||||
m.Set(2, "2")
|
||||
gtest.Assert(m.Get(1), "0")
|
||||
gtest.Assert(m.Get(2), "2")
|
||||
data := m.MapCopy()
|
||||
gtest.Assert(data[1], "0")
|
||||
gtest.Assert(data[2], "2")
|
||||
data[3] = "3"
|
||||
gtest.Assert(m.Get(3), "")
|
||||
m.Set(4, "4")
|
||||
gtest.Assert(data[4], "")
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.Set(1, "0")
|
||||
m.Set(2, "2")
|
||||
t.Assert(m.Get(1), "0")
|
||||
t.Assert(m.Get(2), "2")
|
||||
data := m.MapCopy()
|
||||
t.Assert(data[1], "0")
|
||||
t.Assert(data[2], "2")
|
||||
data[3] = "3"
|
||||
t.Assert(m.Get(3), "")
|
||||
m.Set(4, "4")
|
||||
t.Assert(data[4], "")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_FilterEmpty(t *testing.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.Set(1, "")
|
||||
m.Set(2, "2")
|
||||
gtest.Assert(m.Size(), 2)
|
||||
gtest.Assert(m.Get(2), "2")
|
||||
m.FilterEmpty()
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.Get(2), "2")
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.Set(1, "")
|
||||
m.Set(2, "2")
|
||||
t.Assert(m.Size(), 2)
|
||||
t.Assert(m.Get(2), "2")
|
||||
m.FilterEmpty()
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.Get(2), "2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapIntStr{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
@ -189,75 +236,111 @@ func Test_IntStrMap_Json(t *testing.T) {
|
||||
m1 := gmap.NewIntStrMapFrom(data)
|
||||
b1, err1 := json.Marshal(m1)
|
||||
b2, err2 := json.Marshal(data)
|
||||
gtest.Assert(err1, err2)
|
||||
gtest.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapIntStr{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewIntStrMap()
|
||||
err = json.Unmarshal(b, m)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(m.Get(1), data[1])
|
||||
gtest.Assert(m.Get(2), data[2])
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get(1), data[1])
|
||||
t.Assert(m.Get(2), data[2])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMapFrom(g.MapIntStr{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
})
|
||||
gtest.Assert(m.Size(), 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
gtest.AssertIN(k1, g.Slice{1, 2})
|
||||
gtest.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 1)
|
||||
t.AssertIN(k1, g.Slice{1, 2})
|
||||
t.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
gtest.AssertIN(k2, g.Slice{1, 2})
|
||||
gtest.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 0)
|
||||
t.AssertIN(k2, g.Slice{1, 2})
|
||||
t.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
gtest.AssertNE(k1, k2)
|
||||
gtest.AssertNE(v1, v2)
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMapFrom(g.MapIntStr{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
})
|
||||
gtest.Assert(m.Size(), 3)
|
||||
t.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
gtest.AssertIN(k, g.Slice{1, 2, 3})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
t.AssertIN(k, g.Slice{1, 2, 3})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
gtest.AssertIN(k, g.Slice{1, 2, 3})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
t.AssertIN(k, g.Slice{1, 2, 3})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 0)
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
gtest.Assert(kArray.Unique().Len(), 3)
|
||||
gtest.Assert(vArray.Unique().Len(), 3)
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntStrMap_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Map *gmap.IntStrMap
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": []byte(`{"1":"v1","2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "v1")
|
||||
t.Assert(v.Map.Get(2), "v2")
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get(1), "v1")
|
||||
t.Assert(v.Map.Get(2), "v2")
|
||||
})
|
||||
}
|
||||
|
||||
@ -17,126 +17,173 @@ import (
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func Test_ListMap_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewListMap()
|
||||
func Test_ListMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.ListMap
|
||||
m.Set("key1", "val1")
|
||||
gtest.Assert(m.Keys(), []interface{}{"key1"})
|
||||
t.Assert(m.Keys(), []interface{}{"key1"})
|
||||
|
||||
gtest.Assert(m.Get("key1"), "val1")
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.IsEmpty(), false)
|
||||
t.Assert(m.Get("key1"), "val1")
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
gtest.Assert(m.GetOrSet("key2", "val2"), "val2")
|
||||
gtest.Assert(m.SetIfNotExist("key2", "val2"), false)
|
||||
t.Assert(m.GetOrSet("key2", "val2"), "val2")
|
||||
t.Assert(m.SetIfNotExist("key2", "val2"), false)
|
||||
|
||||
gtest.Assert(m.SetIfNotExist("key3", "val3"), true)
|
||||
gtest.Assert(m.Remove("key2"), "val2")
|
||||
gtest.Assert(m.Contains("key2"), false)
|
||||
t.Assert(m.SetIfNotExist("key3", "val3"), true)
|
||||
t.Assert(m.Remove("key2"), "val2")
|
||||
t.Assert(m.Contains("key2"), false)
|
||||
|
||||
gtest.AssertIN("key3", m.Keys())
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
gtest.AssertIN("val3", m.Values())
|
||||
gtest.AssertIN("val1", m.Values())
|
||||
t.AssertIN("key3", m.Keys())
|
||||
t.AssertIN("key1", m.Keys())
|
||||
t.AssertIN("val3", m.Values())
|
||||
t.AssertIN("val1", m.Values())
|
||||
|
||||
m.Flip()
|
||||
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
|
||||
|
||||
m.Clear()
|
||||
gtest.Assert(m.Size(), 0)
|
||||
gtest.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewListMapFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.Set("key1", "val1")
|
||||
t.Assert(m.Keys(), []interface{}{"key1"})
|
||||
|
||||
t.Assert(m.Get("key1"), "val1")
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet("key2", "val2"), "val2")
|
||||
t.Assert(m.SetIfNotExist("key2", "val2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist("key3", "val3"), true)
|
||||
t.Assert(m.Remove("key2"), "val2")
|
||||
t.Assert(m.Contains("key2"), false)
|
||||
|
||||
t.AssertIN("key3", m.Keys())
|
||||
t.AssertIN("key1", m.Keys())
|
||||
t.AssertIN("val3", m.Values())
|
||||
t.AssertIN("val1", m.Values())
|
||||
|
||||
m.Flip()
|
||||
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
|
||||
m2 := gmap.NewListMapFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
t.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
m.GetOrSetFuncLock("funlock", getValue)
|
||||
gtest.Assert(m.Get("funlock"), 3)
|
||||
gtest.Assert(m.Get("fun"), 3)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false)
|
||||
gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
m.GetOrSetFuncLock("funlock", getValue)
|
||||
t.Assert(m.Get("funlock"), 3)
|
||||
t.Assert(m.Get("fun"), 3)
|
||||
m.GetOrSetFunc("fun", getValue)
|
||||
t.Assert(m.SetIfNotExistFunc("fun", getValue), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Batch(t *testing.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
m.Removes([]interface{}{"key1", 1})
|
||||
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"})
|
||||
m.Removes([]interface{}{"key1", 1})
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
|
||||
})
|
||||
}
|
||||
func Test_ListMap_Iterator(t *testing.T) {
|
||||
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
|
||||
|
||||
m := gmap.NewListMapFrom(expect)
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
gtest.Assert(expect[k], v)
|
||||
return true
|
||||
m := gmap.NewListMapFrom(expect)
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
t.Assert(expect[k], v)
|
||||
return true
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
t.Assert(i, 2)
|
||||
t.Assert(j, 1)
|
||||
})
|
||||
// 断言返回值对遍历控制
|
||||
i := 0
|
||||
j := 0
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
m.Iterator(func(k interface{}, v interface{}) bool {
|
||||
j++
|
||||
return false
|
||||
})
|
||||
gtest.Assert(i, 2)
|
||||
gtest.Assert(j, 1)
|
||||
}
|
||||
|
||||
func Test_ListMap_Clone(t *testing.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewListMapFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
gtest.AssertIN(1, m_clone.Keys())
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
//clone 方法是深克隆
|
||||
m := gmap.NewListMapFrom(map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
m_clone := m.Clone()
|
||||
m.Remove(1)
|
||||
//修改原 map,clone 后的 map 不影响
|
||||
t.AssertIN(1, m_clone.Keys())
|
||||
|
||||
m_clone.Remove("key1")
|
||||
//修改clone map,原 map 不影响
|
||||
gtest.AssertIN("key1", m.Keys())
|
||||
m_clone.Remove("key1")
|
||||
//修改clone map,原 map 不影响
|
||||
t.AssertIN("key1", m.Keys())
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Basic_Merge(t *testing.T) {
|
||||
m1 := gmap.NewListMap()
|
||||
m2 := gmap.NewListMap()
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(m2)
|
||||
gtest.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewListMap()
|
||||
m2 := gmap.NewListMap()
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Order(t *testing.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.Set("k1", "v1")
|
||||
m.Set("k2", "v2")
|
||||
m.Set("k3", "v3")
|
||||
gtest.Assert(m.Keys(), g.Slice{"k1", "k2", "k3"})
|
||||
gtest.Assert(m.Values(), g.Slice{"v1", "v2", "v3"})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.Set("k1", "v1")
|
||||
m.Set("k2", "v2")
|
||||
m.Set("k3", "v3")
|
||||
t.Assert(m.Keys(), g.Slice{"k1", "k2", "k3"})
|
||||
t.Assert(m.Values(), g.Slice{"v1", "v2", "v3"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_FilterEmpty(t *testing.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.Set(1, "")
|
||||
m.Set(2, "2")
|
||||
gtest.Assert(m.Size(), 2)
|
||||
gtest.Assert(m.Get(2), "2")
|
||||
m.FilterEmpty()
|
||||
gtest.Assert(m.Size(), 1)
|
||||
gtest.Assert(m.Get(2), "2")
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.Set(1, "")
|
||||
m.Set(2, "2")
|
||||
t.Assert(m.Size(), 2)
|
||||
t.Assert(m.Get(2), "2")
|
||||
m.FilterEmpty()
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.Get(2), "2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Json(t *testing.T) {
|
||||
// Marshal
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
@ -144,90 +191,126 @@ func Test_ListMap_Json(t *testing.T) {
|
||||
m1 := gmap.NewListMapFrom(data)
|
||||
b1, err1 := json.Marshal(m1)
|
||||
b2, err2 := json.Marshal(gconv.Map(data))
|
||||
gtest.Assert(err1, err2)
|
||||
gtest.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(gconv.Map(data))
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewListMap()
|
||||
err = json.Unmarshal(b, m)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(m.Get("k1"), data["k1"])
|
||||
gtest.Assert(m.Get("k2"), data["k2"])
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
data := g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
b, err := json.Marshal(gconv.Map(data))
|
||||
gtest.Assert(err, nil)
|
||||
t.Assert(err, nil)
|
||||
|
||||
var m gmap.ListMap
|
||||
err = json.Unmarshal(b, &m)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(m.Get("k1"), data["k1"])
|
||||
gtest.Assert(m.Get("k2"), data["k2"])
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMapFrom(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
gtest.Assert(m.Size(), 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
gtest.AssertIN(k1, g.Slice{"k1", "k2"})
|
||||
gtest.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 1)
|
||||
t.AssertIN(k1, g.Slice{"k1", "k2"})
|
||||
t.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
gtest.AssertIN(k2, g.Slice{"k1", "k2"})
|
||||
gtest.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 0)
|
||||
t.AssertIN(k2, g.Slice{"k1", "k2"})
|
||||
t.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
gtest.AssertNE(k1, k2)
|
||||
gtest.AssertNE(v1, v2)
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMapFrom(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
})
|
||||
gtest.Assert(m.Size(), 3)
|
||||
t.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
t.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 2)
|
||||
t.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
t.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
t.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 0)
|
||||
t.Assert(m.Size(), 0)
|
||||
|
||||
gtest.Assert(kArray.Unique().Len(), 3)
|
||||
gtest.Assert(vArray.Unique().Len(), 3)
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListMap_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Map *gmap.ListMap
|
||||
}
|
||||
// JSON
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": []byte(`{"1":"v1","2":"v2"}`),
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("1"), "v1")
|
||||
t.Assert(v.Map.Get("2"), "v2")
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"map": g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
},
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Map.Size(), 2)
|
||||
t.Assert(v.Map.Get("1"), "v1")
|
||||
t.Assert(v.Map.Get("2"), "v2")
|
||||
})
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user