diff --git a/container/garray/garray_z_example_int_test.go b/container/garray/garray_z_example_int_test.go new file mode 100644 index 000000000..c188e962a --- /dev/null +++ b/container/garray/garray_z_example_int_test.go @@ -0,0 +1,709 @@ +// Copyright GoFrame Author(https://goframe.org). 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 garray_test + +import ( + "fmt" + "github.com/gogf/gf/v2/container/garray" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/internal/json" + "github.com/gogf/gf/v2/util/gconv" +) + +func ExampleIntArray_Walk() { + var array garray.IntArray + tables := g.SliceInt{10, 20} + prefix := 99 + array.Append(tables...) + // Add prefix for given table names. + array.Walk(func(value int) int { + return prefix + value + }) + fmt.Println(array.Slice()) + + // Output: + // [109 119] +} + +func ExampleNewIntArray() { + s := garray.NewIntArray() + s.Append(10) + s.Append(20) + s.Append(15) + s.Append(30) + fmt.Println(s.Slice()) + + // Output: + // [10 20 15 30] +} + +func ExampleNewIntArraySize() { + s := garray.NewIntArraySize(3, 5) + s.Set(0, 10) + s.Set(1, 20) + s.Set(2, 15) + s.Set(3, 30) + fmt.Println(s.Slice(), s.Len(), cap(s.Slice())) + + // Output: + // [10 20 15] 3 5 +} + +func ExampleNewIntArrayFrom() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30}) + fmt.Println(s.Slice(), s.Len(), cap(s.Slice())) + + // Output: + // [10 20 15 30] 4 4 +} + +func ExampleNewIntArrayFromCopy() { + s := garray.NewIntArrayFromCopy(g.SliceInt{10, 20, 15, 30}) + fmt.Println(s.Slice(), s.Len(), cap(s.Slice())) + + // Output: + // [10 20 15 30] 4 4 +} + +func ExampleIntArray_At() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30}) + sAt := s.At(2) + fmt.Println(sAt) + + // Output: + // 15 +} + +func ExampleIntArray_Get() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30}) + sGet, sBool := s.Get(3) + fmt.Println(sGet, sBool) + + // Output: + // 30 true +} + +func ExampleIntArray_Set() { + s := garray.NewIntArraySize(3, 5) + s.Set(0, 10) + s.Set(1, 20) + s.Set(2, 15) + s.Set(3, 30) + fmt.Println(s.Slice()) + + // Output: + // [10 20 15] +} + +func ExampleIntArray_SetArray() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30}) + fmt.Println(s.Slice()) + + // Output: + // [10 20 15 30] +} + +func ExampleIntArray_Replace() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30}) + fmt.Println(s.Slice()) + s.Replace(g.SliceInt{12, 13}) + fmt.Println(s.Slice()) + + // Output: + // [10 20 15 30] + // [12 13 15 30] +} + +func ExampleIntArray_Sum() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30}) + a := s.Sum() + fmt.Println(a) + + // Output: + // 75 +} + +func ExampleIntArray_Sort() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30}) + a := s.Sort() + fmt.Println(a) + + // Output: + // [10,15,20,30] +} + +func ExampleIntArray_SortFunc() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30}) + fmt.Println(s) + s.SortFunc(func(v1, v2 int) bool { + //fmt.Println(v1,v2) + return v1 > v2 + }) + fmt.Println(s) + s.SortFunc(func(v1, v2 int) bool { + return v1 < v2 + }) + fmt.Println(s) + + // Output: + // [10,20,15,30] + // [30,20,15,10] + // [10,15,20,30] +} + +func ExampleIntArray_InsertBefore() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30}) + s.InsertBefore(1, 99) + fmt.Println(s.Slice()) + + // Output: + // [10 99 20 15 30] +} + +func ExampleIntArray_InsertAfter() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30}) + s.InsertAfter(1, 99) + fmt.Println(s.Slice()) + + // Output: + // [10 20 99 15 30] +} + +func ExampleIntArray_Remove() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30}) + fmt.Println(s) + s.Remove(1) + fmt.Println(s.Slice()) + + // Output: + // [10,20,15,30] + // [10 15 30] +} + +func ExampleIntArray_RemoveValue() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30}) + fmt.Println(s) + s.RemoveValue(20) + fmt.Println(s.Slice()) + + // Output: + // [10,20,15,30] + // [10 15 30] +} + +func ExampleIntArray_PushLeft() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30}) + fmt.Println(s) + s.PushLeft(96, 97, 98, 99) + fmt.Println(s.Slice()) + + // Output: + // [10,20,15,30] + // [96 97 98 99 10 20 15 30] +} + +func ExampleIntArray_PushRight() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30}) + fmt.Println(s) + s.PushRight(96, 97, 98, 99) + fmt.Println(s.Slice()) + + // Output: + // [10,20,15,30] + // [10 20 15 30 96 97 98 99] +} + +func ExampleIntArray_PopLeft() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30}) + fmt.Println(s) + s.PopLeft() + fmt.Println(s.Slice()) + + // Output: + // [10,20,15,30] + // [20 15 30] +} + +func ExampleIntArray_PopRight() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30}) + fmt.Println(s) + s.PopRight() + fmt.Println(s.Slice()) + + // Output: + // [10,20,15,30] + // [10 20 15] +} + +func ExampleIntArray_PopRand() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60, 70}) + fmt.Println(s) + r, _ := s.PopRand() + fmt.Println(s) + fmt.Println(r) + + // May Output: + // [10,20,15,30,40,50,60,70] + // [10,20,15,30,40,60,70] + // 50 +} + +func ExampleIntArray_PopRands() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + r := s.PopRands(2) + fmt.Println(s) + fmt.Println(r) + + // May Output: + // [10,20,15,30,40,50,60] + // [10,20,15,30,40] + // [50 60] +} + +func ExampleIntArray_PopLefts() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + r := s.PopLefts(2) + fmt.Println(s) + fmt.Println(r) + + // Output: + // [10,20,15,30,40,50,60] + // [15,30,40,50,60] + // [10 20] +} + +func ExampleIntArray_PopRights() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + r := s.PopRights(2) + fmt.Println(s) + fmt.Println(r) + + // Output: + // [10,20,15,30,40,50,60] + // [10,20,15,30,40] + // [50 60] +} + +func ExampleIntArray_Range() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + r := s.Range(2, 5) + fmt.Println(r) + + // Output: + // [10,20,15,30,40,50,60] + // [15 30 40] +} + +func ExampleIntArray_SubSlice() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + r := s.SubSlice(3, 4) + fmt.Println(r) + + // Output: + // [10,20,15,30,40,50,60] + // [30 40 50 60] +} + +func ExampleIntArray_Append() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + s.Append(96, 97, 98) + fmt.Println(s) + + // Output: + // [10,20,15,30,40,50,60] + // [10,20,15,30,40,50,60,96,97,98] +} + +func ExampleIntArray_Len() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + fmt.Println(s.Len()) + + // Output: + // [10,20,15,30,40,50,60] + // 7 +} + +func ExampleIntArray_Slice() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s.Slice()) + + // Output: + // [10 20 15 30 40 50 60] +} + +func ExampleIntArray_Interfaces() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + r := s.Interfaces() + fmt.Println(r) + + // Output: + // [10 20 15 30 40 50 60] +} + +func ExampleIntArray_Clone() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + r := s.Clone() + fmt.Println(r) + + // Output: + // [10,20,15,30,40,50,60] + // [10,20,15,30,40,50,60] +} + +func ExampleIntArray_Clear() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + fmt.Println(s.Clear()) + fmt.Println(s) + + // Output: + // [10,20,15,30,40,50,60] + // [] + // [] +} + +func ExampleIntArray_Contains() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s.Contains(20)) + fmt.Println(s.Contains(21)) + + // Output: + // true + // false +} + +func ExampleIntArray_Search() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s.Search(20)) + fmt.Println(s.Search(21)) + + // Output: + // 1 + // -1 +} + +func ExampleIntArray_Unique() { + s := garray.NewIntArray() + s.SetArray(g.SliceInt{10, 20, 15, 15, 20, 50, 60}) + fmt.Println(s) + fmt.Println(s.Unique()) + + // Output: + // [10,20,15,15,20,50,60] + // [10,20,15,50,60] +} + +func ExampleIntArray_LockFunc() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + s.LockFunc(func(array []int) { + for i := 0; i < len(array)-1; i++ { + fmt.Println(array[i]) + } + }) + + // Output: + // 10 + // 20 + // 15 + // 30 + // 40 + // 50 +} + +func ExampleIntArray_RLockFunc() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + s.RLockFunc(func(array []int) { + for i := 0; i < len(array); i++ { + fmt.Println(array[i]) + } + }) + + // Output: + // 10 + // 20 + // 15 + // 30 + // 40 + // 50 + // 60 +} + +func ExampleIntArray_Merge() { + s1 := garray.NewIntArray() + s2 := garray.NewIntArray() + s1.SetArray(g.SliceInt{10, 20, 15}) + s2.SetArray(g.SliceInt{40, 50, 60}) + fmt.Println(s1) + fmt.Println(s2) + s1.Merge(s2) + fmt.Println(s1) + + // Output: + // [10,20,15] + // [40,50,60] + // [10,20,15,40,50,60] +} + +func ExampleIntArray_Fill() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + s.Fill(2, 3, 99) + fmt.Println(s) + + // Output: + // [10,20,15,30,40,50,60] + // [10,20,99,99,99,50,60] +} + +func ExampleIntArray_Chunk() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + r := s.Chunk(3) + fmt.Println(r) + + // Output: + // [10,20,15,30,40,50,60] + // [[10 20 15] [30 40 50] [60]] +} + +func ExampleIntArray_Pad() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + s.Pad(8, 99) + fmt.Println(s) + s.Pad(-10, 89) + fmt.Println(s) + + // Output: + // [10,20,15,30,40,50,60,99] + // [89,89,10,20,15,30,40,50,60,99] +} + +func ExampleIntArray_Rand() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + fmt.Println(s.Rand()) + + // May Output: + // [10,20,15,30,40,50,60] + // 10 true +} + +func ExampleIntArray_Rands() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + fmt.Println(s.Rands(3)) + + // May Output: + // [10,20,15,30,40,50,60] + // [20 50 20] +} + +func ExampleIntArray_Shuffle() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + fmt.Println(s.Shuffle()) + + // May Output: + // [10,20,15,30,40,50,60] + // [10,40,15,50,20,60,30] +} + +func ExampleIntArray_Reverse() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + fmt.Println(s.Reverse()) + + // Output: + // [10,20,15,30,40,50,60] + // [60,50,40,30,15,20,10] +} + +func ExampleIntArray_Join() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + fmt.Println(s.Join(",")) + + // Output: + // [10,20,15,30,40,50,60] + // 10,20,15,30,40,50,60 +} + +func ExampleIntArray_CountValues() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 15, 40, 40, 40}) + fmt.Println(s.CountValues()) + + // Output: + // map[10:1 15:2 20:1 40:3] +} + +func ExampleIntArray_Iterator() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + s.Iterator(func(k int, v int) bool { + fmt.Println(k, v) + return true + }) + + // Output: + // 0 10 + // 1 20 + // 2 15 + // 3 30 + // 4 40 + // 5 50 + // 6 60 +} + +func ExampleIntArray_IteratorAsc() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + s.IteratorAsc(func(k int, v int) bool { + fmt.Println(k, v) + return true + }) + + // Output: + // 0 10 + // 1 20 + // 2 15 + // 3 30 + // 4 40 + // 5 50 + // 6 60 +} + +func ExampleIntArray_IteratorDesc() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + s.IteratorDesc(func(k int, v int) bool { + fmt.Println(k, v) + return true + }) + + // Output: + // 6 60 + // 5 50 + // 4 40 + // 3 30 + // 2 15 + // 1 20 + // 0 10 +} + +func ExampleIntArray_String() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s) + fmt.Println(s.String()) + + // Output: + // [10,20,15,30,40,50,60] + // [10,20,15,30,40,50,60] +} + +func ExampleIntArray_MarshalJSON() { + type Student struct { + Id int + Name string + Scores garray.IntArray + } + var array garray.IntArray + array.SetArray(g.SliceInt{98, 97, 96}) + s := Student{ + Id: 1, + Name: "john", + Scores: array, + } + b, _ := json.Marshal(s) + fmt.Println(string(b)) + + // Output: + // {"Id":1,"Name":"john","Scores":[98,97,96]} +} + +func ExampleIntArray_UnmarshalJSON() { + b := []byte(`{"Id":1,"Name":"john","Scores":[98,96,97]}`) + type Student struct { + Id int + Name string + Scores *garray.IntArray + } + s := Student{} + json.Unmarshal(b, &s) + fmt.Println(s) + + // Output: + // {1 john [98,96,97]} +} + +func ExampleIntArray_UnmarshalValue() { + type Student struct { + Name string + Scores *garray.IntArray + } + + var s *Student + gconv.Struct(g.Map{ + "name": "john", + "scores": g.SliceInt{96, 98, 97}, + }, &s) + fmt.Println(s) + + // Output: + // &{john [96,98,97]} +} + +func ExampleIntArray_FilterEmpty() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 40, 50, 0, 0, 0, 60}) + fmt.Println(s) + fmt.Println(s.FilterEmpty()) + + // Output: + // [10,40,50,0,0,0,60] + // [10,40,50,60] +} + +func ExampleIntArray_IsEmpty() { + s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30, 40, 50, 60}) + fmt.Println(s.IsEmpty()) + s1 := garray.NewIntArray() + fmt.Println(s1.IsEmpty()) + + // Output: + // false + // true +} diff --git a/container/garray/garray_z_example_sorted_str_test.go b/container/garray/garray_z_example_sorted_str_test.go new file mode 100644 index 000000000..0c9c83a51 --- /dev/null +++ b/container/garray/garray_z_example_sorted_str_test.go @@ -0,0 +1,573 @@ +// Copyright GoFrame Author(https://goframe.org). 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 garray_test + +import ( + "fmt" + "github.com/gogf/gf/v2/container/garray" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/internal/json" + "github.com/gogf/gf/v2/util/gconv" +) + +func ExampleSortedStrArray_Walk() { + var array garray.SortedStrArray + 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 ExampleNewSortedStrArray() { + s := garray.NewSortedStrArray() + s.Append("b") + s.Append("d") + s.Append("c") + s.Append("a") + fmt.Println(s.Slice()) + + // Output: + // [a b c d] +} + +func ExampleNewSortedStrArraySize() { + s := garray.NewSortedStrArraySize(3) + s.SetArray([]string{"b", "d", "a", "c"}) + fmt.Println(s.Slice(), s.Len(), cap(s.Slice())) + + // Output: + // [a b c d] 4 4 +} + +func ExampleNewStrArrayFromCopy() { + s := garray.NewSortedStrArrayFromCopy(g.SliceStr{"b", "d", "c", "a"}) + fmt.Println(s.Slice()) + + // Output: + // [a b c d] +} + +func ExampleSortedStrArray_At() { + s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "d", "c", "a"}) + sAt := s.At(2) + fmt.Println(s) + fmt.Println(sAt) + + // Output: + // ["a","b","c","d"] + // c + +} + +func ExampleSortedStrArray_Get() { + s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "d", "c", "a", "e"}) + sGet, sBool := s.Get(3) + fmt.Println(s) + fmt.Println(sGet, sBool) + + // Output: + // ["a","b","c","d","e"] + // d true +} + +func ExampleSortedStrArray_SetArray() { + s := garray.NewSortedStrArray() + s.SetArray([]string{"b", "d", "a", "c"}) + fmt.Println(s.Slice()) + + // Output: + // [a b c d] +} + +func ExampleSortedStrArray_SetUnique() { + s := garray.NewSortedStrArray() + s.SetArray([]string{"b", "d", "a", "c", "c", "a"}) + fmt.Println(s.SetUnique(true)) + + // Output: + // ["a","b","c","d"] +} + +func ExampleSortedStrArray_Sum() { + s := garray.NewSortedStrArray() + s.SetArray([]string{"5", "3", "2"}) + fmt.Println(s) + a := s.Sum() + fmt.Println(a) + + // Output: + // ["2","3","5"] + // 10 +} + +func ExampleSortedStrArray_Sort() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"b", "d", "a", "c"}) + fmt.Println(s) + a := s.Sort() + fmt.Println(a) + + // Output: + // ["a","b","c","d"] + // ["a","b","c","d"] +} + +func ExampleSortedStrArray_Remove() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"b", "d", "c", "a"}) + fmt.Println(s.Slice()) + s.Remove(1) + fmt.Println(s.Slice()) + + // Output: + // [a b c d] + // [a c d] +} + +func ExampleSortedStrArray_RemoveValue() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"b", "d", "c", "a"}) + fmt.Println(s.Slice()) + s.RemoveValue("b") + fmt.Println(s.Slice()) + + // Output: + // [a b c d] + // [a c d] +} + +func ExampleSortedStrArray_PopLeft() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"b", "d", "c", "a"}) + r, _ := s.PopLeft() + fmt.Println(r) + fmt.Println(s.Slice()) + + // Output: + // a + // [b c d] +} + +func ExampleSortedStrArray_PopRight() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"b", "d", "c", "a"}) + fmt.Println(s.Slice()) + r, _ := s.PopRight() + fmt.Println(r) + fmt.Println(s.Slice()) + + // Output: + // [a b c d] + // d + // [a b c] +} + +func ExampleSortedStrArray_PopRights() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + r := s.PopRights(2) + fmt.Println(r) + fmt.Println(s) + + // Output: + // [g h] + // ["a","b","c","d","e","f"] +} + +func ExampleSortedStrArray_Rand() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + r, _ := s.PopRand() + fmt.Println(r) + fmt.Println(s) + + // May Output: + // b + // ["a","c","d","e","f","g","h"] +} + +func ExampleSortedStrArray_PopRands() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + r := s.PopRands(2) + fmt.Println(r) + fmt.Println(s) + + // May Output: + // [d a] + // ["b","c","e","f","g","h"] +} + +func ExampleSortedStrArray_PopLefts() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + r := s.PopLefts(2) + fmt.Println(r) + fmt.Println(s) + + // Output: + // [a b] + // ["c","d","e","f","g","h"] +} + +func ExampleSortedStrArray_Range() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + r := s.Range(2, 5) + fmt.Println(r) + + // Output: + // [c d e] +} + +func ExampleSortedStrArray_SubSlice() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + r := s.SubSlice(3, 4) + fmt.Println(s.Slice()) + fmt.Println(r) + + // Output: + // [a b c d e f g h] + // [d e f g] +} + +func ExampleSortedStrArray_Add() { + s := garray.NewSortedStrArray() + s.Add("b", "d", "c", "a") + fmt.Println(s) + + // Output: + // ["a","b","c","d"] +} + +func ExampleSortedStrArray_Append() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"b", "d", "c", "a"}) + fmt.Println(s) + s.Append("f", "e", "g") + fmt.Println(s) + + // Output: + // ["a","b","c","d"] + // ["a","b","c","d","e","f","g"] +} + +func ExampleSortedStrArray_Len() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + fmt.Println(s) + fmt.Println(s.Len()) + + // Output: + // ["a","b","c","d","e","f","g","h"] + // 8 +} + +func ExampleSortedStrArray_Slice() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + fmt.Println(s.Slice()) + + // Output: + // [a b c d e f g h] +} + +func ExampleSortedStrArray_Interfaces() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + r := s.Interfaces() + fmt.Println(r) + + // Output: + // [a b c d e f g h] +} + +func ExampleSortedStrArray_Clone() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + r := s.Clone() + fmt.Println(r) + fmt.Println(s) + + // Output: + // ["a","b","c","d","e","f","g","h"] + // ["a","b","c","d","e","f","g","h"] +} + +func ExampleSortedStrArray_Clear() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + fmt.Println(s) + fmt.Println(s.Clear()) + fmt.Println(s) + + // Output: + // ["a","b","c","d","e","f","g","h"] + // [] + // [] +} + +func ExampleSortedStrArray_Contains() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + fmt.Println(s.Contains("e")) + fmt.Println(s.Contains("E")) + fmt.Println(s.Contains("z")) + + // Output: + // true + // false + // false +} + +func ExampleSortedStrArray_ContainsI() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + fmt.Println(s) + fmt.Println(s.ContainsI("E")) + fmt.Println(s.ContainsI("z")) + + // Output: + // ["a","b","c","d","e","f","g","h"] + // true + // false +} + +func ExampleSortedStrArray_Search() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + fmt.Println(s) + fmt.Println(s.Search("e")) + fmt.Println(s.Search("E")) + fmt.Println(s.Search("z")) + + // Output: + // ["a","b","c","d","e","f","g","h"] + // 4 + // -1 + // -1 +} + +func ExampleSortedStrArray_Unique() { + s := garray.NewSortedStrArray() + s.SetArray(g.SliceStr{"a", "b", "c", "c", "c", "d", "d"}) + fmt.Println(s) + fmt.Println(s.Unique()) + + // Output: + // ["a","b","c","c","c","d","d"] + // ["a","b","c","d"] +} + +func ExampleSortedStrArray_LockFunc() { + s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "c", "a"}) + s.LockFunc(func(array []string) { + array[len(array)-1] = "GF fans" + }) + fmt.Println(s) + + // Output: + // ["a","b","GF fans"] +} + +func ExampleSortedStrArray_RLockFunc() { + s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "c", "a"}) + s.RLockFunc(func(array []string) { + array[len(array)-1] = "GF fans" + fmt.Println(array[len(array)-1]) + }) + fmt.Println(s) + + // Output: + // GF fans + // ["a","b","GF fans"] +} + +func ExampleSortedStrArray_Merge() { + s1 := garray.NewSortedStrArray() + s2 := garray.NewSortedStrArray() + s1.SetArray(g.SliceStr{"b", "c", "a"}) + s2.SetArray(g.SliceStr{"e", "d", "f"}) + fmt.Println(s1) + fmt.Println(s2) + s1.Merge(s2) + fmt.Println(s1) + + // Output: + // ["a","b","c"] + // ["d","e","f"] + // ["a","b","c","d","e","f"] +} + +func ExampleSortedStrArray_Chunk() { + s := garray.NewSortedStrArrayFrom(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + r := s.Chunk(3) + fmt.Println(r) + + // Output: + // [[a b c] [d e f] [g h]] +} + +func ExampleSortedStrArray_Rands() { + s := garray.NewSortedStrArrayFrom(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + fmt.Println(s) + fmt.Println(s.Rands(3)) + + // May Output: + // ["a","b","c","d","e","f","g","h"] + // [h g c] +} + +func ExampleSortedStrArray_Join() { + s := garray.NewSortedStrArrayFrom(g.SliceStr{"c", "b", "a", "d", "f", "e", "h", "g"}) + fmt.Println(s.Join(",")) + + // Output: + // a,b,c,d,e,f,g,h +} + +func ExampleSortedStrArray_CountValues() { + s := garray.NewSortedStrArrayFrom(g.SliceStr{"a", "b", "c", "c", "c", "d", "d"}) + fmt.Println(s.CountValues()) + + // Output: + // map[a:1 b:1 c:3 d:2] +} + +func ExampleSortedStrArray_Iterator() { + s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "c", "a"}) + s.Iterator(func(k int, v string) bool { + fmt.Println(k, v) + return true + }) + + // Output: + // 0 a + // 1 b + // 2 c +} + +func ExampleSortedStrArray_IteratorAsc() { + s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "c", "a"}) + s.IteratorAsc(func(k int, v string) bool { + fmt.Println(k, v) + return true + }) + + // Output: + // 0 a + // 1 b + // 2 c +} + +func ExampleSortedStrArray_IteratorDesc() { + s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "c", "a"}) + s.IteratorDesc(func(k int, v string) bool { + fmt.Println(k, v) + return true + }) + + // Output: + // 2 c + // 1 b + // 0 a +} + +func ExampleSortedStrArray_String() { + s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "c", "a"}) + fmt.Println(s.String()) + + // Output: + // ["a","b","c"] +} + +func ExampleSortedStrArray_MarshalJSON() { + type Student struct { + ID int + Name string + Levels garray.SortedStrArray + } + r := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "c", "a"}) + s := Student{ + ID: 1, + Name: "john", + Levels: *r, + } + b, _ := json.Marshal(s) + fmt.Println(string(b)) + + // Output: + // {"ID":1,"Name":"john","Levels":["a","b","c"]} +} + +func ExampleSortedStrArray_UnmarshalJSON() { + b := []byte(`{"Id":1,"Name":"john","Lessons":["Math","English","Sport"]}`) + type Student struct { + Id int + Name string + Lessons *garray.StrArray + } + s := Student{} + json.Unmarshal(b, &s) + fmt.Println(s) + + // Output: + // {1 john ["Math","English","Sport"]} +} + +func ExampleSortedStrArray_UnmarshalValue() { + type Student struct { + Name string + Lessons *garray.StrArray + } + var s *Student + gconv.Struct(g.Map{ + "name": "john", + "lessons": []byte(`["Math","English","Sport"]`), + }, &s) + fmt.Println(s) + + var s1 *Student + gconv.Struct(g.Map{ + "name": "john", + "lessons": g.SliceStr{"Math", "English", "Sport"}, + }, &s1) + fmt.Println(s1) + + // Output: + // &{john ["Math","English","Sport"]} + // &{john ["Math","English","Sport"]} +} + +func ExampleSortedStrArray_FilterEmpty() { + s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "a", "", "c", "", "", "d"}) + fmt.Println(s) + fmt.Println(s.FilterEmpty()) + + // Output: + // ["","","","a","b","c","d"] + // ["a","b","c","d"] +} + +func ExampleSortedStrArray_IsEmpty() { + s := garray.NewSortedStrArrayFrom(g.SliceStr{"b", "a", "", "c", "", "", "d"}) + fmt.Println(s.IsEmpty()) + s1 := garray.NewSortedStrArray() + fmt.Println(s1.IsEmpty()) + + // Output: + // false + // true +} diff --git a/container/gset/gset_z_example_int_test.go b/container/gset/gset_z_example_int_test.go index 761e3552f..d5d2cb411 100644 --- a/container/gset/gset_z_example_int_test.go +++ b/container/gset/gset_z_example_int_test.go @@ -7,17 +7,401 @@ package gset_test import ( + "encoding/json" "fmt" "github.com/gogf/gf/v2/container/gset" + "github.com/gogf/gf/v2/frame/g" ) +// New create and returns a new set, which contains un-repeated items. +// The parameter `safe` is used to specify whether using set in concurrent-safety, +// which is false in default. +func ExampleNewIntSet() { + intSet := gset.NewIntSet() + intSet.Add([]int{1, 2, 3}...) + fmt.Println(intSet.Slice()) + + // May Output: + // [2 1 3] +} + +// NewIntSetFrom returns a new set from `items`. +func ExampleNewFrom() { + intSet := gset.NewIntSetFrom([]int{1, 2, 3}) + fmt.Println(intSet.Slice()) + + // May Output: + // [2 1 3] +} + +// Add adds one or multiple items to the set. +func ExampleIntSet_Add() { + intSet := gset.NewIntSetFrom([]int{1, 2, 3}) + intSet.Add(1) + fmt.Println(intSet.Slice()) + fmt.Println(intSet.AddIfNotExist(1)) + + // Mya Output: + // [1 2 3] + // false +} + +// AddIfNotExist checks whether item exists in the set, +// it adds the item to set and returns true if it does not exists in the set, +// or else it does nothing and returns false. +func ExampleIntSet_AddIfNotExist() { + intSet := gset.NewIntSetFrom([]int{1, 2, 3}) + intSet.Add(1) + fmt.Println(intSet.Slice()) + fmt.Println(intSet.AddIfNotExist(1)) + + // Mya Output: + // [1 2 3] + // false +} + +// AddIfNotExistFunc checks whether item exists in the set, +// it adds the item to set and returns true if it does not exists in the set and function `f` returns true, +// or else it does nothing and returns false. +// Note that, the function `f` is executed without writing lock. +func ExampleIntSet_AddIfNotExistFunc() { + intSet := gset.NewIntSetFrom([]int{1, 2, 3}) + intSet.Add(1) + fmt.Println(intSet.Slice()) + fmt.Println(intSet.AddIfNotExistFunc(5, func() bool { + return true + })) + + // May Output: + // [1 2 3] + // true +} + +// AddIfNotExistFunc checks whether item exists in the set, +// it adds the item to set and returns true if it does not exists in the set and function `f` returns true, +// or else it does nothing and returns false. +// Note that, the function `f` is executed without writing lock. +func ExampleIntSet_AddIfNotExistFuncLock() { + intSet := gset.NewIntSetFrom([]int{1, 2, 3}) + intSet.Add(1) + fmt.Println(intSet.Slice()) + fmt.Println(intSet.AddIfNotExistFuncLock(4, func() bool { + return true + })) + + // May Output: + // [1 2 3] + // true +} + +// Clear deletes all items of the set. +func ExampleIntSet_Clear() { + intSet := gset.NewIntSetFrom([]int{1, 2, 3}) + fmt.Println(intSet.Size()) + intSet.Clear() + fmt.Println(intSet.Size()) + + // Output: + // 3 + // 0 +} + +// Complement returns a new set which is the complement from `set` to `full`. +// Which means, all the items in `newSet` are in `full` and not in `set`. +// It returns the difference between `full` and `set` if the given set `full` is not the full set of `set`. +func ExampleIntSet_Complement() { + intSet := gset.NewIntSetFrom([]int{1, 2, 3, 4, 5}) + s := gset.NewIntSetFrom([]int{1, 2, 3}) + fmt.Println(s.Complement(intSet).Slice()) + + // May Output: + // [4 5] +} + +// Contains checks whether the set contains `item`. func ExampleIntSet_Contains() { - var set gset.IntSet - set.Add(1) - fmt.Println(set.Contains(1)) - fmt.Println(set.Contains(2)) + var set1 gset.IntSet + set1.Add(1, 4, 5, 6, 7) + fmt.Println(set1.Contains(1)) + + var set2 gset.IntSet + set2.Add(1, 4, 5, 6, 7) + fmt.Println(set2.Contains(8)) // Output: // true // false } + +// Diff returns a new set which is the difference set from `set` to `other`. +// Which means, all the items in `newSet` are in `set` but not in `other`. +func ExampleIntSet_Diff() { + s1 := gset.NewIntSetFrom([]int{1, 2, 3}) + s2 := gset.NewIntSetFrom([]int{1, 2, 3, 4}) + fmt.Println(s2.Diff(s1).Slice()) + + // Output: + // [4] +} + +// Equal checks whether the two sets equal. +func ExampleIntSet_Equal() { + s1 := gset.NewIntSetFrom([]int{1, 2, 3}) + s2 := gset.NewIntSetFrom([]int{1, 2, 3, 4}) + fmt.Println(s2.Equal(s1)) + + s3 := gset.NewIntSetFrom([]int{1, 2, 3}) + s4 := gset.NewIntSetFrom([]int{1, 2, 3}) + fmt.Println(s3.Equal(s4)) + + // Output: + // false + // true +} + +// Intersect returns a new set which is the intersection from `set` to `other`. +// Which means, all the items in `newSet` are in `set` and also in `other`. +func ExampleIntSet_Intersect() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2, 3}...) + var s2 gset.IntSet + s2.Add([]int{1, 2, 3, 4}...) + fmt.Println(s2.Intersect(s1).Slice()) + + // May Output: + // [1 2 3] +} + +// IsSubsetOf checks whether the current set is a sub-set of `other` +func ExampleIntSet_IsSubsetOf() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2, 3, 4}...) + var s2 gset.IntSet + s2.Add([]int{1, 2, 4}...) + fmt.Println(s2.IsSubsetOf(s1)) + + // Output: + // true +} + +// Iterator iterates the set readonly with given callback function `f`, +// if `f` returns true then continue iterating; or false to stop. +func ExampleIntSet_Iterator() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2, 3, 4}...) + s1.Iterator(func(v int) bool { + fmt.Println("Iterator", v) + return true + }) + // May Output: + // Iterator 2 + // Iterator 3 + // Iterator 1 + // Iterator 4 +} + +// Join joins items with a string `glue`. +func ExampleIntSet_Join() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2, 3, 4}...) + fmt.Println(s1.Join(",")) + + // May Output: + // 3,4,1,2 +} + +// LockFunc locks writing with callback function `f`. +func ExampleIntSet_LockFunc() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2}...) + s1.LockFunc(func(m map[int]struct{}) { + m[3] = struct{}{} + }) + fmt.Println(s1.Slice()) + + // May Output + // [2 3 1] +} + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func ExampleIntSet_MarshalJSON() { + type Student struct { + Id int + Name string + Scores *gset.IntSet + } + s := Student{ + Id: 1, + Name: "john", + Scores: gset.NewIntSetFrom([]int{100, 99, 98}), + } + b, _ := json.Marshal(s) + fmt.Println(string(b)) + + // May Output: + // {"Id":1,"Name":"john","Scores":[100,99,98]} +} + +// Merge adds items from `others` sets into `set`. +func ExampleIntSet_Merge() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2, 3, 4}...) + + s2 := gset.NewIntSet() + fmt.Println(s1.Merge(s2).Slice()) + + // May Output: + // [1 2 3 4] +} + +// Pops randomly pops an item from set. +func ExampleIntSet_Pop() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2, 3, 4}...) + + fmt.Println(s1.Pop()) + + // May Output: + // 1 +} + +// Pops randomly pops `size` items from set. +// It returns all items if size == -1. +func ExampleIntSet_Pops() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2, 3, 4}...) + for _, v := range s1.Pops(2) { + fmt.Println(v) + } + + // May Output: + // 1 + // 2 +} + +// RLockFunc locks reading with callback function `f`. +func ExampleIntSet_RLockFunc() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2, 3, 4}...) + s1.RLockFunc(func(m map[int]struct{}) { + fmt.Println(m) + }) + + // Output: + // map[1:{} 2:{} 3:{} 4:{}] +} + +// Remove deletes `item` from set. +func ExampleIntSet_Remove() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2, 3, 4}...) + s1.Remove(1) + fmt.Println(s1.Slice()) + + // May Output: + // [3 4 2] +} + +// Size returns the size of the set. +func ExampleIntSet_Size() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2, 3, 4}...) + fmt.Println(s1.Size()) + + // Output: + // 4 +} + +// Slice returns the a of items of the set as slice. +func ExampleIntSet_Slice() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2, 3, 4}...) + fmt.Println(s1.Slice()) + + // May Output: + // [1, 2, 3, 4] +} + +// String returns items as a string, which implements like json.Marshal does. +func ExampleIntSet_String() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2, 3, 4}...) + fmt.Println(s1.String()) + + // May Output: + // [1,2,3,4] +} + +// Sum sums items. Note: The items should be converted to int type, +// or you'd get a result that you unexpected. +func ExampleIntSet_Sum() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2, 3, 4}...) + fmt.Println(s1.Sum()) + + // Output: + // 10 +} + +// Union returns a new set which is the union of `set` and `other`. +// Which means, all the items in `newSet` are in `set` or in `other`. +func ExampleIntSet_Union() { + s1 := gset.NewIntSet() + s1.Add([]int{1, 2, 3, 4}...) + s2 := gset.NewIntSet() + s2.Add([]int{1, 2, 4}...) + fmt.Println(s1.Union(s2).Slice()) + + // May Output: + // [3 4 1 2] +} + +// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal. +func ExampleIntSet_UnmarshalJSON() { + b := []byte(`{"Id":1,"Name":"john","Scores":[100,99,98]}`) + type Student struct { + Id int + Name string + Scores *gset.IntSet + } + s := Student{} + json.Unmarshal(b, &s) + fmt.Println(s) + + // May Output: + // {1 john [100,99,98]} +} + +// UnmarshalValue is an interface implement which sets any type of value for set. +func ExampleIntSet_UnmarshalValue() { + b := []byte(`{"Id":1,"Name":"john","Scores":100,99,98}`) + type Student struct { + Id int + Name string + Scores *gset.IntSet + } + s := Student{} + json.Unmarshal(b, &s) + fmt.Println(s) + + // May Output: + // {1 john [100,99,98]} +} + +// Walk applies a user supplied function `f` to every item of set. +func ExampleIntSet_Walk() { + var ( + set gset.IntSet + names = g.SliceInt{1, 0} + delta = 10 + ) + set.Add(names...) + // Add prefix for given table names. + set.Walk(func(item int) int { + return delta + item + }) + fmt.Println(set.Slice()) + + // May Output: + // [12 60] +} diff --git a/database/gdb/gdb_z_mysql_association_scanlist_test.go b/database/gdb/gdb_z_mysql_association_scanlist_test.go index dee305cfc..5016d60a4 100644 --- a/database/gdb/gdb_z_mysql_association_scanlist_test.go +++ b/database/gdb/gdb_z_mysql_association_scanlist_test.go @@ -1776,7 +1776,7 @@ CREATE TABLE %s ( t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[4])["uid"], 3) t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[4])["score"], 5) }) - db.SetDebug(true) + // Result ScanList with struct elements and pointer attributes. gtest.C(t, func(t *gtest.T) { var users []Entity diff --git a/internal/utils/utils_list.go b/internal/utils/utils_list.go new file mode 100644 index 000000000..355ad9f8e --- /dev/null +++ b/internal/utils/utils_list.go @@ -0,0 +1,37 @@ +// Copyright GoFrame Author(https://goframe.org). 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 utils + +import "fmt" + +// ListToMapByKey converts `list` to a map[string]interface{} of which key is specified by `key`. +// Note that the item value may be type of slice. +func ListToMapByKey(list []map[string]interface{}, key string) map[string]interface{} { + var ( + s = "" + m = make(map[string]interface{}) + tempMap = make(map[string][]interface{}) + hasMultiValues bool + ) + for _, item := range list { + if k, ok := item[key]; ok { + s = fmt.Sprintf(`%v`, k) + tempMap[s] = append(tempMap[s], item) + if len(tempMap[s]) > 1 { + hasMultiValues = true + } + } + } + for k, v := range tempMap { + if hasMultiValues { + m[k] = v + } else { + m[k] = v[0] + } + } + return m +} diff --git a/internal/utils/utils_map.go b/internal/utils/utils_map.go new file mode 100644 index 000000000..6cb1b6067 --- /dev/null +++ b/internal/utils/utils_map.go @@ -0,0 +1,26 @@ +// Copyright GoFrame Author(https://goframe.org). 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 utils + +// MapPossibleItemByKey tries to find the possible key-value pair for given key ignoring cases and symbols. +// +// Note that this function might be of low performance. +func MapPossibleItemByKey(data map[string]interface{}, key string) (foundKey string, foundValue interface{}) { + if len(data) == 0 { + return + } + if v, ok := data[key]; ok { + return key, v + } + // Loop checking. + for k, v := range data { + if EqualFoldWithoutChars(k, key) { + return k, v + } + } + return "", nil +} diff --git a/text/gregex/gregex_z_example_test.go b/text/gregex/gregex_z_example_test.go new file mode 100644 index 000000000..ecf6128d7 --- /dev/null +++ b/text/gregex/gregex_z_example_test.go @@ -0,0 +1,280 @@ +// Copyright GoFrame Author(https://goframe.org). 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 gregex_test + +import ( + "bytes" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/text/gregex" + "strings" +) + +func ExampleIsMatch() { + patternStr := `\d+` + g.Dump(gregex.IsMatch(patternStr, []byte("hello 2022! hello gf!"))) + g.Dump(gregex.IsMatch(patternStr, nil)) + g.Dump(gregex.IsMatch(patternStr, []byte("hello gf!"))) + + // Output: + // true + // false + // false +} + +func ExampleIsMatchString() { + patternStr := `\d+` + g.Dump(gregex.IsMatchString(patternStr, "hello 2022! hello gf!")) + g.Dump(gregex.IsMatchString(patternStr, "hello gf!")) + g.Dump(gregex.IsMatchString(patternStr, "")) + + // Output: + // true + // false + // false +} + +func ExampleMatch() { + patternStr := `(\w+)=(\w+)` + matchStr := "https://goframe.org/pages/viewpage.action?pageId=1114219&searchId=8QC5D1D2E!" + // This method looks for the first match index + result, err := gregex.Match(patternStr, []byte(matchStr)) + g.Dump(result) + g.Dump(err) + + // Output: + // [ + // "pageId=1114219", + // "pageId", + // "1114219", + // ] + // +} + +func ExampleMatchString() { + patternStr := `(\w+)=(\w+)` + matchStr := "https://goframe.org/pages/viewpage.action?pageId=1114219&searchId=8QC5D1D2E!" + // This method looks for the first match index + result, err := gregex.MatchString(patternStr, matchStr) + g.Dump(result) + g.Dump(err) + + // Output: + // [ + // "pageId=1114219", + // "pageId", + // "1114219", + // ] + // +} + +func ExampleMatchAll() { + patternStr := `(\w+)=(\w+)` + matchStr := "https://goframe.org/pages/viewpage.action?pageId=1114219&searchId=8QC5D1D2E!" + result, err := gregex.MatchAll(patternStr, []byte(matchStr)) + g.Dump(result) + g.Dump(err) + + // Output: + // [ + // [ + // "pageId=1114219", + // "pageId", + // "1114219", + // ], + // [ + // "searchId=8QC5D1D2E", + // "searchId", + // "8QC5D1D2E", + // ], + // ] + // +} + +func ExampleMatchAllString() { + patternStr := `(\w+)=(\w+)` + matchStr := "https://goframe.org/pages/viewpage.action?pageId=1114219&searchId=8QC5D1D2E!" + result, err := gregex.MatchAllString(patternStr, matchStr) + g.Dump(result) + g.Dump(err) + + // Output: + // [ + // [ + // "pageId=1114219", + // "pageId", + // "1114219", + // ], + // [ + // "searchId=8QC5D1D2E", + // "searchId", + // "8QC5D1D2E", + // ], + // ] + // +} + +func ExampleQuote() { + result := gregex.Quote(`[1-9]\d+`) + g.Dump(result) + + // Output: + // "\[1-9\]\\d\+" +} + +func ExampleReplace() { + var ( + patternStr = `\d+` + str = "hello gf 2020!" + repStr = "2021" + result, err = gregex.Replace(patternStr, []byte(repStr), []byte(str)) + ) + g.Dump(err) + g.Dump(result) + + // Output: + // + // "hello gf 2021!" +} + +func ExampleReplaceFunc() { + // In contrast to [ExampleReplaceFunc] + // the result contains the `pattern' of all subpattern that use the matching function + result, err := gregex.ReplaceFuncMatch(`(\d+)~(\d+)`, []byte("hello gf 2018~2020!"), func(match [][]byte) []byte { + g.Dump(match) + match[2] = []byte("2021") + return bytes.Join(match[1:], []byte("~")) + }) + g.Dump(result) + g.Dump(err) + + // Output: + // [ + // "2018~2020", + // "2018", + // "2020", + // ] + // "hello gf 2018~2021!" + // +} + +func ExampleReplaceFuncMatch() { + var ( + patternStr = `(\d+)~(\d+)` + str = "hello gf 2018~2020!" + ) + // In contrast to [ExampleReplaceFunc] + // the result contains the `pattern' of all subpatterns that use the matching function + result, err := gregex.ReplaceFuncMatch(patternStr, []byte(str), func(match [][]byte) []byte { + g.Dump(match) + match[2] = []byte("2021") + return bytes.Join(match[1:], []byte("-")) + }) + g.Dump(result) + g.Dump(err) + + // Output: + // [ + // "2018~2020", + // "2018", + // "2020", + // ] + // "hello gf 2018-2021!" + // +} + +func ExampleReplaceString() { + patternStr := `\d+` + str := "hello gf 2020!" + replaceStr := "2021" + result, err := gregex.ReplaceString(patternStr, replaceStr, str) + + g.Dump(result) + g.Dump(err) + + // Output: + // "hello gf 2021!" + // +} + +func ExampleReplaceStringFunc() { + replaceStrMap := map[string]string{ + "2020": "2021", + } + // When the regular statement can match multiple results + // func can be used to further control the value that needs to be modified + result, err := gregex.ReplaceStringFunc(`\d+`, `hello gf 2018~2020!`, func(b string) string { + g.Dump(b) + if replaceStr, ok := replaceStrMap[b]; ok { + return replaceStr + } + return b + }) + g.Dump(result) + g.Dump(err) + + result, err = gregex.ReplaceStringFunc(`[a-z]*`, "gf@goframe.org", strings.ToUpper) + g.Dump(result) + g.Dump(err) + + // Output: + // "2018" + // "2020" + // "hello gf 2018~2021!" + // + // "GF@GOFRAME.ORG" + // +} + +func ExampleReplaceStringFuncMatch() { + var ( + patternStr = `([A-Z])\w+` + str = "hello Golang 2018~2021!" + ) + // In contrast to [ExampleReplaceFunc] + // the result contains the `pattern' of all subpatterns that use the matching function + result, err := gregex.ReplaceStringFuncMatch(patternStr, str, func(match []string) string { + g.Dump(match) + match[0] = "Gf" + return match[0] + }) + g.Dump(result) + g.Dump(err) + + // Output: + // [ + // "Golang", + // "G", + // ] + // "hello Gf 2018~2021!" + // +} + +func ExampleSplit() { + patternStr := `\d+` + str := "hello2020gf" + result := gregex.Split(patternStr, str) + g.Dump(result) + + // Output: + // [ + // "hello", + // "gf", + // ] +} + +func ExampleValidate() { + // Valid match statement + g.Dump(gregex.Validate(`\d+`)) + // Mismatched statement + g.Dump(gregex.Validate(`[a-9]\d+`)) + + // Output: + // + // { + // Code: "invalid character class range", + // Expr: "a-9", + // } +} diff --git a/text/gstr/gstr.go b/text/gstr/gstr.go index 7a08c0cf4..bc7b4d7bb 100644 --- a/text/gstr/gstr.go +++ b/text/gstr/gstr.go @@ -327,14 +327,7 @@ func Split(str, delimiter string) []string { // and calls Trim to every element of this array. It ignores the elements // which are empty after Trim. func SplitAndTrim(str, delimiter string, characterMask ...string) []string { - array := make([]string, 0) - for _, v := range strings.Split(str, delimiter) { - v = Trim(v, characterMask...) - if v != "" { - array = append(array, v) - } - } - return array + return utils.SplitAndTrim(str, delimiter, characterMask...) } // Join concatenates the elements of `array` to create a single string. The separator string diff --git a/util/gconv/gconv_scan.go b/util/gconv/gconv_scan.go index 8df73d0e7..5472bdd83 100644 --- a/util/gconv/gconv_scan.go +++ b/util/gconv/gconv_scan.go @@ -7,8 +7,11 @@ package gconv import ( + "database/sql" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/internal/structs" + "github.com/gogf/gf/v2/internal/utils" "reflect" ) @@ -38,7 +41,18 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) } pointerKind = pointerType.Kind() if pointerKind != reflect.Ptr { - return gerror.NewCodef(gcode.CodeInvalidParameter, "params should be type of pointer, but got type: %v", pointerKind) + if pointerValue.CanAddr() { + pointerValue = pointerValue.Addr() + pointerType = pointerValue.Type() + pointerKind = pointerType.Kind() + } else { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + "params should be type of pointer, but got type: %v", + pointerType, + ) + } + } // Direct assignment checks! var ( @@ -94,3 +108,410 @@ func Scan(params interface{}, pointer interface{}, mapping ...map[string]string) return doStruct(params, pointer, keyToAttributeNameMapping, "") } } + +// ScanList converts `structSlice` to struct slice which contains other complex struct attributes. +// Note that the parameter `structSlicePointer` should be type of *[]struct/*[]*struct. +// +// Usage example 1: Normal attribute struct relation: +// type EntityUser struct { +// Uid int +// Name string +// } +// type EntityUserDetail struct { +// Uid int +// Address string +// } +// type EntityUserScores struct { +// Id int +// Uid int +// Score int +// Course string +// } +// type Entity struct { +// User *EntityUser +// UserDetail *EntityUserDetail +// UserScores []*EntityUserScores +// } +// var users []*Entity +// ScanList(records, &users, "User") +// ScanList(records, &users, "User", "uid") +// ScanList(records, &users, "UserDetail", "User", "uid:Uid") +// ScanList(records, &users, "UserScores", "User", "uid:Uid") +// ScanList(records, &users, "UserScores", "User", "uid") +// +// +// Usage example 2: Embedded attribute struct relation: +// type EntityUser struct { +// Uid int +// Name string +// } +// type EntityUserDetail struct { +// Uid int +// Address string +// } +// type EntityUserScores struct { +// Id int +// Uid int +// Score int +// } +// type Entity struct { +// EntityUser +// UserDetail EntityUserDetail +// UserScores []EntityUserScores +// } +// +// var users []*Entity +// ScanList(records, &users) +// ScanList(records, &users, "UserDetail", "uid") +// ScanList(records, &users, "UserScores", "uid") +// +// +// The parameters "User/UserDetail/UserScores" in the example codes specify the target attribute struct +// that current result will be bound to. +// +// The "uid" in the example codes is the table field name of the result, and the "Uid" is the relational +// struct attribute name - not the attribute name of the bound to target. In the example codes, it's attribute +// name "Uid" of "User" of entity "Entity". It automatically calculates the HasOne/HasMany relationship with +// given `relation` parameter. +// +// See the example or unit testing cases for clear understanding for this function. +func ScanList(structSlice interface{}, structSlicePointer interface{}, bindToAttrName string, relationAttrNameAndFields ...string) (err error) { + var ( + relationAttrName string + relationFields string + ) + switch len(relationAttrNameAndFields) { + case 2: + relationAttrName = relationAttrNameAndFields[0] + relationFields = relationAttrNameAndFields[1] + case 1: + relationFields = relationAttrNameAndFields[0] + } + return doScanList(structSlice, structSlicePointer, bindToAttrName, relationAttrName, relationFields) +} + +// doScanList converts `structSlice` to struct slice which contains other complex struct attributes recursively. +// Note that the parameter `structSlicePointer` should be type of *[]struct/*[]*struct. +func doScanList(structSlice interface{}, structSlicePointer interface{}, bindToAttrName, relationAttrName, relationFields string) (err error) { + var ( + maps = Maps(structSlice) + ) + if len(maps) == 0 { + return nil + } + // Necessary checks for parameters. + if bindToAttrName == "" { + return gerror.NewCode(gcode.CodeInvalidParameter, `bindToAttrName should not be empty`) + } + + if relationAttrName == "." { + relationAttrName = "" + } + + var ( + reflectValue = reflect.ValueOf(structSlicePointer) + reflectKind = reflectValue.Kind() + ) + if reflectKind == reflect.Interface { + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + } + if reflectKind != reflect.Ptr { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + "structSlicePointer should be type of *[]struct/*[]*struct, but got: %v", + reflectKind, + ) + } + reflectValue = reflectValue.Elem() + reflectKind = reflectValue.Kind() + if reflectKind != reflect.Slice && reflectKind != reflect.Array { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + "structSlicePointer should be type of *[]struct/*[]*struct, but got: %v", + reflectKind, + ) + } + length := len(maps) + if length == 0 { + // The pointed slice is not empty. + if reflectValue.Len() > 0 { + // It here checks if it has struct item, which is already initialized. + // It then returns error to warn the developer its empty and no conversion. + if v := reflectValue.Index(0); v.Kind() != reflect.Ptr { + return sql.ErrNoRows + } + } + // Do nothing for empty struct slice. + return nil + } + var ( + arrayValue reflect.Value // Like: []*Entity + arrayItemType reflect.Type // Like: *Entity + reflectType = reflect.TypeOf(structSlicePointer) + ) + if reflectValue.Len() > 0 { + arrayValue = reflectValue + } else { + arrayValue = reflect.MakeSlice(reflectType.Elem(), length, length) + } + + // Slice element item. + arrayItemType = arrayValue.Index(0).Type() + + // Relation variables. + var ( + relationDataMap map[string]interface{} + relationFromFieldName string // Eg: relationKV: id:uid -> id + relationBindToFieldName string // Eg: relationKV: id:uid -> uid + ) + if len(relationFields) > 0 { + // The relation key string of table filed name and attribute name + // can be joined with char '=' or ':'. + array := utils.SplitAndTrim(relationFields, "=") + if len(array) == 1 { + // Compatible with old splitting char ':'. + array = utils.SplitAndTrim(relationFields, ":") + } + if len(array) == 1 { + // The relation names are the same. + array = []string{relationFields, relationFields} + } + if len(array) == 2 { + // Defined table field to relation attribute name. + // Like: + // uid:Uid + // uid:UserId + relationFromFieldName = array[0] + relationBindToFieldName = array[1] + if key, _ := utils.MapPossibleItemByKey(maps[0], relationFromFieldName); key == "" { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + `cannot find possible related table field name "%s" from given relation fields "%s"`, + relationFromFieldName, + relationFields, + ) + } else { + relationFromFieldName = key + } + } else { + return gerror.NewCode( + gcode.CodeInvalidParameter, + `parameter relationKV should be format of "ResultFieldName:BindToAttrName"`, + ) + } + if relationFromFieldName != "" { + // Note that the value might be type of slice. + relationDataMap = utils.ListToMapByKey(maps, relationFromFieldName) + } + if len(relationDataMap) == 0 { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + `cannot find the relation data map, maybe invalid relation fields given "%v"`, + relationFields, + ) + } + } + // Bind to target attribute. + var ( + ok bool + bindToAttrValue reflect.Value + bindToAttrKind reflect.Kind + bindToAttrType reflect.Type + bindToAttrField reflect.StructField + ) + if arrayItemType.Kind() == reflect.Ptr { + if bindToAttrField, ok = arrayItemType.Elem().FieldByName(bindToAttrName); !ok { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + `invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`, + bindToAttrName, + ) + } + } else { + if bindToAttrField, ok = arrayItemType.FieldByName(bindToAttrName); !ok { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + `invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`, + bindToAttrName, + ) + } + } + bindToAttrType = bindToAttrField.Type + bindToAttrKind = bindToAttrType.Kind() + + // Bind to relation conditions. + var ( + relationFromAttrValue reflect.Value + relationFromAttrField reflect.Value + relationBindToFieldNameChecked bool + ) + for i := 0; i < arrayValue.Len(); i++ { + arrayElemValue := arrayValue.Index(i) + // The FieldByName should be called on non-pointer reflect.Value. + if arrayElemValue.Kind() == reflect.Ptr { + // Like: []*Entity + arrayElemValue = arrayElemValue.Elem() + if !arrayElemValue.IsValid() { + // The element is nil, then create one and set it to the slice. + // The "reflect.New(itemType.Elem())" creates a new element and returns the address of it. + // For example: + // reflect.New(itemType.Elem()) => *Entity + // reflect.New(itemType.Elem()).Elem() => Entity + arrayElemValue = reflect.New(arrayItemType.Elem()).Elem() + arrayValue.Index(i).Set(arrayElemValue.Addr()) + } + } else { + // Like: []Entity + } + bindToAttrValue = arrayElemValue.FieldByName(bindToAttrName) + if relationAttrName != "" { + // Attribute value of current slice element. + relationFromAttrValue = arrayElemValue.FieldByName(relationAttrName) + if relationFromAttrValue.Kind() == reflect.Ptr { + relationFromAttrValue = relationFromAttrValue.Elem() + } + } else { + // Current slice element. + relationFromAttrValue = arrayElemValue + } + if len(relationDataMap) > 0 && !relationFromAttrValue.IsValid() { + return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields) + } + // Check and find possible bind to attribute name. + if relationFields != "" && !relationBindToFieldNameChecked { + relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName) + if !relationFromAttrField.IsValid() { + var ( + filedMap, _ = structs.FieldMap(structs.FieldMapInput{ + Pointer: relationFromAttrValue, + RecursiveOption: structs.RecursiveOptionEmbeddedNoTag, + }) + ) + if key, _ := utils.MapPossibleItemByKey(Map(filedMap), relationBindToFieldName); key == "" { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + `cannot find possible related attribute name "%s" from given relation fields "%s"`, + relationBindToFieldName, + relationFields, + ) + } else { + relationBindToFieldName = key + } + } + relationBindToFieldNameChecked = true + } + switch bindToAttrKind { + case reflect.Array, reflect.Slice: + if len(relationDataMap) > 0 { + relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName) + if relationFromAttrField.IsValid() { + //results := make(Result, 0) + results := make([]interface{}, 0) + for _, v := range SliceAny(relationDataMap[String(relationFromAttrField.Interface())]) { + item := v + results = append(results, item) + } + if err = Structs(results, bindToAttrValue.Addr()); err != nil { + return err + } + } else { + // Maybe the attribute does not exist yet. + return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields) + } + } else { + return gerror.NewCodef( + gcode.CodeInvalidParameter, + `relationKey should not be empty as field "%s" is slice`, + bindToAttrName, + ) + } + + case reflect.Ptr: + var element reflect.Value + if bindToAttrValue.IsNil() { + element = reflect.New(bindToAttrType.Elem()).Elem() + } else { + element = bindToAttrValue.Elem() + } + if len(relationDataMap) > 0 { + relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName) + if relationFromAttrField.IsValid() { + v := relationDataMap[String(relationFromAttrField.Interface())] + if v == nil { + // There's no relational data. + continue + } + if utils.IsSlice(v) { + if err = Struct(SliceAny(v)[0], element); err != nil { + return err + } + } else { + if err = Struct(v, element); err != nil { + return err + } + } + } else { + // Maybe the attribute does not exist yet. + return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields) + } + } else { + if i >= len(maps) { + // There's no relational data. + continue + } + v := maps[i] + if v == nil { + // There's no relational data. + continue + } + if err = Struct(v, element); err != nil { + return err + } + } + bindToAttrValue.Set(element.Addr()) + + case reflect.Struct: + if len(relationDataMap) > 0 { + relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName) + if relationFromAttrField.IsValid() { + relationDataItem := relationDataMap[String(relationFromAttrField.Interface())] + if relationDataItem == nil { + // There's no relational data. + continue + } + if utils.IsSlice(relationDataItem) { + if err = Struct(SliceAny(relationDataItem)[0], bindToAttrValue); err != nil { + return err + } + } else { + if err = Struct(relationDataItem, bindToAttrValue); err != nil { + return err + } + } + } else { + // Maybe the attribute does not exist yet. + return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields) + } + } else { + if i >= len(maps) { + // There's no relational data. + continue + } + relationDataItem := maps[i] + if relationDataItem == nil { + // There's no relational data. + continue + } + if err = Struct(relationDataItem, bindToAttrValue); err != nil { + return err + } + } + + default: + return gerror.NewCodef(gcode.CodeInvalidParameter, `unsupported attribute type: %s`, bindToAttrKind.String()) + } + } + reflect.ValueOf(structSlicePointer).Elem().Set(arrayValue) + return nil +} diff --git a/util/gconv/gconv_z_unit_scan_test.go b/util/gconv/gconv_z_unit_scan_test.go index 65248bced..e34f91d99 100644 --- a/util/gconv/gconv_z_unit_scan_test.go +++ b/util/gconv/gconv_z_unit_scan_test.go @@ -319,3 +319,285 @@ func Test_Scan_SameType_Just_Assign(t *testing.T) { t.Assert(*m1["int"], *m2["int"]) }) } + +func Test_ScanList_Basic(t *testing.T) { + // Struct attribute. + gtest.C(t, func(t *gtest.T) { + type EntityUser struct { + Uid int + Name string + } + + type EntityUserDetail struct { + Uid int + Address string + } + + type EntityUserScores struct { + Id int + Uid int + Score int + } + + type Entity struct { + User EntityUser + UserDetail EntityUserDetail + UserScores []EntityUserScores + } + + var ( + err error + entities []Entity + entityUsers = []EntityUser{ + {Uid: 1, Name: "name1"}, + {Uid: 2, Name: "name2"}, + {Uid: 3, Name: "name3"}, + } + userDetails = []EntityUserDetail{ + {Uid: 1, Address: "address1"}, + {Uid: 2, Address: "address2"}, + } + userScores = []EntityUserScores{ + {Id: 10, Uid: 1, Score: 100}, + {Id: 11, Uid: 1, Score: 60}, + {Id: 20, Uid: 2, Score: 99}, + } + ) + err = gconv.ScanList(entityUsers, &entities, "User") + t.AssertNil(err) + + err = gconv.ScanList(userDetails, &entities, "UserDetail", "User", "uid") + t.AssertNil(err) + + err = gconv.ScanList(userScores, &entities, "UserScores", "User", "uid") + t.AssertNil(err) + + t.Assert(len(entities), 3) + t.Assert(entities[0].User, entityUsers[0]) + t.Assert(entities[1].User, entityUsers[1]) + t.Assert(entities[2].User, entityUsers[2]) + + t.Assert(entities[0].UserDetail, userDetails[0]) + t.Assert(entities[1].UserDetail, userDetails[1]) + t.Assert(entities[2].UserDetail, EntityUserDetail{}) + + t.Assert(len(entities[0].UserScores), 2) + t.Assert(entities[0].UserScores[0], userScores[0]) + t.Assert(entities[0].UserScores[1], userScores[1]) + + t.Assert(len(entities[1].UserScores), 1) + t.Assert(entities[1].UserScores[0], userScores[2]) + + t.Assert(len(entities[2].UserScores), 0) + }) + // Pointer attribute. + gtest.C(t, func(t *gtest.T) { + type EntityUser struct { + Uid int + Name string + } + + type EntityUserDetail struct { + Uid int + Address string + } + + type EntityUserScores struct { + Id int + Uid int + Score int + } + + type Entity struct { + User *EntityUser + UserDetail *EntityUserDetail + UserScores []*EntityUserScores + } + + var ( + err error + entities []*Entity + entityUsers = []*EntityUser{ + {Uid: 1, Name: "name1"}, + {Uid: 2, Name: "name2"}, + {Uid: 3, Name: "name3"}, + } + userDetails = []*EntityUserDetail{ + {Uid: 1, Address: "address1"}, + {Uid: 2, Address: "address2"}, + } + userScores = []*EntityUserScores{ + {Id: 10, Uid: 1, Score: 100}, + {Id: 11, Uid: 1, Score: 60}, + {Id: 20, Uid: 2, Score: 99}, + } + ) + err = gconv.ScanList(entityUsers, &entities, "User") + t.AssertNil(err) + + err = gconv.ScanList(userDetails, &entities, "UserDetail", "User", "uid") + t.AssertNil(err) + + err = gconv.ScanList(userScores, &entities, "UserScores", "User", "uid") + t.AssertNil(err) + + t.Assert(len(entities), 3) + t.Assert(entities[0].User, entityUsers[0]) + t.Assert(entities[1].User, entityUsers[1]) + t.Assert(entities[2].User, entityUsers[2]) + + t.Assert(entities[0].UserDetail, userDetails[0]) + t.Assert(entities[1].UserDetail, userDetails[1]) + t.Assert(entities[2].UserDetail, nil) + + t.Assert(len(entities[0].UserScores), 2) + t.Assert(entities[0].UserScores[0], userScores[0]) + t.Assert(entities[0].UserScores[1], userScores[1]) + + t.Assert(len(entities[1].UserScores), 1) + t.Assert(entities[1].UserScores[0], userScores[2]) + + t.Assert(len(entities[2].UserScores), 0) + }) +} + +func Test_ScanList_Embedded(t *testing.T) { + // Struct attribute. + gtest.C(t, func(t *gtest.T) { + type EntityUser struct { + Uid int + Name string + } + + type EntityUserDetail struct { + Uid int + Address string + } + + type EntityUserScores struct { + Id int + Uid int + Score int + } + + type Entity struct { + EntityUser + UserDetail EntityUserDetail + UserScores []EntityUserScores + } + + var ( + err error + entities []Entity + entityUsers = []EntityUser{ + {Uid: 1, Name: "name1"}, + {Uid: 2, Name: "name2"}, + {Uid: 3, Name: "name3"}, + } + userDetails = []EntityUserDetail{ + {Uid: 1, Address: "address1"}, + {Uid: 2, Address: "address2"}, + } + userScores = []EntityUserScores{ + {Id: 10, Uid: 1, Score: 100}, + {Id: 11, Uid: 1, Score: 60}, + {Id: 20, Uid: 2, Score: 99}, + } + ) + err = gconv.Scan(entityUsers, &entities) + t.AssertNil(err) + + err = gconv.ScanList(userDetails, &entities, "UserDetail", "uid") + t.AssertNil(err) + + err = gconv.ScanList(userScores, &entities, "UserScores", "uid") + t.AssertNil(err) + + t.Assert(len(entities), 3) + t.Assert(entities[0].EntityUser, entityUsers[0]) + t.Assert(entities[1].EntityUser, entityUsers[1]) + t.Assert(entities[2].EntityUser, entityUsers[2]) + + t.Assert(entities[0].UserDetail, userDetails[0]) + t.Assert(entities[1].UserDetail, userDetails[1]) + t.Assert(entities[2].UserDetail, EntityUserDetail{}) + + t.Assert(len(entities[0].UserScores), 2) + t.Assert(entities[0].UserScores[0], userScores[0]) + t.Assert(entities[0].UserScores[1], userScores[1]) + + t.Assert(len(entities[1].UserScores), 1) + t.Assert(entities[1].UserScores[0], userScores[2]) + + t.Assert(len(entities[2].UserScores), 0) + }) + // Pointer attribute. + gtest.C(t, func(t *gtest.T) { + type EntityUser struct { + Uid int + Name string + } + + type EntityUserDetail struct { + Uid int + Address string + } + + type EntityUserScores struct { + Id int + Uid int + Score int + } + + type Entity struct { + *EntityUser + UserDetail *EntityUserDetail + UserScores []*EntityUserScores + } + + var ( + err error + entities []Entity + entityUsers = []EntityUser{ + {Uid: 1, Name: "name1"}, + {Uid: 2, Name: "name2"}, + {Uid: 3, Name: "name3"}, + } + userDetails = []EntityUserDetail{ + {Uid: 1, Address: "address1"}, + {Uid: 2, Address: "address2"}, + } + userScores = []EntityUserScores{ + {Id: 10, Uid: 1, Score: 100}, + {Id: 11, Uid: 1, Score: 60}, + {Id: 20, Uid: 2, Score: 99}, + } + ) + err = gconv.Scan(entityUsers, &entities) + t.AssertNil(err) + + err = gconv.ScanList(userDetails, &entities, "UserDetail", "uid") + t.AssertNil(err) + + err = gconv.ScanList(userScores, &entities, "UserScores", "uid") + t.AssertNil(err) + + t.Assert(len(entities), 3) + t.Assert(entities[0].EntityUser, entityUsers[0]) + t.Assert(entities[1].EntityUser, entityUsers[1]) + t.Assert(entities[2].EntityUser, entityUsers[2]) + + t.Assert(entities[0].UserDetail, userDetails[0]) + t.Assert(entities[1].UserDetail, userDetails[1]) + t.Assert(entities[2].UserDetail, nil) + + t.Assert(len(entities[0].UserScores), 2) + t.Assert(entities[0].UserScores[0], userScores[0]) + t.Assert(entities[0].UserScores[1], userScores[1]) + + t.Assert(len(entities[1].UserScores), 1) + t.Assert(entities[1].UserScores[0], userScores[2]) + + t.Assert(len(entities[2].UserScores), 0) + }) +} diff --git a/util/gutil/gutil_dump.go b/util/gutil/gutil_dump.go index 06ed56fd5..0e331426f 100644 --- a/util/gutil/gutil_dump.go +++ b/util/gutil/gutil_dump.go @@ -15,6 +15,16 @@ import ( "strings" ) +// iString is used for type assert api for String(). +type iString interface { + String() string +} + +// iMarshalJSON is the interface for custom Json marshaling. +type iMarshalJSON interface { + MarshalJSON() ([]byte, error) +} + // ExportOption specifies the behavior of function Export. type ExportOption struct { WithoutType bool // WithoutType specifies exported content has no type information. @@ -151,7 +161,7 @@ func doExport(value interface{}, indent string, buffer *bytes.Buffer, option doE "%s%v:%s", newIndent, mapKeyStr, - gstr.Repeat(" ", maxSpaceNum-tmpSpaceNum+1), + strings.Repeat(" ", maxSpaceNum-tmpSpaceNum+1), )) } else { buffer.WriteString(fmt.Sprintf( @@ -159,7 +169,7 @@ func doExport(value interface{}, indent string, buffer *bytes.Buffer, option doE newIndent, mapKey.Type().String(), mapKeyStr, - gstr.Repeat(" ", maxSpaceNum-tmpSpaceNum+1), + strings.Repeat(" ", maxSpaceNum-tmpSpaceNum+1), )) } doExport(reflectValue.MapIndex(mapKey).Interface(), newIndent, buffer, option) @@ -173,10 +183,31 @@ func doExport(value interface{}, indent string, buffer *bytes.Buffer, option doE RecursiveOption: structs.RecursiveOptionEmbeddedNoTag, }) if len(structFields) == 0 { - if option.WithoutType { - buffer.WriteString("{}") + var ( + structContentStr = "" + attributeCountStr = "0" + ) + if v, ok := value.(iString); ok { + structContentStr = v.String() + } else if v, ok := value.(iMarshalJSON); ok { + b, _ := v.MarshalJSON() + structContentStr = string(b) + } + if structContentStr == "" { + structContentStr = "{}" } else { - buffer.WriteString(fmt.Sprintf("%s(0) {}", reflectTypeName)) + structContentStr = fmt.Sprintf(`"%s"`, gstr.AddSlashes(structContentStr)) + attributeCountStr = fmt.Sprintf(`%d`, len(structContentStr)-2) + } + if option.WithoutType { + buffer.WriteString(structContentStr) + } else { + buffer.WriteString(fmt.Sprintf( + "%s(%s) %s", + reflectTypeName, + attributeCountStr, + structContentStr, + )) } return } @@ -202,7 +233,7 @@ func doExport(value interface{}, indent string, buffer *bytes.Buffer, option doE "%s%s:%s", newIndent, field.Name(), - gstr.Repeat(" ", maxSpaceNum-tmpSpaceNum+1), + strings.Repeat(" ", maxSpaceNum-tmpSpaceNum+1), )) doExport(field.Value.Interface(), newIndent, buffer, option) buffer.WriteString(",\n") diff --git a/util/gutil/gutil_list.go b/util/gutil/gutil_list.go index 14fd8e2f6..12a8735e3 100644 --- a/util/gutil/gutil_list.go +++ b/util/gutil/gutil_list.go @@ -7,6 +7,7 @@ package gutil import ( + "github.com/gogf/gf/v2/internal/utils" "reflect" ) @@ -130,3 +131,9 @@ func ListItemValuesUnique(list interface{}, key string, subKey ...interface{}) [ } return values } + +// ListToMapByKey converts `list` to a map[string]interface{} of which key is specified by `key`. +// Note that the item value may be type of slice. +func ListToMapByKey(list []map[string]interface{}, key string) map[string]interface{} { + return utils.ListToMapByKey(list, key) +} diff --git a/util/gutil/gutil_map.go b/util/gutil/gutil_map.go index 1a9b96029..2b5246e91 100644 --- a/util/gutil/gutil_map.go +++ b/util/gutil/gutil_map.go @@ -67,19 +67,7 @@ func MapMergeCopy(src ...map[string]interface{}) (copy map[string]interface{}) { // // Note that this function might be of low performance. func MapPossibleItemByKey(data map[string]interface{}, key string) (foundKey string, foundValue interface{}) { - if len(data) == 0 { - return - } - if v, ok := data[key]; ok { - return key, v - } - // Loop checking. - for k, v := range data { - if utils.EqualFoldWithoutChars(k, key) { - return k, v - } - } - return "", nil + return utils.MapPossibleItemByKey(data, key) } // MapContainsPossibleKey checks if the given `key` is contained in given map `data`. diff --git a/util/gutil/gutil_z_unit_dump_test.go b/util/gutil/gutil_z_unit_dump_test.go new file mode 100755 index 000000000..236d07add --- /dev/null +++ b/util/gutil/gutil_z_unit_dump_test.go @@ -0,0 +1,128 @@ +// Copyright GoFrame Author(https://goframe.org). 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/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/util/gmeta" + "testing" + + "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/util/gutil" +) + +func Test_Dump(t *testing.T) { + type CommonReq struct { + AppId int64 `json:"appId" v:"required" in:"path" des:"应用Id" sum:"应用Id Summary"` + ResourceId string `json:"resourceId" in:"query" des:"资源Id" sum:"资源Id Summary"` + } + type SetSpecInfo struct { + StorageType string `v:"required|in:CLOUD_PREMIUM,CLOUD_SSD,CLOUD_HSSD" des:"StorageType"` + Shards int32 `des:"shards 分片数" sum:"Shards Summary"` + Params []string `des:"默认参数(json 串-ClickHouseParams)" sum:"Params Summary"` + } + type CreateResourceReq struct { + CommonReq + gmeta.Meta `path:"/CreateResourceReq" method:"POST" tags:"default" sum:"CreateResourceReq sum"` + Name string + CreatedAt *gtime.Time + SetMap map[string]*SetSpecInfo + SetSlice []SetSpecInfo + Handler ghttp.HandlerFunc + internal string + } + req := &CreateResourceReq{ + CommonReq: CommonReq{ + AppId: 12345678, + ResourceId: "tdchqy-xxx", + }, + Name: "john", + CreatedAt: gtime.Now(), + SetMap: map[string]*SetSpecInfo{ + "test1": { + StorageType: "ssd", + Shards: 2, + Params: []string{"a", "b", "c"}, + }, + "test2": { + StorageType: "hssd", + Shards: 10, + Params: []string{}, + }, + }, + SetSlice: []SetSpecInfo{ + { + StorageType: "hssd", + Shards: 10, + Params: []string{"h"}, + }, + }, + } + gtest.C(t, func(t *gtest.T) { + gutil.Dump(map[int]int{ + 100: 100, + }) + gutil.Dump(req) + }) +} + +func TestDumpWithType(t *testing.T) { + type CommonReq struct { + AppId int64 `json:"appId" v:"required" in:"path" des:"应用Id" sum:"应用Id Summary"` + ResourceId string `json:"resourceId" in:"query" des:"资源Id" sum:"资源Id Summary"` + } + type SetSpecInfo struct { + StorageType string `v:"required|in:CLOUD_PREMIUM,CLOUD_SSD,CLOUD_HSSD" des:"StorageType"` + Shards int32 `des:"shards 分片数" sum:"Shards Summary"` + Params []string `des:"默认参数(json 串-ClickHouseParams)" sum:"Params Summary"` + } + type CreateResourceReq struct { + CommonReq + gmeta.Meta `path:"/CreateResourceReq" method:"POST" tags:"default" sum:"CreateResourceReq sum"` + Name string + CreatedAt *gtime.Time + SetMap map[string]*SetSpecInfo `v:"required" des:"配置Map"` + SetSlice []SetSpecInfo `v:"required" des:"配置Slice"` + Handler ghttp.HandlerFunc + internal string + } + req := &CreateResourceReq{ + CommonReq: CommonReq{ + AppId: 12345678, + ResourceId: "tdchqy-xxx", + }, + Name: "john", + CreatedAt: gtime.Now(), + SetMap: map[string]*SetSpecInfo{ + "test1": { + StorageType: "ssd", + Shards: 2, + Params: []string{"a", "b", "c"}, + }, + "test2": { + StorageType: "hssd", + Shards: 10, + Params: []string{}, + }, + }, + SetSlice: []SetSpecInfo{ + { + StorageType: "hssd", + Shards: 10, + Params: []string{"h"}, + }, + }, + } + gtest.C(t, func(t *gtest.T) { + gutil.DumpWithType(map[int]int{ + 100: 100, + }) + gutil.DumpWithType(req) + gutil.DumpWithType([][]byte{[]byte("hello")}) + }) +} diff --git a/util/gutil/gutil_z_unit_test.go b/util/gutil/gutil_z_unit_test.go index 253b89373..0459ad4f7 100755 --- a/util/gutil/gutil_z_unit_test.go +++ b/util/gutil/gutil_z_unit_test.go @@ -8,129 +8,12 @@ package gutil_test import ( "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/net/ghttp" - "github.com/gogf/gf/v2/util/gmeta" "testing" "github.com/gogf/gf/v2/test/gtest" "github.com/gogf/gf/v2/util/gutil" ) -func Test_Dump(t *testing.T) { - type CommonReq struct { - AppId int64 `json:"appId" v:"required" in:"path" des:"应用Id" sum:"应用Id Summary"` - ResourceId string `json:"resourceId" in:"query" des:"资源Id" sum:"资源Id Summary"` - } - type SetSpecInfo struct { - StorageType string `v:"required|in:CLOUD_PREMIUM,CLOUD_SSD,CLOUD_HSSD" des:"StorageType"` - Shards int32 `des:"shards 分片数" sum:"Shards Summary"` - Params []string `des:"默认参数(json 串-ClickHouseParams)" sum:"Params Summary"` - } - type CreateResourceReq struct { - CommonReq - gmeta.Meta `path:"/CreateResourceReq" method:"POST" tags:"default" sum:"CreateResourceReq sum"` - Name string `des:"实例名称"` - Product string `des:"业务类型"` - Region string `v:"required" des:"区域"` - SetMap map[string]*SetSpecInfo `v:"required" des:"配置Map"` - SetSlice []SetSpecInfo `v:"required" des:"配置Slice"` - Handler ghttp.HandlerFunc - internal string - } - req := &CreateResourceReq{ - CommonReq: CommonReq{ - AppId: 12345678, - ResourceId: "tdchqy-xxx", - }, - Name: "john", - Product: "goframe", - Region: "cd", - SetMap: map[string]*SetSpecInfo{ - "test1": { - StorageType: "ssd", - Shards: 2, - Params: []string{"a", "b", "c"}, - }, - "test2": { - StorageType: "hssd", - Shards: 10, - Params: []string{}, - }, - }, - SetSlice: []SetSpecInfo{ - { - StorageType: "hssd", - Shards: 10, - Params: []string{"h"}, - }, - }, - } - gtest.C(t, func(t *gtest.T) { - gutil.Dump(map[int]int{ - 100: 100, - }) - gutil.Dump(req) - }) -} - -func TestDumpWithType(t *testing.T) { - type CommonReq struct { - AppId int64 `json:"appId" v:"required" in:"path" des:"应用Id" sum:"应用Id Summary"` - ResourceId string `json:"resourceId" in:"query" des:"资源Id" sum:"资源Id Summary"` - } - type SetSpecInfo struct { - StorageType string `v:"required|in:CLOUD_PREMIUM,CLOUD_SSD,CLOUD_HSSD" des:"StorageType"` - Shards int32 `des:"shards 分片数" sum:"Shards Summary"` - Params []string `des:"默认参数(json 串-ClickHouseParams)" sum:"Params Summary"` - } - type CreateResourceReq struct { - CommonReq - gmeta.Meta `path:"/CreateResourceReq" method:"POST" tags:"default" sum:"CreateResourceReq sum"` - Name string `des:"实例名称"` - Product string `des:"业务类型"` - Region string `v:"required" des:"区域"` - SetMap map[string]*SetSpecInfo `v:"required" des:"配置Map"` - SetSlice []SetSpecInfo `v:"required" des:"配置Slice"` - Handler ghttp.HandlerFunc - internal string - } - req := &CreateResourceReq{ - CommonReq: CommonReq{ - AppId: 12345678, - ResourceId: "tdchqy-xxx", - }, - Name: "john", - Product: "goframe", - Region: "cd", - SetMap: map[string]*SetSpecInfo{ - "test1": { - StorageType: "ssd", - Shards: 2, - Params: []string{"a", "b", "c"}, - }, - "test2": { - StorageType: "hssd", - Shards: 10, - Params: []string{}, - }, - }, - SetSlice: []SetSpecInfo{ - { - StorageType: "hssd", - Shards: 10, - Params: []string{"h"}, - }, - }, - } - gtest.C(t, func(t *gtest.T) { - gutil.DumpWithType(map[int]int{ - 100: 100, - }) - gutil.DumpWithType(req) - gutil.DumpWithType([][]byte{[]byte("hello")}) - }) -} - func Test_Try(t *testing.T) { gtest.C(t, func(t *gtest.T) { s := `gutil Try test`