mirror of
https://gitee.com/johng/gf
synced 2026-07-04 21:03:13 +08:00
add ListItemValues/ListItemValuesUnique functions and associated unit testing cases for package gutil/gvar
This commit is contained in:
25
container/gvar/gvar_list.go
Normal file
25
container/gvar/gvar_list.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2020 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 gvar
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
)
|
||||
|
||||
// ListItemValues retrieves and returns the elements of all item struct/map with key <key>.
|
||||
// Note that the parameter <list> should be type of slice which contains elements of map or struct,
|
||||
// or else it returns an empty slice.
|
||||
func (v *Var) ListItemValues(key interface{}) (values []interface{}) {
|
||||
return gutil.ListItemValues(v.Val(), key)
|
||||
}
|
||||
|
||||
// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key <key>.
|
||||
// Note that the parameter <list> should be type of slice which contains elements of map or struct,
|
||||
// or else it returns an empty slice.
|
||||
func (v *Var) ListItemValuesUnique(key string) []interface{} {
|
||||
return gutil.ListItemValuesUnique(v.Val(), key)
|
||||
}
|
||||
115
container/gvar/gvar_z_unit_list_test.go
Normal file
115
container/gvar/gvar_z_unit_list_test.go
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright 2020 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 gvar_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_ListItemValues_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
listMap := g.List{
|
||||
g.Map{"id": 1, "score": 100},
|
||||
g.Map{"id": 2, "score": 99},
|
||||
g.Map{"id": 3, "score": 99},
|
||||
}
|
||||
t.Assert(gvar.New(listMap).ListItemValues("id"), g.Slice{1, 2, 3})
|
||||
t.Assert(gvar.New(listMap).ListItemValues("score"), g.Slice{100, 99, 99})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
listMap := g.List{
|
||||
g.Map{"id": 1, "score": 100},
|
||||
g.Map{"id": 2, "score": nil},
|
||||
g.Map{"id": 3, "score": 0},
|
||||
}
|
||||
t.Assert(gvar.New(listMap).ListItemValues("id"), g.Slice{1, 2, 3})
|
||||
t.Assert(gvar.New(listMap).ListItemValues("score"), g.Slice{100, nil, 0})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListItemValues_Struct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type T struct {
|
||||
Id int
|
||||
Score float64
|
||||
}
|
||||
listStruct := g.Slice{
|
||||
T{1, 100},
|
||||
T{2, 99},
|
||||
T{3, 0},
|
||||
}
|
||||
t.Assert(gvar.New(listStruct).ListItemValues("Id"), g.Slice{1, 2, 3})
|
||||
t.Assert(gvar.New(listStruct).ListItemValues("Score"), g.Slice{100, 99, 0})
|
||||
})
|
||||
// Pointer items.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type T struct {
|
||||
Id int
|
||||
Score float64
|
||||
}
|
||||
listStruct := g.Slice{
|
||||
&T{1, 100},
|
||||
&T{2, 99},
|
||||
&T{3, 0},
|
||||
}
|
||||
t.Assert(gvar.New(listStruct).ListItemValues("Id"), g.Slice{1, 2, 3})
|
||||
t.Assert(gvar.New(listStruct).ListItemValues("Score"), g.Slice{100, 99, 0})
|
||||
})
|
||||
// Nil element value.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type T struct {
|
||||
Id int
|
||||
Score interface{}
|
||||
}
|
||||
listStruct := g.Slice{
|
||||
T{1, 100},
|
||||
T{2, nil},
|
||||
T{3, 0},
|
||||
}
|
||||
t.Assert(gvar.New(listStruct).ListItemValues("Id"), g.Slice{1, 2, 3})
|
||||
t.Assert(gvar.New(listStruct).ListItemValues("Score"), g.Slice{100, nil, 0})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListItemValuesUnique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
listMap := g.List{
|
||||
g.Map{"id": 1, "score": 100},
|
||||
g.Map{"id": 2, "score": 100},
|
||||
g.Map{"id": 3, "score": 100},
|
||||
g.Map{"id": 4, "score": 100},
|
||||
g.Map{"id": 5, "score": 100},
|
||||
}
|
||||
t.Assert(gvar.New(listMap).ListItemValuesUnique("id"), g.Slice{1, 2, 3, 4, 5})
|
||||
t.Assert(gvar.New(listMap).ListItemValuesUnique("score"), g.Slice{100})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
listMap := g.List{
|
||||
g.Map{"id": 1, "score": 100},
|
||||
g.Map{"id": 2, "score": 100},
|
||||
g.Map{"id": 3, "score": 100},
|
||||
g.Map{"id": 4, "score": 100},
|
||||
g.Map{"id": 5, "score": 99},
|
||||
}
|
||||
t.Assert(gvar.New(listMap).ListItemValuesUnique("id"), g.Slice{1, 2, 3, 4, 5})
|
||||
t.Assert(gvar.New(listMap).ListItemValuesUnique("score"), g.Slice{100, 99})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
listMap := g.List{
|
||||
g.Map{"id": 1, "score": 100},
|
||||
g.Map{"id": 2, "score": 100},
|
||||
g.Map{"id": 3, "score": 0},
|
||||
g.Map{"id": 4, "score": 100},
|
||||
g.Map{"id": 5, "score": 99},
|
||||
}
|
||||
t.Assert(gvar.New(listMap).ListItemValuesUnique("id"), g.Slice{1, 2, 3, 4, 5})
|
||||
t.Assert(gvar.New(listMap).ListItemValuesUnique("score"), g.Slice{100, 0, 99})
|
||||
})
|
||||
}
|
||||
106
util/gutil/gutil_list.go
Normal file
106
util/gutil/gutil_list.go
Normal file
@ -0,0 +1,106 @@
|
||||
// Copyright 2020 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 gutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// ListItemValues retrieves and returns the elements of all item struct/map with key <key>.
|
||||
// Note that the parameter <list> should be type of slice which contains elements of map or struct,
|
||||
// or else it returns an empty slice.
|
||||
func ListItemValues(list interface{}, key interface{}) (values []interface{}) {
|
||||
// If the given <list> is the most common used slice type []map[string]interface{},
|
||||
// it enhances the performance using type assertion.
|
||||
if l, ok := list.([]map[string]interface{}); ok {
|
||||
if mapKey, ok := key.(string); ok {
|
||||
if len(l) == 0 {
|
||||
return
|
||||
}
|
||||
if _, ok := l[0][mapKey]; !ok {
|
||||
return
|
||||
}
|
||||
values = make([]interface{}, len(l))
|
||||
for k, m := range l {
|
||||
values[k] = m[mapKey]
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// It uses reflect for common checks and converting.
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(list)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
values = []interface{}{}
|
||||
switch reflectKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
if reflectValue.Len() == 0 {
|
||||
return
|
||||
}
|
||||
var (
|
||||
itemValue reflect.Value
|
||||
givenKeyValue = reflect.ValueOf(key)
|
||||
)
|
||||
for i := 0; i < reflectValue.Len(); i++ {
|
||||
itemValue = reflectValue.Index(i)
|
||||
// If the items are type of interface{}.
|
||||
if itemValue.Kind() == reflect.Interface {
|
||||
itemValue = itemValue.Elem()
|
||||
}
|
||||
if itemValue.Kind() == reflect.Ptr {
|
||||
itemValue = itemValue.Elem()
|
||||
}
|
||||
switch itemValue.Kind() {
|
||||
case reflect.Map:
|
||||
v := itemValue.MapIndex(givenKeyValue)
|
||||
if v.IsValid() {
|
||||
values = append(values, v.Interface())
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
// The <mapKey> must be type of string.
|
||||
v := itemValue.FieldByName(givenKeyValue.String())
|
||||
if v.IsValid() {
|
||||
values = append(values, v.Interface())
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key <key>.
|
||||
// Note that the parameter <list> should be type of slice which contains elements of map or struct,
|
||||
// or else it returns an empty slice.
|
||||
func ListItemValuesUnique(list interface{}, key string) []interface{} {
|
||||
values := ListItemValues(list, key)
|
||||
if len(values) > 0 {
|
||||
var (
|
||||
ok bool
|
||||
m = make(map[interface{}]struct{}, len(values))
|
||||
)
|
||||
for i := 0; i < len(values); {
|
||||
if _, ok = m[values[i]]; ok {
|
||||
values = SliceDelete(values, i)
|
||||
} else {
|
||||
m[values[i]] = struct{}{}
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
return values
|
||||
}
|
||||
@ -13,3 +13,21 @@ func SliceCopy(data []interface{}) []interface{} {
|
||||
copy(newData, data)
|
||||
return newData
|
||||
}
|
||||
|
||||
// SliceDelete deletes an element at <index> and returns the new slice.
|
||||
// It does nothing if the given <index> is invalid.
|
||||
func SliceDelete(data []interface{}, index int) (newSlice []interface{}) {
|
||||
if index < 0 || index >= len(data) {
|
||||
return data
|
||||
}
|
||||
// Determine array boundaries when deleting to improve deletion efficiency.
|
||||
if index == 0 {
|
||||
return data[1:]
|
||||
} else if index == len(data)-1 {
|
||||
return data[:index]
|
||||
}
|
||||
// If it is a non-boundary delete,
|
||||
// it will involve the creation of an array,
|
||||
// then the deletion is less efficient.
|
||||
return append(data[:index], data[index+1:]...)
|
||||
}
|
||||
|
||||
116
util/gutil/gutil_z_unit_list_test.go
Executable file
116
util/gutil/gutil_z_unit_list_test.go
Executable file
@ -0,0 +1,116 @@
|
||||
// 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 this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gutil_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
)
|
||||
|
||||
func Test_ListItemValues_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
listMap := g.List{
|
||||
g.Map{"id": 1, "score": 100},
|
||||
g.Map{"id": 2, "score": 99},
|
||||
g.Map{"id": 3, "score": 99},
|
||||
}
|
||||
t.Assert(gutil.ListItemValues(listMap, "id"), g.Slice{1, 2, 3})
|
||||
t.Assert(gutil.ListItemValues(listMap, "score"), g.Slice{100, 99, 99})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
listMap := g.List{
|
||||
g.Map{"id": 1, "score": 100},
|
||||
g.Map{"id": 2, "score": nil},
|
||||
g.Map{"id": 3, "score": 0},
|
||||
}
|
||||
t.Assert(gutil.ListItemValues(listMap, "id"), g.Slice{1, 2, 3})
|
||||
t.Assert(gutil.ListItemValues(listMap, "score"), g.Slice{100, nil, 0})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListItemValues_Struct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type T struct {
|
||||
Id int
|
||||
Score float64
|
||||
}
|
||||
listStruct := g.Slice{
|
||||
T{1, 100},
|
||||
T{2, 99},
|
||||
T{3, 0},
|
||||
}
|
||||
t.Assert(gutil.ListItemValues(listStruct, "Id"), g.Slice{1, 2, 3})
|
||||
t.Assert(gutil.ListItemValues(listStruct, "Score"), g.Slice{100, 99, 0})
|
||||
})
|
||||
// Pointer items.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type T struct {
|
||||
Id int
|
||||
Score float64
|
||||
}
|
||||
listStruct := g.Slice{
|
||||
&T{1, 100},
|
||||
&T{2, 99},
|
||||
&T{3, 0},
|
||||
}
|
||||
t.Assert(gutil.ListItemValues(listStruct, "Id"), g.Slice{1, 2, 3})
|
||||
t.Assert(gutil.ListItemValues(listStruct, "Score"), g.Slice{100, 99, 0})
|
||||
})
|
||||
// Nil element value.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type T struct {
|
||||
Id int
|
||||
Score interface{}
|
||||
}
|
||||
listStruct := g.Slice{
|
||||
T{1, 100},
|
||||
T{2, nil},
|
||||
T{3, 0},
|
||||
}
|
||||
t.Assert(gutil.ListItemValues(listStruct, "Id"), g.Slice{1, 2, 3})
|
||||
t.Assert(gutil.ListItemValues(listStruct, "Score"), g.Slice{100, nil, 0})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListItemValuesUnique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
listMap := g.List{
|
||||
g.Map{"id": 1, "score": 100},
|
||||
g.Map{"id": 2, "score": 100},
|
||||
g.Map{"id": 3, "score": 100},
|
||||
g.Map{"id": 4, "score": 100},
|
||||
g.Map{"id": 5, "score": 100},
|
||||
}
|
||||
t.Assert(gutil.ListItemValuesUnique(listMap, "id"), g.Slice{1, 2, 3, 4, 5})
|
||||
t.Assert(gutil.ListItemValuesUnique(listMap, "score"), g.Slice{100})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
listMap := g.List{
|
||||
g.Map{"id": 1, "score": 100},
|
||||
g.Map{"id": 2, "score": 100},
|
||||
g.Map{"id": 3, "score": 100},
|
||||
g.Map{"id": 4, "score": 100},
|
||||
g.Map{"id": 5, "score": 99},
|
||||
}
|
||||
t.Assert(gutil.ListItemValuesUnique(listMap, "id"), g.Slice{1, 2, 3, 4, 5})
|
||||
t.Assert(gutil.ListItemValuesUnique(listMap, "score"), g.Slice{100, 99})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
listMap := g.List{
|
||||
g.Map{"id": 1, "score": 100},
|
||||
g.Map{"id": 2, "score": 100},
|
||||
g.Map{"id": 3, "score": 0},
|
||||
g.Map{"id": 4, "score": 100},
|
||||
g.Map{"id": 5, "score": 99},
|
||||
}
|
||||
t.Assert(gutil.ListItemValuesUnique(listMap, "id"), g.Slice{1, 2, 3, 4, 5})
|
||||
t.Assert(gutil.ListItemValuesUnique(listMap, "score"), g.Slice{100, 0, 99})
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user