Package set is a small wrapper around the official reflect package that facilitates loose type conversion and assignment into native Go types.

Documentation Go Report Card Build Status codecov License: MIT

Package set is a small wrapper around the official reflect package that facilitates loose type conversion and assignment into native Go types.

Read the godoc for more detailed explanations and examples but here are some enticing snippets.

Scalars and Type-Coercion

{
    // type coercion
    b, i := true, 42
    set.V(&b).To("False")    // Sets b to false
    set.V(&i).To("3.14")     // Sets i to 3
}

{
    // type coercion
    a := int(0)
    b := uint(42)
    set.V(&a).To(b)             // This coerces b into a if possible.
    set.V(&a).To("-57")         // Also works.
    set.V(&a).To("Hello")       // Returns an error.    
}

Pointer Allocation Plus Type-Coercion

{
    // pointer allocation and type coercion
    var bppp ***bool
    set.V(&bppp).To("True")
    fmt.Println(***bppp) // Prints true
}

Scalars-to-Slices and Slices-to-Scalars

{
    // assign scalars to slices
    var b []bool
    set.V(&b).To("True") // b is []bool{ true }

    // or slices to scalars (last element wins)
    var b bool
    set.V(&b).To([]bool{ false, false, true } ) // b is true, coercion not needed.
    set.V(&b).To([]interface{}{ float32(1), uint(0) }) // b is false, coercion needed.
}

Slices to Slices Including Type-Coercion

{
    // slices to slices with or without type coercion; new slice is always created!
    var t []bool
    var s []interface{}
    s = []interface{}{ "true", 0, float64(1) }
    set.V(&t).To(s) // b is []bool{ true, false, true }
}

{
    var t []bool
    var s []bool
    s = []bool{ true, false, true }
    set.V(&t).To(s) // b is []bool{ true, false, true } and t != s
}

Filling Structs by Field Name

m := map[string]interface{}{
    "Name": "Bob",
    "Age":  42,
    "Address": map[interface{}]string{
        "Street1": "97531 Some Street",
        "Street2": "",
        "City":    "Big City",
        "State":   "ST",
        "Zip":     "12345",
    },
}
myGetter := set.MapGetter(m)

type Address struct {
    Street1 string 
    Street2 string 
    City    string 
    State   string 
    Zip     string 
}
type Person struct {
    Name    string  
    Age     uint    
    Address Address 
}
var t Person
set.V(&t).Fill(myGetter)

Filling Structs by Struct Tag

m := map[string]interface{}{
    "name": "Bob",
    "age":  42,
    "address": map[interface{}]string{
        "street1": "97531 Some Street",
        "street2": "",
        "city":    "Big City",
        "state":   "ST",
        "zip":     "12345",
    },
}
myGetter := set.MapGetter(m)

type Address struct {
    Street1 string `key:"street1"`
    Street2 string `key:"street2"`
    City    string `key:"city"`
    State   string `key:"state"`
    Zip     string `key:"zip"`
}
type Person struct {
    Name    string  `key:"name"`
    Age     uint    `key:"age"`
    Address Address `key:"address"`
}
var t Person
set.V(&t).FillByTag("key", myGetter)

Allocating Struct Pointers and Pointer Fields

type Address struct {
    Street1 string `key:"street1"`
    Street2 string `key:"street2"`
    City    string `key:"city"`
    State   string `key:"state"`
    Zip     string `key:"zip"`
}
type Person struct {
    Name    string   `key:"name"`
    Age     uint     `key:"age"`
    Address *Address `key:"address"`
}
m := map[string]interface{}{
    "name": "Bob",
    "age":  42,
    "address": map[interface{}]string{
        "street1": "97531 Some Street",
        "street2": "",
        "city":    "Big City",
        "state":   "ST",
        "zip":     "12345",
    },
}
getter := set.MapGetter(m)

var t *Person
set.V(&t).FillByTag("key", getter)
fmt.Println(t.Name)                 // Bob
fmt.Println(t.Address.Street1)      // 97531 Some Street

Field Pointers Always Allocated

type Address struct {
    Street1 string `key:"street1"`
    Street2 string `key:"street2"`
    City    string `key:"city"`
    State   string `key:"state"`
    Zip     string `key:"zip"`
}
type Person struct {
    Name    string   `key:"name"`
    Age     uint     `key:"age"`
    Address *Address `key:"address"`
}
m := map[string]interface{}{
    "name": "Bob",
    "age":  42,
    // address is missing!
}
getter := set.MapGetter(m)
var t *Person
set.V(&t).FillByTag("key", getter)
fmt.Printf("%p\n", t.Address) // Prints a memory address; the field was allocated.

