• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/docs/tutorials/03-objects.md

  1 The [manual chapter on objects](https://ucode-lang.org/manual/) covers the
  2 value type, key handling and every builtin in detail. The examples below
  3 collect recipes for frequently needed operations that are not part of the
  4 core library.
  5 
  6 ## Merging
  7 
  8 Spread expressions merge objects shallowly, later keys winning:
  9 
 10 ```ucode
 11 let defaults = { theme: "light", fontSize: 12, notifications: true };
 12 let user = { theme: "dark" };
 13 
 14 let merged = { ...defaults, ...user };
 15 
 16 print(merged, "\n");
 17 // { "theme": "dark", "fontSize": 12, "notifications": true }
 18 ```
 19 
 20 For nested objects a recursive version is needed; note the forward
 21 declaration, as ucode does not hoist function declarations:
 22 
 23 ```ucode
 24 function deepMergeArray;
 25 
 26 function deepMerge(target, ...sources) {
 27     for (let source in sources) {
 28         if (type(source) != "object")
 29             continue;
 30 
 31         for (let key in keys(source)) {
 32             if (type(source[key]) == "object" && type(target[key]) == "object")
 33                 target[key] = deepMerge({ ...target[key] }, source[key]);
 34             else if (type(source[key]) == "array" && type(target[key]) == "array")
 35                 target[key] = deepMergeArray(target[key], source[key]);
 36             else
 37                 target[key] = source[key];
 38         }
 39     }
 40 
 41     return target;
 42 }
 43 
 44 function deepMergeArray(target, source) {
 45     for (let i = 0; i < length(source); i++) {
 46         if (type(source[i]) == "object" && type(target[i]) == "object")
 47             target[i] = deepMerge({ ...target[i] }, source[i]);
 48         else if (type(source[i]) == "array" && type(target[i]) == "array")
 49             target[i] = deepMergeArray(target[i], source[i]);
 50         else
 51             target[i] = source[i];
 52     }
 53 
 54     return target;
 55 }
 56 
 57 let profile = {
 58     name: "Alice",
 59     preferences: { theme: "light", sidebar: { visible: true, width: 250 } }
 60 };
 61 
 62 let updates = { preferences: { theme: "dark", sidebar: { width: 300 } } };
 63 
 64 deepMerge({}, profile, updates);
 65 /*
 66 {
 67     "name": "Alice",
 68     "preferences": {
 69         "theme": "dark",
 70         "sidebar": { "visible": true, "width": 300 }
 71     }
 72 }
 73 */
 74 ```
 75 
 76 ## Filtering and mapping
 77 
 78 ```ucode
 79 function filterObject(obj, filterFn) {
 80     let result = {};
 81 
 82     for (let key in keys(obj))
 83         if (filterFn(key, obj[key]))
 84             result[key] = obj[key];
 85 
 86     return result;
 87 }
 88 
 89 let mixed = { a: 1, b: "string", c: 3, d: true, e: 4.5 };
 90 
 91 filterObject(mixed, (key, value) =>
 92     type(value) == "int" || type(value) == "double");
 93 // { "a": 1, "c": 3, "e": 4.5 }
 94 ```
 95 
 96 ```ucode
 97 function mapObject(obj, mapFn) {
 98     let result = {};
 99 
100     for (let key in keys(obj))
101         result[key] = mapFn(key, obj[key]);
102 
103     return result;
104 }
105 
106 mapObject({ apple: 1.25, banana: 0.75, cherry: 2.50 },
107           (fruit, price) => price * 0.8);
108 // { "apple": 1, "banana": 0.6, "cherry": 2 }
109 ```
110 
111 ## Caches and lookup tables
112 
113 Objects make natural memoization caches, captured by closure:
114 
115 ```ucode
116 let fibonacci = (function () {
117     let cache = {};
118 
119     return function fib(n) {
120         if (exists(cache, n))
121             return cache[n];
122 
123         let result = (n <= 1) ? n : fib(n - 1) + fib(n - 2);
124         cache[n] = result;
125         return result;
126     };
127 })();
128 
129 fibonacci(40);   // 102334155
130 ```
131 
132 Lookup tables replace chains of conditionals, with `??` supplying the
133 fallback:
134 
135 ```ucode
136 let statusMessages = { "200": "OK", "404": "Not Found", "500": "Server Error" };
137 
138 let getStatus = (code) => statusMessages[code] ?? "Unknown";
139 
140 getStatus(404);   // "Not Found"
141 getStatus(999);   // "Unknown"
142 ```
143 
144 ## Deep cloning
145 
146 A deep clone re-uses the array and object halves from
147 [the arrays page](tutorial-02-arrays.html); the object half, adapted to start
148 from an object:
149 
150 ```ucode
151 function deepCloneArray;
152 
153 function deepClone(obj) {
154     if (type(obj) != "object")
155         return obj;
156 
157     let clone = {};
158 
159     for (let key in keys(obj)) {
160         if (type(obj[key]) == "object")
161             clone[key] = deepClone(obj[key]);
162         else if (type(obj[key]) == "array")
163             clone[key] = deepCloneArray(obj[key]);
164         else
165             clone[key] = obj[key];
166     }
167 
168     return clone;
169 }
170 ```
171 
172 ## Comparing by value
173 
174 Equality operators test reference identity; structural comparison is a
175 recipe, again forward-declared to break the mutual recursion:
176 
177 ```ucode
178 function arrayEquals;
179 
180 function objectEquals(obj1, obj2) {
181     if (type(obj1) != "object" || type(obj2) != "object")
182         return obj1 === obj2;
183 
184     let keys1 = keys(obj1);
185     let keys2 = keys(obj2);
186 
187     if (length(keys1) != length(keys2))
188         return false;
189 
190     for (let key in keys1) {
191         if (!exists(obj2, key))
192             return false;
193 
194         if (type(obj1[key]) == "object" && type(obj2[key]) == "object") {
195             if (!objectEquals(obj1[key], obj2[key]))
196                 return false;
197         } else if (type(obj1[key]) == "array" && type(obj2[key]) == "array") {
198             if (!arrayEquals(obj1[key], obj2[key]))
199                 return false;
200         } else if (obj1[key] !== obj2[key]) {
201             return false;
202         }
203     }
204 
205     return true;
206 }
207 
208 function arrayEquals(arr1, arr2) {
209     if (length(arr1) != length(arr2))
210         return false;
211 
212     for (let i = 0; i < length(arr1); i++) {
213         if (type(arr1[i]) == "object" && type(arr2[i]) == "object") {
214             if (!objectEquals(arr1[i], arr2[i]))
215                 return false;
216         } else if (type(arr1[i]) == "array" && type(arr2[i]) == "array") {
217             if (!arrayEquals(arr1[i], arr2[i]))
218                 return false;
219         } else if (arr1[i] !== arr2[i]) {
220             return false;
221         }
222     }
223 
224     return true;
225 }
226 ```
227 
228 ## Iterating and sorting
229 
230 The two-variable `for ... in` form destructures key and value:
231 
232 ```ucode
233 let inventory = { apples: 50, oranges: 25, bananas: 30 };
234 
235 for (let fruit, quantity in inventory)
236     printf("We have %d %s in stock\n", quantity, fruit);
237 ```
238 
239 Objects keep their keys in insertion order, and `sort()` reorders them in
240 place; the compare function receives both keys and both values:
241 
242 ```ucode
243 let stock = { apples: 45, bananas: 25, oranges: 30, grapes: 60 };
244 
245 sort(stock, (k1, k2, v1, v2) => v2 - v1);
246 
247 for (let fruit, quantity in stock)
248     printf("%s: %d\n", fruit, quantity);
249 // grapes: 60
250 // apples: 45
251 // oranges: 30
252 // bananas: 25
253 ```

This page was automatically generated by LXR 0.3.1.  •  OpenWrt