From 4943a5380780423007b1f061bf6a6f00335cebd6 Mon Sep 17 00:00:00 2001 From: Felix Sun Date: Mon, 15 Apr 2024 18:21:52 +0800 Subject: [PATCH] Add ForEach --- example_test.go | 108 +++++++++++++++++++++++++++++++++++++----------- foreach.go | 82 ++++++++++++++++++++++++++++++++++++ go.mod | 2 +- 3 files changed, 167 insertions(+), 25 deletions(-) create mode 100644 foreach.go diff --git a/example_test.go b/example_test.go index 6ddab35..87bd8aa 100644 --- a/example_test.go +++ b/example_test.go @@ -55,7 +55,7 @@ func ExampleSet_1setfield() { Set(&p, "Gender", 1) printJsonV(p) fmt.Println(MustGet(&p, "Score")) - //Output: + // Output: // { // "Name": "Felix", // "Score": 66.88, @@ -77,7 +77,7 @@ func ExampleSet_2setstructproperty() { printJsonV(p) fmt.Println(MustGet(&p, ".Company.Phone.Number")) - //Output: + // Output: // { // "Name": "", // "Score": 0, @@ -112,7 +112,7 @@ func ExampleSet_3setsliceproperty() { fmt.Println(MustGet(&p, "Departments[4].Name")) fmt.Println(MustGet(&p, "Projects[0].Name")) - //Output: + // Output: // { // "Name": "", // "Score": 0, @@ -156,7 +156,7 @@ func ExampleSet_4setmapproperty() { printJsonV(p) fmt.Println(MustGet(&p, "Languages.zh_CN.Code")) - //Output: + // Output: // { // "Name": "", // "Score": 0, @@ -186,7 +186,7 @@ func ExampleSet_5setdeeper() { printJsonV(p) fmt.Println(MustGet(&p, "Projects[0].Members[0].Company.Phone.Number")) - //Output: + // Output: // { // "Name": "", // "Score": 0, @@ -232,7 +232,7 @@ func ExampleSet_6appendtoarray() { Set(&p, "Departments[]", d) } printJsonV(p) - //Output: + // Output: // { // "Name": "", // "Score": 0, @@ -287,12 +287,12 @@ func ExampleSet_6byteandstring() { fmt.Println(o.IntValue) fmt.Println(o.FloatValue) fmt.Println(o.IntValueForBytes) - //Output: - //hello - //Felix - //22 - //22.88 - //22 + // Output: + // hello + // Felix + // 22 + // 22.88 + // 22 } @@ -302,11 +302,10 @@ func ExampleSet_7notexists() { err := Set(&p, "Whatever.Not.Exists", "911") fmt.Println(err) - //Output: + // Output: // no such field } - // Get Type of a deep nested object func ExampleSet_8gettype() { type Variant struct { @@ -314,7 +313,7 @@ func ExampleSet_8gettype() { } type Product struct { Variants []*Variant - ByCode map[string]*Variant + ByCode map[string]*Variant } type Obj struct { MainProduct *Product @@ -330,15 +329,76 @@ func ExampleSet_8gettype() { fmt.Println(GetType(o, "MainProduct.ByCode")) fmt.Println(GetType(o, "x123.ByCode")) fmt.Println(GetType(o, "MainProduct.ByCode.abc.NotExist")) - //Output: - //string - //*reflectutils_test.Variant - //[]*reflectutils_test.Variant - //*reflectutils_test.Product - //*reflectutils_test.Variant - //map[string]*reflectutils_test.Variant - // - // + // Output: + // string + // *reflectutils_test.Variant + // []*reflectutils_test.Variant + // *reflectutils_test.Product + // *reflectutils_test.Variant + // map[string]*reflectutils_test.Variant + // + // +} + +// ForEach slice with any +func ExampleForEach_9_0_normal() { + var arr = []*Language{ + {"en_US", "United States"}, + {"zh_CN", "China"}, + } + + ForEach(arr, func(v any) { + ve := v.(*Language) + fmt.Printf("%s\n", ve.Name) + }) + // Output: + // United States + // China +} + +// ForEach slice with correct type +func ExampleForEach_9_1_with_type() { + var arr = []*Language{ + {"en_US", "United States"}, + {"zh_CN", "China"}, + } + + ForEach(arr, func(v *Language) { + fmt.Printf("%s\n", v.Name) + }) + // Output: + // United States + // China +} + +// ForEach map with any +func ExampleForEach_9_2_map_with_any() { + var m = map[string]*Language{ + "en_US": {"en_US", "United States"}, + "zh_CN": {"zh_CN", "China"}, + } + + ForEach(m, func(c any, v any) { + fmt.Printf("%s: %s\n", c.(string), v.(*Language).Name) + }) + // Output: + // en_US: United States + // zh_CN: China +} + +// ForEach map with any +func ExampleForEach_9_3_map_with_correct_type() { + var m = map[string]*Language{ + "en_US": {"en_US", "United States"}, + "zh_CN": {"zh_CN", "China"}, + } + + ForEach(m, func(c string, v *Language) { + fmt.Printf("%s: %s\n", c, v.Name) + }) + // Output: + // en_US: United States + // zh_CN: China } func printJsonV(v interface{}) { diff --git a/foreach.go b/foreach.go new file mode 100644 index 0000000..1016fe3 --- /dev/null +++ b/foreach.go @@ -0,0 +1,82 @@ +package reflectutils + +import ( + "fmt" + "reflect" +) + +func ForEach(arr interface{}, predicate interface{}) { + var ( + funcValue = reflect.ValueOf(predicate) + arrValue = reflect.ValueOf(arr) + arrType = arrValue.Type() + arrKind = arrType.Kind() + funcType = funcValue.Type() + ) + + if IsNil(arr) { + return + } + + if !(arrKind == reflect.Array || arrKind == reflect.Slice || arrKind == reflect.Map) { + panic("First parameter must be an iteratee") + } + + if arrKind == reflect.Slice || arrKind == reflect.Array { + if !isFunction(predicate, 1, 0) { + panic("Second argument must be a function with one parameter") + } + + arrElemType := arrValue.Type().Elem() + + // Checking whether element type is convertible to function's first argument's type. + if !arrElemType.ConvertibleTo(funcType.In(0)) { + panic("Map function's argument is not compatible with type of array.") + } + + for i := 0; i < arrValue.Len(); i++ { + funcValue.Call([]reflect.Value{arrValue.Index(i)}) + } + return + } + + if arrKind == reflect.Map { + if !isFunction(predicate, 2, 0) { + panic("Second argument must be a function with two parameters") + } + + // Type checking for Map = (key, value) + keyType := arrType.Key() + valueType := arrType.Elem() + + if !keyType.ConvertibleTo(funcType.In(0)) { + panic(fmt.Sprintf("function first argument is not compatible with %s", keyType.String())) + } + + if !valueType.ConvertibleTo(funcType.In(1)) { + panic(fmt.Sprintf("function second argument is not compatible with %s", valueType.String())) + } + + for _, key := range arrValue.MapKeys() { + funcValue.Call([]reflect.Value{key, arrValue.MapIndex(key)}) + } + return + } +} + +// isFunction returns if the argument is a function. +func isFunction(in interface{}, num ...int) bool { + funcType := reflect.TypeOf(in) + + result := funcType != nil && funcType.Kind() == reflect.Func + + if len(num) >= 1 { + result = result && funcType.NumIn() == num[0] + } + + if len(num) == 2 { + result = result && funcType.NumOut() == num[1] + } + + return result +} diff --git a/go.mod b/go.mod index ff3743d..91b1584 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/sunfmin/reflectutils -go 1.17 +go 1.22