func (c *Config) Set(key string, val interface{}, setByPath ...bool) (err error) {
	if c.opts.Readonly {
		return readonlyErr
	}
	c.lock.Lock()
	defer c.lock.Unlock()
	sep := c.opts.Delimiter
	if key = formatKey(key, string(sep)); key == "" {
		return keyIsEmptyErr
	}
	if strings.IndexByte(key, sep) == -1 {
		c.data[key] = val
		return
	}
	if len(setByPath) > 0 && !setByPath[0] {
		c.data[key] = val
		return
	}
	keys := strings.Split(key, string(sep))
	topK := keys[0]
	paths := keys[1:]
	var ok bool
	var item interface{}
	if item, ok = c.data[topK]; !ok {
		c.data[topK] = buildValueByPath(paths, val)
		return
	}
	switch typeData := item.(type) {
	case map[interface{}]interface{}:  
		dstItem := make(map[string]interface{})
		for k, v := range typeData {
			sk := fmt.Sprintf("%v", k)
			dstItem[sk] = v
		}
		newItem := buildValueByPath(paths, val)
		err = mergo.Merge(&dstItem, newItem, mergo.WithOverride)
		if err != nil {
			return
		}
		c.data[topK] = dstItem
	case map[string]interface{}:  
		newItem := buildValueByPath(paths, val)
		err = mergo.Merge(&typeData, &newItem, mergo.WithOverride)
		if err != nil {
			return
		}
		c.data[topK] = typeData
	case []interface{}:  
		index, err := strconv.Atoi(keys[1])
		if len(keys) == 2 && err == nil {
			if index <= len(typeData) {
				typeData[index] = val
			}
			c.data[topK] = typeData
		} else {
			err = errors.New("max allow 1 level for setting array value, current key: " + key)
			return err
		}
	default:
		c.data[key] = val
	}
	return
}
Set a value by key string.
package config
import (
	"fmt"
	"strconv"
	"strings"
	"github.com/imdario/mergo"
	"github.com/pkg/errors"
)
var (
	readonlyErr   = errors.New("the config instance in 'readonly' mode")
	keyIsEmptyErr = errors.New("the config key is cannot be empty")
)
func SetData(data map[string]interface{}) {
	dc.SetData(data)
}
func (c *Config) SetData(data map[string]interface{}) {
	c.data = data
}
func Set(key string, val interface{}, setByPath ...bool) error {
	return dc.Set(key, val, setByPath...)
}