diff --git a/README.md b/README.md index ebf5bac..8f658ce 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,21 @@ if err != nil { panic(err) } -fmt.Printf("%#v", m) // prints: map[string]interface {}{"Name":"Arslan", "ID":123456, "Enabled":true} +fmt.Printf("%#v", m) ``` +```go +// convert it to a []interface{}. Slice values are sorted according to the +// field names. +m, err := structure.ToSlice(s) +if err != nil { + panic(err) +} + +// prints: []interface {}{true, 123456, "Arslan"} +fmt.Printf("%#v", m) + ```go // check if it's a struct or a pointer to struct if structure.IsStruct(s) { diff --git a/structure.go b/structure.go index b389fc7..df5720c 100644 --- a/structure.go +++ b/structure.go @@ -4,6 +4,7 @@ package structure import ( "errors" "reflect" + "sort" ) // ErrNotStruct is returned when the passed value is not a struct @@ -13,11 +14,11 @@ var ErrNotStruct = errors.New("not struct") // string is the struct fieldname but this can be changed by defining a // "structure" tag key if needed. Note that only exported fields of a struct // can be accessed, non exported fields will be neglected. -func ToMap(in interface{}) (map[string]interface{}, error) { +func ToMap(s interface{}) (map[string]interface{}, error) { out := make(map[string]interface{}) - t := reflect.TypeOf(in) - v := reflect.ValueOf(in) + t := reflect.TypeOf(s) + v := reflect.ValueOf(s) // if pointer get the underlying element≤ if t.Kind() == reflect.Ptr { @@ -50,10 +51,38 @@ func ToMap(in interface{}) (map[string]interface{}, error) { return out, nil } +// ToSlice converts a struct's field values to a []interface{}. Values are +// inserted and sorted according to the field names. Note that only exported +// fields of a struct can be accessed, non exported fields will be neglected. +func ToSlice(s interface{}) ([]interface{}, error) { + m, err := ToMap(s) + if err != nil { + return nil, err + } + + keys := make([]string, len(m)) + count := 0 + for k := range m { + keys[count] = k + count++ + } + + sort.Strings(keys) + + t := make([]interface{}, len(m)) + + for i, key := range keys { + t[i] = m[key] + } + + return t, nil + +} + // IsStruct returns true if the given variable is a struct or a pointer to // struct. -func IsStruct(strct interface{}) bool { - t := reflect.TypeOf(strct) +func IsStruct(s interface{}) bool { + t := reflect.TypeOf(s) if t.Kind() == reflect.Ptr { t = t.Elem() } diff --git a/structure_example_test.go b/structure_example_test.go index 3c5e7bf..eba2a26 100644 --- a/structure_example_test.go +++ b/structure_example_test.go @@ -58,3 +58,27 @@ func ExampleToMap_tags() { // false } + +func ExampleToSlice() { + type Server struct { + Name string + ID int32 + Enabled bool + } + + s := &Server{ + Name: "Fatih", + ID: 135790, + Enabled: false, + } + + m, err := ToSlice(s) + if err != nil { + panic(err) + } + + // note that the output is sorted according to the field names + fmt.Printf("%#v\n", m) + // Output: + // []interface {}{false, 135790, "Fatih"} +} diff --git a/structure_test.go b/structure_test.go index 5cacc67..25b624c 100644 --- a/structure_test.go +++ b/structure_test.go @@ -105,3 +105,36 @@ func TestStruct(t *testing.T) { } } + +func TestToSlice(t *testing.T) { + var T = struct { + A string + B int + C bool + }{ + A: "a-value", + B: 2, + C: true, + } + + s, err := ToSlice(T) + if err != nil { + t.Error(err) + } + + inSlice := func(val interface{}) bool { + for _, v := range s { + if reflect.DeepEqual(v, val) { + return true + } + } + + return false + } + + for _, val := range []interface{}{"a-value", 2, true} { + if !inSlice(val) { + t.Errorf("ToSlice should have the value %v", val) + } + } +}