github.com/coveo/gotemplate@v2.7.7+incompatible/collections/implementation/dict_helper.go (about) 1 package implementation 2 3 import ( 4 "fmt" 5 6 "github.com/coveo/gotemplate/collections" 7 ) 8 9 func (d baseDict) String() string { 10 // Unlike go maps, we render dictionary keys in order 11 keys := d.KeysAsString() 12 for i, k := range keys { 13 keys[i] = str(fmt.Sprintf("%s:%v", k, d.Get(k))) 14 } 15 return fmt.Sprintf("dict[%s]", keys.Join(" ")) 16 } 17 18 func (d baseDict) PrettyPrint() string { return d.String() } 19 20 // DictHelper implements basic functionalities required for IDictionary. 21 type DictHelper struct { 22 BaseHelper 23 } 24 25 // AsDictionary returns the object casted as IDictionary. 26 func (dh DictHelper) AsDictionary(object interface{}) baseIDict { 27 return must(dh.TryAsDictionary(object)).(baseIDict) 28 } 29 30 // Clone returns a distinct copy of the object with only supplied keys. If no keys are supplied, all keys from d are copied. 31 func (dh DictHelper) Clone(dict baseIDict, keys []interface{}) baseIDict { 32 if len(keys) == 0 { 33 keys = dict.GetKeys().AsArray() 34 } 35 newDict := dh.CreateDictionary(dict.Len()) 36 for i := range keys { 37 value := dict.Get(keys[i]) 38 if value != nil { 39 if v, err := dh.TryAsDictionary(value); err == nil { 40 value = dh.Clone(v, nil) 41 } else if v, err := dh.TryAsList(value); err == nil { 42 value = dh.ConvertList(v.Clone()) 43 } 44 } 45 newDict.Set(keys[i], value) 46 } 47 return newDict 48 } 49 50 // Default returns defVal if dictionary doesn't contain key, otherwise, simply returns entry corresponding to key. 51 func (dh DictHelper) Default(dict baseIDict, key, defVal interface{}) interface{} { 52 if !dict.Has(key) { 53 return defVal 54 } 55 return dict.Get(key) 56 } 57 58 // Delete removes the entry value associated with key. The entry must exist. 59 func (dh DictHelper) Delete(dict baseIDict, keys []interface{}) (baseIDict, error) { 60 return dh.delete(dict, keys, true) 61 } 62 63 // Flush removes all specified keys from the dictionary. If no key is specified, all keys are removed. 64 func (dh DictHelper) Flush(dict baseIDict, keys []interface{}) baseIDict { 65 if len(keys) == 0 { 66 keys = dict.GetKeys().AsArray() 67 } 68 dh.delete(dict, keys, false) 69 return dict 70 } 71 72 // Get returns the value associated with key. 73 func (dh DictHelper) Get(dict baseIDict, keys []interface{}) interface{} { 74 switch len(keys) { 75 case 0: 76 return nil 77 case 1: 78 return dict.AsMap()[fmt.Sprint(keys[0])] 79 } 80 result := dict.CreateList(len(keys)) 81 for i := range result.AsArray() { 82 result.Set(i, dict.Get(keys[i])) 83 } 84 return result 85 } 86 87 // Has returns true if the dictionary object contains all the keys. 88 func (dh DictHelper) Has(dict baseIDict, keys []interface{}) bool { 89 for _, key := range keys { 90 if _, ok := dict.AsMap()[fmt.Sprint(key)]; !ok { 91 return false 92 } 93 } 94 return dict.Len() > 0 95 } 96 97 // GetKeys returns the keys in the dictionary in alphabetical order. 98 func (dh DictHelper) GetKeys(dict baseIDict) baseIList { 99 keys := dict.KeysAsString() 100 result := dh.CreateList(dict.Len()) 101 102 for i := range keys { 103 result.Set(i, keys[i]) 104 } 105 return result 106 } 107 108 // KeysAsString returns the keys in the dictionary in alphabetical order. 109 func (dh DictHelper) KeysAsString(dict baseIDict) collections.StringArray { 110 keys := make(collections.StringArray, 0, dict.Len()) 111 for key := range dict.AsMap() { 112 keys = append(keys, str(key)) 113 } 114 return keys.Sorted() 115 } 116 117 // Merge merges the other dictionaries into the current dictionary. 118 func (dh DictHelper) Merge(target baseIDict, sources []baseIDict) baseIDict { 119 for i := range sources { 120 if sources[i] == nil { 121 continue 122 } 123 target = dh.deepMerge(target, dh.ConvertDict(sources[i])) 124 } 125 return target 126 } 127 128 func (dh DictHelper) deepMerge(target baseIDict, source baseIDict) baseIDict { 129 targetMap := target.AsMap() 130 sourceMap := source.AsMap() 131 for key := range sourceMap { 132 sourceValue, sourceHasKey := sourceMap[key] 133 targetValue, targetHasKey := targetMap[key] 134 135 if sourceHasKey && !targetHasKey { 136 targetMap[key] = sourceValue 137 continue 138 } 139 140 targetValueDict, targetValueIsDict := targetValue.(baseIDict) 141 sourceValueDict, sourceValueIsDict := sourceValue.(baseIDict) 142 143 if sourceValueIsDict && targetValueIsDict { 144 targetMap[key] = dh.deepMerge(targetValueDict, sourceValueDict) 145 } 146 } 147 148 return target 149 } 150 151 // Omit returns a distinct copy of the object including all keys except specified ones. 152 func (dh DictHelper) Omit(dict baseIDict, keys []interface{}) baseIDict { 153 omitKeys := make(map[string]bool, len(keys)) 154 for i := range keys { 155 omitKeys[fmt.Sprint(keys[i])] = true 156 } 157 keep := make([]interface{}, 0, dict.Len()) 158 for key := range dict.AsMap() { 159 if !omitKeys[key] { 160 keep = append(keep, key) 161 } 162 } 163 return dh.Clone(dict, keep) 164 } 165 166 // Pop returns and remove the objects with the specified keys. 167 func (dh DictHelper) Pop(dict baseIDict, keys []interface{}) interface{} { 168 if len(keys) == 0 { 169 return nil 170 } 171 result := dh.Get(dict, keys) 172 dh.delete(dict, keys, false) 173 return result 174 } 175 176 // Set sets key to value in the dictionary. 177 func (dh DictHelper) Set(dict baseIDict, key interface{}, value interface{}) baseIDict { 178 if dict.AsMap() == nil { 179 dict = dh.CreateDictionary() 180 } 181 dict.AsMap()[fmt.Sprint(key)] = dh.Convert(value) 182 return dict 183 } 184 185 // Add adds value to an existing key instead of replacing the value as done by set. 186 func (dh DictHelper) Add(dict baseIDict, key interface{}, value interface{}) baseIDict { 187 if dict.AsMap() == nil { 188 dict = dh.CreateDictionary() 189 } 190 m := dict.AsMap() 191 k := fmt.Sprint(key) 192 193 if current, ok := m[k]; ok { 194 if list, err := collections.TryAsList(current); err == nil { 195 m[k] = list.Append(value) 196 } else { 197 // Convert the current value into a list 198 m[k] = dict.CreateList().Append(current, value) 199 } 200 } else { 201 m[k] = dh.Convert(value) 202 } 203 return dict 204 } 205 206 // GetValues returns the values in the dictionary in key alphabetical order. 207 func (dh DictHelper) GetValues(dict baseIDict) baseIList { 208 result := dh.CreateList(dict.Len()) 209 for i, key := range dict.KeysAsString() { 210 result.Set(i, dict.Get(key)) 211 } 212 return result 213 } 214 215 // Transpose returns a dictionary with values as keys and keys as values. The resulting dictionary 216 // is a dictionary where each key could contains single value or list of values if there are multiple matches. 217 func (dh DictHelper) Transpose(dict baseIDict) baseIDict { 218 result := dh.CreateDictionary() 219 for _, key := range dict.GetKeys().AsArray() { 220 value := dict.Get(key) 221 if list, err := collections.TryAsList(value); err == nil { 222 // If the element is a list, we scan each element 223 for _, value := range list.AsArray() { 224 result.Add(value, key) 225 } 226 } else { 227 result.Add(value, key) 228 } 229 } 230 return result 231 } 232 233 func (dh DictHelper) delete(dict baseIDict, keys []interface{}, mustExist bool) (baseIDict, error) { 234 for i := range keys { 235 if mustExist && !dict.Has(keys[i]) { 236 return dict, fmt.Errorf("key %v not found", keys[i]) 237 } 238 delete(dict.AsMap(), fmt.Sprint(keys[i])) 239 } 240 return dict, nil 241 } 242 243 // Register the default implementation of dictionary helper 244 var _ = func() int { 245 collections.DictionaryHelper = baseDictHelper 246 return 0 247 }()