Pointer-to-Slices-of-Struct-Pointers -- OH MY!

Also noteworthy in this example is the same fuzzy logic for assigning scalar-to-slice or slice-to-scalar also works for struct-to-[]struct and []struct-to-struct.

func TestValue_fillNestedStructPointerToSlicesAsPointers(t *testing.T) {
    chk := assert.New(t)
    //
    var err error
    type Address struct {
        Street1 string `key:"street1"`
        Street2 string `key:"street2"`
        City    string `key:"city"`
        State   string `key:"state"`
        Zip     string `key:"zip"`
    }
    type Person struct {
        Name    string   `key:"name"`
        Age     uint     `key:"age"`
        Address *Address `key:"address"`
    }
    type Company struct {
        Name         string     `key:"name"`
        // Within the Getter "employees" is a []map so it is intuitive that Company.Employees becomes
        // a slice with as many entries as Getter( "employees" ).
        Employees    *[]*Person `key:"employees"`
        // The "employees" key is reused here but now it is going into a single struct pointer; the
        // last set of data in Getter( "employees" ) wins, aka Sally.
        LastEmployee *Person    `key:"employees"`
        // Note the "slice" key in the getter is not a []map itself; but a single *Person is
        // created and inserted into Company.Slice.
        Slice        *[]*Person `key:"slice"`
    }
    //
    // Also noteworthy is that Company.Employees and Company.Slice are pointers to slices and
    // they are instantiated automatically by this package.  The syntax to use them becomes unwieldly
    // so I don't know why you'd want to do this but hey -- it works and that's neat.
    //
    m := map[string]interface{}{
        "name": "Some Company",
        "slice": map[string]interface{}{
            "name": "Slice",
            "age":  2,
            "address": map[interface{}]string{
                "street1": "Slice Street",
                "street2": "",
                "city":    "Slice City",
                "state":   "SL",
                "zip":     "99999",
            },
        },
        "employees": []map[string]interface{}{
            {
                "name": "Bob",
                "age":  42,
                "address": map[interface{}]string{
                    "street1": "97531 Some Street",
                    "street2": "",
                    "city":    "Big City",
                    "state":   "ST",
                    "zip":     "12345",
                },
            },
            {
                "name": "Sally",
                "age":  48,
                "address": map[interface{}]string{
                    "street1": "555 Small Lane",
                    "street2": "",
                    "city":    "Other City",
                    "state":   "OO",
                    "zip":     "54321",
                },
            },
        },
    }
    getter := set.MapGetter(m)

    var t *Company
    err = set.V(&t).FillByTag("key", getter)
    chk.NoError(err)
    chk.Equal("Some Company", t.Name)
    //
    chk.Equal(2, len(*t.Employees))
    //
    chk.Equal("Bob", (*t.Employees)[0].Name)
    chk.Equal(uint(42), (*t.Employees)[0].Age)
    chk.Equal("97531 Some Street", (*t.Employees)[0].Address.Street1)
    chk.Equal("", (*t.Employees)[0].Address.Street2)
    chk.Equal("Big City", (*t.Employees)[0].Address.City)
    chk.Equal("ST", (*t.Employees)[0].Address.State)
    chk.Equal("12345", (*t.Employees)[0].Address.Zip)
    //
    chk.Equal("Sally", (*t.Employees)[1].Name)
    chk.Equal(uint(48), (*t.Employees)[1].Age)
    chk.Equal("555 Small Lane", (*t.Employees)[1].Address.Street1)
    chk.Equal("", (*t.Employees)[1].Address.Street2)
    chk.Equal("Other City", (*t.Employees)[1].Address.City)
    chk.Equal("OO", (*t.Employees)[1].Address.State)
    chk.Equal("54321", (*t.Employees)[1].Address.Zip)
    //
    chk.Equal("Sally", t.LastEmployee.Name)
    chk.Equal(uint(48), t.LastEmployee.Age)
    chk.Equal("555 Small Lane", t.LastEmployee.Address.Street1)
    chk.Equal("", t.LastEmployee.Address.Street2)
    chk.Equal("Other City", t.LastEmployee.Address.City)
    chk.Equal("OO", t.LastEmployee.Address.State)
    chk.Equal("54321", t.LastEmployee.Address.Zip)
    //
    chk.Equal(1, len(*t.Slice))
    chk.Equal("Slice", (*t.Slice)[0].Name)
    chk.Equal("Slice Street", (*t.Slice)[0].Address.Street1)
    chk.Equal("", (*t.Slice)[0].Address.Street2)
    chk.Equal("Slice City", (*t.Slice)[0].Address.City)
    chk.Equal("SL", (*t.Slice)[0].Address.State)
    chk.Equal("99999", (*t.Slice)[0].Address.Zip)
}
Similar Resources

