feat: 支持切片追加及 -1 头部插入
This commit is contained in:
parent
cbe079ddcd
commit
b373dde7f7
40
README.md
40
README.md
@ -246,6 +246,46 @@ rfx.Set("NewMap.key", "value") // 如果 NewMap 是 nil,会自动初始化
|
||||
|
||||
**注意**: 如果设置失败(路径不存在、类型不匹配等),会 panic。
|
||||
|
||||
#### 切片追加 (Append)
|
||||
|
||||
`Append` 用于在当前 `R` 对应的切片上追加一个或多个元素,并支持自动类型转换:
|
||||
|
||||
```go
|
||||
// 追加到顶层切片
|
||||
items := []string{"apple", "banana"}
|
||||
rfx := reflux.New(&items)
|
||||
|
||||
// 追加单个和多个元素
|
||||
rfx.Append("cherry")
|
||||
rfx.Append("durian", "kiwi")
|
||||
// items == []string{"apple", "banana", "cherry", "durian", "kiwi"}
|
||||
|
||||
// 使用索引 -1 在切片前面插入新元素
|
||||
// 相当于在开头插入,原有元素整体后移
|
||||
rfx.Set("-1", "first")
|
||||
// 对于上面的 items,现在结果为:
|
||||
// items == []string{"first", "apple", "banana", "cherry", "durian", "kiwi"}
|
||||
|
||||
// 追加时支持类型转换
|
||||
nums := []int{1, 2}
|
||||
rfxNums := reflux.New(&nums)
|
||||
rfxNums.Append("3", 4.0) // "3" -> 3, 4.0 -> 4
|
||||
// nums == []int{1, 2, 3, 4}
|
||||
|
||||
// 追加到嵌套切片字段
|
||||
type Container struct {
|
||||
Tags []string
|
||||
}
|
||||
c := Container{Tags: []string{"go"}}
|
||||
rfxContainer := reflux.New(&c)
|
||||
rfxContainer.Get("Tags").Append("rust", "python")
|
||||
// c.Tags == []string{"go", "rust", "python"}
|
||||
```
|
||||
|
||||
**注意**:
|
||||
- 只能对**切片类型**调用 `Append`,否则会 panic
|
||||
- 追加多个值时会一次性追加,避免多次扩容
|
||||
|
||||
### 5. 删除值 (Delete)
|
||||
|
||||
`Delete` 方法支持链式调用,返回 Reflux 自身:
|
||||
|
||||
16
reflux.go
16
reflux.go
@ -18,6 +18,11 @@ type R interface {
|
||||
// 示例: rfx.Set("name", "Alice").Set("age", 30)
|
||||
Set(key string, v any) R
|
||||
|
||||
// Append 追加指定路径的值
|
||||
// 参数 items 为要追加的值
|
||||
// 返回当前 R 实例以支持链式调用
|
||||
Append(items ...any) R
|
||||
|
||||
// Delete 删除指定路径的值
|
||||
// 参数 p 为路径片段
|
||||
// 返回当前 R 实例以支持链式调用
|
||||
@ -123,7 +128,7 @@ type R interface {
|
||||
// - 如果传入指针: 将直接使用该指针,可以修改原始数据
|
||||
// - 如果传入值: 会自动创建一个深度克隆的指针副本,修改不影响原始数据
|
||||
// 支持的类型: map、struct、slice、array
|
||||
// 也支持 interface 类型,会自动解析到实际类型
|
||||
// 也支持 interface 类型以及部分基础类型(string/bool/float),会自动解析到实际类型
|
||||
// 返回一个 R 接口实例,可用于访问和操作嵌套的字段、元素和键值对
|
||||
func New(v any) R {
|
||||
rv := reflect.ValueOf(v)
|
||||
@ -150,7 +155,14 @@ func New(v any) R {
|
||||
|
||||
// 检查最终的实际类型是否为支持的类型
|
||||
switch actualValue.Kind() {
|
||||
case reflect.Map, reflect.Struct, reflect.Slice, reflect.Array:
|
||||
case reflect.Map,
|
||||
reflect.Struct,
|
||||
reflect.Slice,
|
||||
reflect.Array,
|
||||
reflect.String,
|
||||
reflect.Bool,
|
||||
reflect.Float32,
|
||||
reflect.Float64:
|
||||
// 支持的类型
|
||||
default:
|
||||
panic("rfx: unsupported type, only map, struct, slice, and array are allowed")
|
||||
|
||||
67
rfx.go
67
rfx.go
@ -115,6 +115,42 @@ func (r *rfx) Set(key string, v any) R {
|
||||
return r
|
||||
}
|
||||
|
||||
// Append 追加指定路径的值
|
||||
// 参数 items 为要追加的值
|
||||
// 返回当前 R 实例以支持链式调用
|
||||
func (r *rfx) Append(items ...any) R {
|
||||
// 没有要追加的元素,直接返回
|
||||
if len(items) == 0 {
|
||||
return r
|
||||
}
|
||||
|
||||
target := r.getParentValue()
|
||||
for target.Kind() == reflect.Ptr || target.Kind() == reflect.Interface {
|
||||
if target.IsNil() {
|
||||
panic("rfx: cannot append to nil value")
|
||||
}
|
||||
target = target.Elem()
|
||||
}
|
||||
|
||||
if !target.IsValid() || target.Kind() != reflect.Slice {
|
||||
panic("rfx: Append only supports slice type")
|
||||
}
|
||||
|
||||
// 一次性构造所有要追加的元素,然后调用一次 reflect.Append
|
||||
elemType := target.Type().Elem()
|
||||
newValues := make([]reflect.Value, len(items))
|
||||
for i, item := range items {
|
||||
newElem := reflect.New(elemType).Elem()
|
||||
if !r.setValue(newElem, item) {
|
||||
panic("rfx: failed to append item to slice")
|
||||
}
|
||||
newValues[i] = newElem
|
||||
}
|
||||
|
||||
target.Set(reflect.Append(target, newValues...))
|
||||
return r
|
||||
}
|
||||
|
||||
// setNestedValue 递归设置嵌套值,特殊处理 map 中的 struct
|
||||
func (r *rfx) setNestedValue(current reflect.Value, keys []string, v any) bool {
|
||||
// 解引用指针和接口
|
||||
@ -232,7 +268,36 @@ func (r *rfx) setFieldValue(target reflect.Value, key string, v any) bool {
|
||||
}
|
||||
target.SetMapIndex(reflect.ValueOf(key), val)
|
||||
return true
|
||||
case reflect.Slice, reflect.Array:
|
||||
case reflect.Slice:
|
||||
idx, err := strconv.Atoi(key)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// 对于切片,支持使用索引 -1 追加新元素(插入到切片前面)
|
||||
if idx == -1 {
|
||||
elemType := target.Type().Elem()
|
||||
newElem := reflect.New(elemType).Elem()
|
||||
if !r.setValue(newElem, v) {
|
||||
return false
|
||||
}
|
||||
// 将新元素放在前面,原有元素顺序后移
|
||||
newSlice := reflect.MakeSlice(target.Type(), 0, target.Len()+1)
|
||||
newSlice = reflect.Append(newSlice, newElem)
|
||||
newSlice = reflect.AppendSlice(newSlice, target)
|
||||
target.Set(newSlice)
|
||||
return true
|
||||
}
|
||||
|
||||
if idx < 0 || idx >= target.Len() {
|
||||
return false
|
||||
}
|
||||
elem := target.Index(idx)
|
||||
if !elem.CanSet() {
|
||||
return false
|
||||
}
|
||||
return r.setValue(elem, v)
|
||||
case reflect.Array:
|
||||
idx, err := strconv.Atoi(key)
|
||||
if err != nil || idx < 0 || idx >= target.Len() {
|
||||
return false
|
||||
|
||||
@ -57,11 +57,17 @@ func ExampleNew_withPointer_slice() {
|
||||
rfx.Set("0", "orange")
|
||||
rfx.Set("2", "grape")
|
||||
|
||||
// 使用 Append 追加元素
|
||||
rfx.Append("kiwi")
|
||||
|
||||
// 使用索引 -1 在切片前面插入新元素
|
||||
rfx.Set("-1", "pear")
|
||||
|
||||
// 原始 slice 已被修改
|
||||
fmt.Printf("Items: %v\n", items)
|
||||
|
||||
// Output:
|
||||
// Items: [orange banana grape]
|
||||
// Items: [pear orange banana grape kiwi]
|
||||
}
|
||||
|
||||
// ExampleNew_withValue 演示如何使用 New 函数传入值
|
||||
@ -158,19 +164,27 @@ func ExampleNew_withValue_slice() {
|
||||
rfx.Set("0", "orange")
|
||||
rfx.Set("2", "grape")
|
||||
|
||||
// 使用 Append 追加元素
|
||||
rfx.Append("kiwi")
|
||||
|
||||
// 使用索引 -1 在切片前面插入新元素
|
||||
rfx.Set("-1", "pear")
|
||||
|
||||
// 原始 slice 未被修改
|
||||
fmt.Printf("Original: %v\n", items)
|
||||
|
||||
// rfx 内部的数据已修改
|
||||
fmt.Printf("Clone: [%s %s %s]\n",
|
||||
fmt.Printf("Clone: [%s %s %s %s %s]\n",
|
||||
rfx.Get("0").String(),
|
||||
rfx.Get("1").String(),
|
||||
rfx.Get("2").String())
|
||||
rfx.Get("2").String(),
|
||||
rfx.Get("3").String(),
|
||||
rfx.Get("4").String())
|
||||
|
||||
// Output:
|
||||
// After New: [apple banana cherry]
|
||||
// Original: [apple banana cherry]
|
||||
// Clone: [orange banana grape]
|
||||
// Clone: [pear orange banana grape kiwi]
|
||||
}
|
||||
|
||||
// ExampleNew_withValue_nestedStruct 演示传入嵌套结构体值
|
||||
|
||||
Loading…
Reference in New Issue
Block a user