1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
# Data utilities
### Expandable Map & Collection
Expandable structure enable nested data structure to substitution source for other data structure or text.
Data substitution expression starts with $ sign, you can use path where dot or [index] allows access data sub node.
- [ExpandAsText](#ExpandAsText)
- [Expand](#Expand)
### ExpandAsText
ExpandAsText expands any expression that has satisfied dependencies, meaning only expression that path is present
can be expanded, otherwise expression is left unchanged.
In case when UDF is used, expression is expanded if UDF does not return an error.
**Usage:**
```go
aMap := Map(map[string]interface{}{
"key1": 1,
"key2": map[string]interface{}{
"subKey1":10,
"subKey2":20,
},
"key3": "subKey2",
"array": []interface{}{
111, 222, 333,
},
"slice": []interface{}{
map[string]interface{}{
"attr1":111,
"attr2":222,
},
},
})
expandedText := aMap.ExpandAsText(`1: $key1,
2: ${array[2]}
3: $key2.subKey1
4: $key2[$key3] ${slice[0].attr1}
5: ${(key1 + 1) * 3}
6: $abc
7: end
`)
/* expands to
1: 1,
2: 333
3: 10
4: 20 111
5: 6
6: $abc
7: end
*/
```
## Expand arbitrary data structure
```go
aMap := Map(map[string]interface{}{
"key1": 1,
"key2": map[string]interface{}{
"subKey1":10,
"subKey2":20,
},
"key3": "subKey2",
"array": []interface{}{
111, 222, 333,
},
"slice": []interface{}{
map[string]interface{}{
"attr1":111,
"attr2":222,
},
},
})
data := map[string]interface{}{
"k1": "$key1",
"k2":"$array",
"k3":"$key2",
}
expanded := aMap.Expand(data)
/* expands to
map[string]interface{}{
"k1": 1,
"k2": []interface{}{111, 222, 333},
"k3": map[string]interface{}{
"subKey1":10,
"subKey2":20,
},
}
*/
```
# UDF expandable User defined function
You can add dynamic data substitution by registering function in top level map.
```go
type Udf func(interface{}, Map) (interface{}, error)
```
```go
aMap: data.Map(map[string]interface{}{
"dailyCap": 100,
"overallCap": 2,
"AsFloat": func(source interface{}, state Map) (interface{}, error) {
return toolbox.AsFloat(source), nil
},
})
expanded := aMap.Expand("$AsFloat($dailyCap)")
//expands to actual float: 100.0
```
[Predefined UDF](udf)
### Compacted slice
Using a generic data structure in a form []map[string]interface{} is extremely memory inefficient,
CompactedSlice addresses the memory inefficiency by storing the new item values in a slice
and by mapping corresponding fields to the item slice index positions.
On top of that any neighboring nil values can be compacted too.
**Usage**
```go
collection := NewCompactedSlice(true, true)
for i := 0;i<10;i++ {
collection.Add(map[string]interface{}{
"f1": i+1,
"f12": i+10,
"f15": i*20,
"f20": i+4,
"f11": nil,
"f12": nil,
"f13": nil,
"f14": "",
})
}
collection.Range(func(data interface{}) (bool, error) {
actual = append(actual, toolbox.AsMap(data))
return true, nil
})
```
|