succinct provides several static succinct data types

succinct provides several static succinct data types Succinct Set Synopsis Performance Implementation License Succinct Set

Jan 5, 2023

Go strcut based enum, support all types.

go-ultra-enum go-ultra-enum is an enum generator for Go. It is inspired by the powerful enum types found in Java. go-ultra-enum has the following capa

Dec 21, 2021

Custom generic HTTP handler providing automatic JSON decoding/encoding of HTTP request/response to your concrete types

gap Custom generic HTTP handler providing automatic JSON decoding/encoding of HTTP request/response to your concrete types. gap.Wrap allows to use the

Aug 28, 2022

Generic types that are missing from Go, including sets, trees, sorted lists, etc.

go-typ Generic types that are missing from Go, including sets, trees, sorted lists, etc. All code is implemented with 0 dependencies and in pure Go co

Dec 4, 2022

Type-agnostic partitioning for Go's indexable collections and strings.

Go Type-Agnostic Collection Partitioning Type-agnostic partitioning for anything that can be indexed in Go - slices, arrays,strings. Inspired by Guava

Aug 11, 2021

low level data type and utils in Golang.

low low level data type and utils in Golang. A stable low level function set is the basis of a robust architecture. It focuses on stability and requir

Dec 24, 2022

ID type with marshalling to/from hash to prevent sending IDs to clients.

ID type with marshalling to/from hash to prevent sending IDs to clients.

Hide IDs Hide is a simple package to provide an ID type that is marshalled to/from a hash string. This prevents sending technical IDs to clients and c

Dec 10, 2022

flexible data type for Go

flexible data type for Go

Generic flexible data type for Go support: Go 1.12+ Install standard go get: go get -u github.com/usk81/generic/v2 Usage encode/decode: package main

Dec 31, 2022

A faster method to get elements from an interface (Struct or Slice type) for Go.

A faster method to get elements from an interface (Struct or Slice type) for Go.

May 13, 2022
A simple set type for the Go language. Trusted by Docker, 1Password, Ethereum and Hashicorp.

golang-set The missing set collection for the Go language. Until Go has sets built-in...use this. Coming from Python one of the things I miss is the s

Jan 8, 2023
goxml - A thin wrapper around libxml2

golibxml golibxml is a simple wrapper for libxml. The goal is to avoid any extra magic so that a more friendly library can be written to sit on top of

Oct 25, 2020
Zero allocation Nullable structures in one library with handy conversion functions, marshallers and unmarshallers

nan - No Allocations Nevermore Package nan - Zero allocation Nullable structures in one library with handy conversion functions, marshallers and unmar

Dec 20, 2022
A fast (5x) string keyed read-only map for Go - particularly good for keys using a small set of nearby runes.

faststringmap faststringmap is a fast read-only string keyed map for Go (golang). For our use case it is approximately 5 times faster than using Go's

Jan 8, 2023
Provides conversion from athena outputs to strongly-typed data models.
Provides conversion from athena outputs to strongly-typed data models.

Provides conversion from athena outputs to strongly defined data models. Getting started Given the following data struct you define: type MyModel stru

Feb 7, 2022
Package iter provides generic, lazy iterators, functions for producing them from primitive types, as well as functions and methods for transforming and consuming them.

iter Package iter provides generic, lazy iterators, functions for producing them from primitive types, as well as functions and methods for transformi

Dec 16, 2022
Go library for encoding native Go structures into generic map values.

wstructs origin: github.com/things-go/structs Go library for encoding native Go structures into generic map values. Installation Use go get. go ge

Jan 10, 2022
Hybro - App To Play Around With Fyne; To Remind Myself To Drink Water

Hybro Drink Up Homie! App To Remind Myself To Drink Water. Probably also will ke

Jul 24, 2022
Package mafsa implements Minimal Acyclic Finite State Automata in Go, essentially a high-speed, memory-efficient, Unicode-friendly set of strings.

MA-FSA for Go Package mafsa implements Minimal Acyclic Finite State Automata (MA-FSA) with Minimal Perfect Hashing (MPH). Basically, it's a set of str

Oct 27, 2022
Nullable Go types that can be marshalled/unmarshalled to/from JSON.

Nullable Go types Description This package provides nullable Go types for bool, float64, int64, int32, string and time.Time replacing sql.NullString,

Dec 12, 2022