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。
|
**注意**: 如果设置失败(路径不存在、类型不匹配等),会 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)
|
### 5. 删除值 (Delete)
|
||||||
|
|
||||||
`Delete` 方法支持链式调用,返回 Reflux 自身:
|
`Delete` 方法支持链式调用,返回 Reflux 自身:
|
||||||
|
|||||||
16
reflux.go
16
reflux.go
@ -18,6 +18,11 @@ type R interface {
|
|||||||
// 示例: rfx.Set("name", "Alice").Set("age", 30)
|
// 示例: rfx.Set("name", "Alice").Set("age", 30)
|
||||||
Set(key string, v any) R
|
Set(key string, v any) R
|
||||||
|
|
||||||
|
// Append 追加指定路径的值
|
||||||
|
// 参数 items 为要追加的值
|
||||||
|
// 返回当前 R 实例以支持链式调用
|
||||||
|
Append(items ...any) R
|
||||||
|
|
||||||
// Delete 删除指定路径的值
|
// Delete 删除指定路径的值
|
||||||
// 参数 p 为路径片段
|
// 参数 p 为路径片段
|
||||||
// 返回当前 R 实例以支持链式调用
|
// 返回当前 R 实例以支持链式调用
|
||||||
@ -123,7 +128,7 @@ type R interface {
|
|||||||
// - 如果传入指针: 将直接使用该指针,可以修改原始数据
|
// - 如果传入指针: 将直接使用该指针,可以修改原始数据
|
||||||
// - 如果传入值: 会自动创建一个深度克隆的指针副本,修改不影响原始数据
|
// - 如果传入值: 会自动创建一个深度克隆的指针副本,修改不影响原始数据
|
||||||
// 支持的类型: map、struct、slice、array
|
// 支持的类型: map、struct、slice、array
|
||||||
// 也支持 interface 类型,会自动解析到实际类型
|
// 也支持 interface 类型以及部分基础类型(string/bool/float),会自动解析到实际类型
|
||||||
// 返回一个 R 接口实例,可用于访问和操作嵌套的字段、元素和键值对
|
// 返回一个 R 接口实例,可用于访问和操作嵌套的字段、元素和键值对
|
||||||
func New(v any) R {
|
func New(v any) R {
|
||||||
rv := reflect.ValueOf(v)
|
rv := reflect.ValueOf(v)
|
||||||
@ -150,7 +155,14 @@ func New(v any) R {
|
|||||||
|
|
||||||
// 检查最终的实际类型是否为支持的类型
|
// 检查最终的实际类型是否为支持的类型
|
||||||
switch actualValue.Kind() {
|
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:
|
default:
|
||||||
panic("rfx: unsupported type, only map, struct, slice, and array are allowed")
|
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
|
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
|
// setNestedValue 递归设置嵌套值,特殊处理 map 中的 struct
|
||||||
func (r *rfx) setNestedValue(current reflect.Value, keys []string, v any) bool {
|
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)
|
target.SetMapIndex(reflect.ValueOf(key), val)
|
||||||
return true
|
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)
|
idx, err := strconv.Atoi(key)
|
||||||
if err != nil || idx < 0 || idx >= target.Len() {
|
if err != nil || idx < 0 || idx >= target.Len() {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@ -57,11 +57,17 @@ func ExampleNew_withPointer_slice() {
|
|||||||
rfx.Set("0", "orange")
|
rfx.Set("0", "orange")
|
||||||
rfx.Set("2", "grape")
|
rfx.Set("2", "grape")
|
||||||
|
|
||||||
|
// 使用 Append 追加元素
|
||||||
|
rfx.Append("kiwi")
|
||||||
|
|
||||||
|
// 使用索引 -1 在切片前面插入新元素
|
||||||
|
rfx.Set("-1", "pear")
|
||||||
|
|
||||||
// 原始 slice 已被修改
|
// 原始 slice 已被修改
|
||||||
fmt.Printf("Items: %v\n", items)
|
fmt.Printf("Items: %v\n", items)
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// Items: [orange banana grape]
|
// Items: [pear orange banana grape kiwi]
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExampleNew_withValue 演示如何使用 New 函数传入值
|
// ExampleNew_withValue 演示如何使用 New 函数传入值
|
||||||
@ -158,19 +164,27 @@ func ExampleNew_withValue_slice() {
|
|||||||
rfx.Set("0", "orange")
|
rfx.Set("0", "orange")
|
||||||
rfx.Set("2", "grape")
|
rfx.Set("2", "grape")
|
||||||
|
|
||||||
|
// 使用 Append 追加元素
|
||||||
|
rfx.Append("kiwi")
|
||||||
|
|
||||||
|
// 使用索引 -1 在切片前面插入新元素
|
||||||
|
rfx.Set("-1", "pear")
|
||||||
|
|
||||||
// 原始 slice 未被修改
|
// 原始 slice 未被修改
|
||||||
fmt.Printf("Original: %v\n", items)
|
fmt.Printf("Original: %v\n", items)
|
||||||
|
|
||||||
// rfx 内部的数据已修改
|
// rfx 内部的数据已修改
|
||||||
fmt.Printf("Clone: [%s %s %s]\n",
|
fmt.Printf("Clone: [%s %s %s %s %s]\n",
|
||||||
rfx.Get("0").String(),
|
rfx.Get("0").String(),
|
||||||
rfx.Get("1").String(),
|
rfx.Get("1").String(),
|
||||||
rfx.Get("2").String())
|
rfx.Get("2").String(),
|
||||||
|
rfx.Get("3").String(),
|
||||||
|
rfx.Get("4").String())
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// After New: [apple banana cherry]
|
// After New: [apple banana cherry]
|
||||||
// Original: [apple banana cherry]
|
// Original: [apple banana cherry]
|
||||||
// Clone: [orange banana grape]
|
// Clone: [pear orange banana grape kiwi]
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExampleNew_withValue_nestedStruct 演示传入嵌套结构体值
|
// ExampleNew_withValue_nestedStruct 演示传入嵌套结构体值
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user