1'use strict'; 2 3/** 4 * Gbg Object manipulation Utility 5 * Static calls only 6 */ 7 8gbg.H = {}; 9 10/** 11 * Search safely for a property in an object 12 * 13 * ### Example 14 * left obj = { 15 * attr1: 'firstVal', 16 * attr2: 'secondVal', 17 * attrObj: { 18 * subAttr: 'subVal' 19 * } 20 * }; 21 * gbg.H.get(obj, 'attr1', false) returns 'firstVal' 22 * gbg.H.get(obj, 'attr1.x', false) returns false 23 * gbg.H.get(obj, 'attrObj.subAttr', false) returns 'subVal' 24 * 25 * @param {{}||[]} objectOrArray 26 * @param {string} path 27 * @param {any} defaultVal 28 * 29 * @return {any} 30 */ 31gbg.H.get = function (objectOrArray, path, defaultVal = null) { 32 33 if (typeof objectOrArray === 'undefined' || objectOrArray === null || 34 (typeof objectOrArray !== 'object' && !Array.isArray(objectOrArray))) { 35 return defaultVal; 36 } 37 38 if (typeof objectOrArray[path] !== 'undefined' && objectOrArray[path] !== null) { 39 return objectOrArray[path]; 40 } 41 42 let properties = path.split('.'), 43 result = objectOrArray; 44 45 for (let i = 0; i < properties.length; i++) { 46 if (typeof result[properties[i]] === 'undefined' || result[properties[i]] === null || result[properties[i]] === 'null') { 47 return defaultVal; 48 } else { 49 result = result[properties[i]]; 50 } 51 } 52 53 return result; 54 55}; 56 57/** 58 * Find the first defined and not null attribute of an object 59 * property can be a path. 60 * 61 * ### Example : 62 * 63 * let object = {code: null, num: 1}; 64 * gbg.H.getFirst(object, ['code', 'id', 'num'], null) 65 * will return 1 66 * 67 * @param objectOrArray 68 * @param paths 69 * @param defaultVal 70 * 71 * @return {*} 72 */ 73gbg.H.getFirst = function (objectOrArray, paths, defaultVal) { 74 75 if (gbg.isEmpty(paths)) { 76 return defaultVal; 77 } 78 79 for (let i = 0; i < paths.length; i++) { 80 let val = gbg.H.get(objectOrArray, paths[i], undefined); 81 if (val !== undefined) { 82 return val; 83 } 84 } 85 return defaultVal; 86 87}; 88 89gbg.H.set = function (objectOrArray, path, value) { 90 91 if (typeof objectOrArray === 'undefined' || !objectOrArray || ['object', 'array'].indexOf(typeof objectOrArray) === -1) { 92 return; 93 } 94 95 path = path.split('.'); 96 97 let recursiveSet = function(currentObjectOrArray, path, value) { 98 // console.log(path); 99 if (path.length === 1) { 100 currentObjectOrArray[path] = value; 101 // console.log(currentObjectOrArray[path]); 102 return currentObjectOrArray; 103 } else { 104 let firstPathPart = path.shift(); 105 // console.log(firstPathPart, path); 106 if (gbg.H.get(currentObjectOrArray, firstPathPart, null) === null) { 107 currentObjectOrArray[firstPathPart] = {}; 108 } 109 currentObjectOrArray[firstPathPart] = recursiveSet(currentObjectOrArray[firstPathPart], path, value); 110 return currentObjectOrArray; 111 } 112 }; 113 114 return recursiveSet(objectOrArray, path, value); 115}; 116 117/** 118 * Flattens a nested array to a flat array. 119 * 120 * For example, flattens an array that was expanded with gbg.H.::unflatten() into a flat array. 121 * ### Example 122 * [['Super' => ['Man' => 'IsTheBest']]] becomes ['0.Super.Man' => 'IsTheBest'] 123 * [['Super' => ['Woman' => 'IsGoat']]] becomes ['0.Super.Woman' => 'IsGoat'] 124 * 125 * @param {[]|{}} $thingy 126 * @param {[]} options 127 * 128 * @return {{}} 129 * 130 */ 131 132gbg.H.flatten = function (objectOrArray, options) { 133 134 if (typeof objectOrArray === 'undefined' || !objectOrArray || ['object', 'array'].indexOf(typeof objectOrArray) === -1) { 135 return; 136 } 137 138 options = gbg.extend(options ?? {separator: '.', currentKey: ''}, {separator: '.', currentKey: ''}); 139 140 let result = {}, 141 stack = [], 142 path = null, 143 //keys = Object.keys(objectOrArray), 144 attributes = gbg.H.clone(objectOrArray), 145 tmpSeparator = '$$$$$'; 146 147 while (!gbg.isEmpty(attributes)) { 148 let key = Object.keys(attributes).shift(), 149 element = attributes[key]; 150 151 //console.log(attributes, key) 152 delete attributes[key]; 153 154 // if value is a hierarchical thingy, then process it now, 155 // and register $attributes to continue with it later 156 157 if (!gbg.isEmpty(element) && typeof element === 'object') { 158 if (!gbg.isEmpty(attributes)) { 159 stack.push([gbg.H.clone(attributes), path]); 160 } 161 attributes = gbg.H.clone(element); 162 //path = (path ? path : '') + key + tmpSeparator; 163 path = (path ? path : '') + key + options.separator; 164 } else { 165 result[(path ? path : '') + key] = element; 166 } 167 168 if (gbg.isEmpty(attributes) && !gbg.isEmpty(stack)) { 169 let stackItem = stack.pop(); 170 attributes = stackItem[0]; 171 path = stackItem[1]; 172 } 173 } 174 175 return result; 176 177 // if (options.separator.length === 2) { 178 // options.separator = [options.separator[0], options.separator[1]]; 179 // } else { 180 // options.separator = [options.separator[0], '']; 181 // } 182 183 // let newResult = Array.isArray(objectOrArray) ? [] : {}; 184 // 185 // console.log(result) 186 // 187 // for (let key in result) { 188 // let newKey = key; 189 // if (key.indexOf(tmpSeparator) > -1) { 190 // let keyParts = key.split(tmpSeparator); 191 // console.log(keyParts) 192 // for (let i in keyParts) { 193 // if (i === 0) { 194 // newKey = keyParts[i];
195 // continue; 196 // } 197 // newKey += options.separator[0] + keyParts[i] + options.separator[1]; 198 // } 199 // newResult[newKey] = result[key]; 200 // } else { 201 // newResult[key] = result[key]; 202 // } 203 // } 204 // console.log(result) 205 206 // return newResult; 207 208}; 209 210/** 211 * Clone an objectOrArray 212 * @param {[]|{}} objectOrArray 213 * @return {[]|{}} 214 */ 215gbg.H.clone = function (objectOrArray) { 216 return Object.assign({}, objectOrArray); 217 // 218 // if (Array.isArray(objectOrArray)) { 219 // return Array.from(objectOrArray); 220 // } 221 // if (typeof objectOrArray === 'object') { 222 // return Object.assign({}, objectOrArray); 223 // } 224 // return objectOrArray; 225} 226 227/** 228 * Format an object or an array, for example for a select2 229 * 230 * ### Examples : 231 * 232 * gbg.H.format( 233 * [ {id: 1, name: 'My name1', 'prop': 'MyProp1'}, {id: 2, name: 'My name2', 'prop': 'MyProp2'} ], 234 * '{id}', 235 * '{name} - {prop}', 236 * ) 237 * returns : 238 * { 239 * 1: 'My name1 - MyProp1', 240 * 2: 'My name2 - MyProp2', 241 * } 242 * 243 * gbg.H.format( 244 * [ {id: 1, name: 'My name1', 'prop': 'MyProp1'}, {id: 2, name: 'My name2', 'prop': 'MyProp2'} ], 245 * null, 246 * {id: '{id}', text: '{name} - {prop}}' }, 247 * ) 248 * returns : 249 * [ 250 * { 251 * id: 1, 252 * text: 'My name1 - MyProp1' 253 * }, 254 * { 255 * id: 2, 256 * text: 'My name2 - MyProp2' 257 * } 258 * ] 259 * 260 * 261 * @param objectOrArray 262 * @param keyFormula 263 * @param valueMap 264 */ 265gbg.H.format = function (objectOrArray, keyFormula, valueMap) { 266 267 let result = []; 268 269 if (gbg.isEmpty(objectOrArray)) { 270 return []; 271 } 272 273 if (gbg.isEmpty(keyFormula)) { 274 keyFormula = null; 275 } 276 277 for (let k in objectOrArray) { 278 279 let itemV = [], 280 itemK = null; 281 282 if (keyFormula) { 283 itemK = gbg.H.resolveFormula(objectOrArray[k], keyFormula, k); 284 } 285 286 if (!itemK) { 287 itemK = result.length; 288 } 289 290 if (valueMap) { 291 if (typeof valueMap === 'object') { 292 for (let valueFieldK in valueMap) { 293 itemV[valueFieldK] = gbg.H.resolveFormula(objectOrArray[k], valueMap[valueFieldK], valueFieldK); 294 } 295 } else { 296 itemV = gbg.H.resolveFormula(objectOrArray[k], valueMap, k); 297 } 298 } 299 300 if (itemV) { 301 result[itemK] = itemV; 302 } 303 304 } 305 306 return result; 307 308} 309 310/** 311 * Group an objectOrArray 312 * @param {[]|{}} objectOrArray 313 * @param {string} groupByFormula 314 * @param {[]|string} groupMap 315 * @return {[]|{}} 316 */ 317gbg.H.group = function (objectOrArray, groupByFormula, groupMap) { 318 319 320 let result = []; 321 322 if (gbg.isEmpty(objectOrArray) || gbg.isEmpty(groupByFormula)) { 323 return result; 324 } 325 326 if (gbg.isEmpty(groupMap)) { 327 groupMap = null; 328 } 329 330 for (let k in objectOrArray) { 331 332 let itemGroupVal = gbg.H.resolveFormula(objectOrArray[k], groupByFormula, k), 333 itemGroupK = itemGroupVal; 334 335 if (!gbg.isSet(result[itemGroupK])) { 336 result[itemGroupK] = []; 337 } 338 339 let resultItem = {}; 340 341 if (!groupMap) { 342 343 result[itemGroupK].push(objectOrArray[k]); 344 345 } else { 346 347 if (typeof groupMap === 'object') { 348 349 if (groupMap.hasOwnProperty('children') && groupMap['children']) { 350 if (gbg.isEmpty(result[itemGroupK]['children'])) { 351 result[itemGroupK]['children'] = []; 352 } 353 let newChild = null; 354 if (groupMap['children'] === true || groupMap['children'] === 1) { 355 newChild = objectOrArray[k]; 356 } else if (typeof groupMap['children'] === 'object') { 357 newChild = {}; 358 for (let groupChildK in groupMap['children']) { 359 newChild[groupChildK] = gbg.H.resolveFormula(objectOrArray[k], groupMap['children'][groupChildK], k); 360 } 361 } else { 362 // only a string formula 363 newChild = gbg.H.resolveFormula(objectOrArray[k], groupMap['children'], k); 364 } 365 result[itemGroupK]['children'].push(newChild); 366 } 367 368 for (let groupK in groupMap) { 369 if (groupK !== 'children' && groupK !== '_key') { 370 if (typeof groupMap[groupK] === 'undefined' || groupMap[groupK] === null) { 371 result[itemGroupK][groupK] = groupMap[groupK]; 372 } else { 373 result[itemGroupK][groupK] = gbg.H.resolveFormula(objectOrArray[k], groupMap[groupK], k); 374 } 375 } 376 } 377 } else { 378 result[itemGroupK] = gbg.H.resolveFormula(objectOrArray[k], groupMap, k); 379 } 380 } 381 382 } 383 384 if (gbg.H.get(groupMap, '_key', null) === '{{n}}') { 385 return Object.values(result); 386 } else { 387 return result; 388 } 389 390 391}; 392 393/** 394 * Resolve a formula 395 * @param {[]|{}} objectOrArray 396 * @param {string} formula 397 * @param {[]} key 398 * @return {[]|{}} 399 */ 400gbg.H.resolveFormula = function (objectOrArray, formula, key) { 401 402 let placeholders = gbg.S.new(formula).getBetweenAll('{', '}'); 403 let result = formula; 404 405 // to avoid that integer could be casted to string 406 if (formula === '{{key}}') { 407 return key; 408 } 409 410 if (!placeholders.length) { 411 result = gbg.H.get(objectOrArray, formula); 412 } else { 413 for (let placeholder of placeholders) { 414 let value = null; 415 if (placeholder === '{key}') { 416 value = key; 417 } else if (placeholder === '{val}') { 418 value = objectOrArray; 419 } else { 420 value = gbg.H.get(objectOrArray, placeholder, key); 421 } 422 result = result.replace('{' + placeholder + '}', value); 423 } 424 } 425 426 return result; 427} 428 429/** 430 * Sort an objectOrArray from its keys or values 431 * @param {[]|{}} objectOrArray 432 * @param {string} sortFormula 433 * @param {[]} options 434 * @return {[]|{}} 435 */ 436gbg.H.sort = function (objectOrArray, sortFormula = { depth0: ['{{key}}', 'ASC'] }, options = {}) { 437 438 if (gbg.isEmpty(objectOrArray)) { 439 return objectOrArray; 440 } 441 442 options = gbg.extend(options, { currentDepth: 0 }); 443 444 if (gbg.isEmpty(sortFormula['depth' + options.currentDepth])) { 445 return objectOrArray; 446 } 447
448 let currentSortFormula = sortFormula['depth' + options.currentDepth]; 449 if (typeof currentSortFormula === 'string') { 450 currentSortFormula = [currentSortFormula, 'ASC']; 451 } 452 453 let sortTemp = [], 454 result = [], 455 areKeysIntegers = true; 456 457 for (let k in objectOrArray) { 458 459 if (!gbg.isInteger(k)) { 460 areKeysIntegers = false; 461 } 462 463 let objectOrArrayItem = objectOrArray[k]; 464 let temp = { key: k, value: objectOrArrayItem, sortVal: 0 }; 465 if (typeof objectOrArrayItem === 'object') { 466 temp.value = gbg.H.sort(objectOrArrayItem, sortFormula, { currentDepth: options.currentDepth + 1 }); 467 } 468 469 temp.sortVal = gbg.H.resolveFormula(objectOrArrayItem, currentSortFormula[0], k); 470 if (gbg.isNumeric(temp.sortVal)) { 471 temp.sortVal = parseFloat(temp.sortVal); 472 } 473 sortTemp.push(temp); 474 } 475 476 let sortTemp2 = 477 Object.entries(sortTemp).sort( 478 function (a, b) { 479 if (a[1].sortVal === b[1].sortVal) { 480 return 0; 481 } 482 let res = (a[1].sortVal > b[1].sortVal) ? 1 : -1; 483 return (currentSortFormula[1].toLowerCase() === 'desc') ? -res : res; 484 } 485 ); 486 487 result = (areKeysIntegers ? [] : {}); 488 489 for (let k in sortTemp2) { 490 if (areKeysIntegers) { 491 result.push(sortTemp2[k][1].value); 492 } else { 493 result['' + String(sortTemp2[k][1].key)] = sortTemp2[k][1].value; 494 } 495 } 496 497 return result; 498 499} 500 501/** 502 * Remove duplicates in it's values for a variable 503 * @param {[]|{}} objectOrArray 504 * @return {[]|{}} 505 */ 506gbg.H.unique = function (objectOrArray) { 507 let valuesTmp = [], 508 values = []; 509 for (let key in objectOrArray) { 510 if (valuesTmp.indexOf(objectOrArray[key]) === -1) { 511 values[key] = objectOrArray[key]; 512 } 513 valuesTmp[key] = objectOrArray[key]; 514 } 515 return values; 516}; 517 518 519gbg.H.filterEmpty = function (objectOrArray) { 520 let type = Array.isArray(objectOrArray) ? 'array' : 'object', 521 sequential = gbg.H.arrayIsSequential(objectOrArray), 522 values = (type === 'array' ? [] : {}); 523 524 if (gbg.isEmpty(objectOrArray)) { 525 return objectOrArray; 526 } 527 for (let key in objectOrArray) { 528 if (!gbg.isEmpty(objectOrArray[key])) { 529 if (type === 'array' && sequential) { 530 values.push(objectOrArray[key]); 531 } else { 532 values[key] = objectOrArray[key]; 533 } 534 } 535 } 536 return values; 537}; 538 539/** 540 * Detect if an array is sequencial (it's indexes are 0, 1, 2, 3, 4, 5, ....) 541 * @param {[]} array 542 * @return {boolean} 543 */ 544gbg.H.arrayIsSequential = function (array) { 545 let i = 0; 546 if (!Array.isArray(array)) { 547 return undefined; 548 } 549 for (let key in array) { 550 if (String(key) !== String(i++)) { 551 return false; 552 } 553 } 554 return true; 555} 556 557/** 558 * If variable is not an array, then wrap it into an array 559 * @param {any} variable 560 */ 561gbg.H.ensureArray = function (variable) { 562 if (typeof variable === 'undefined' || !variable) { return []; } 563 if (!Array.isArray(variable)) { return [variable]; } 564 return variable; 565}; 566 567/*gbg.H.groupBy = function(objectOrArray, groupByFormula, keyFormula = '{{key}}', valueFormula = '{{val}}') { 568 569 let result = []; 570 571 if (gbg.isEmpty(objectOrArray)) { 572 return result; 573 } 574 575 for (let k in objectOrArray) { 576 577 let itemV = [], 578 itemK = null; 579 580 let groupByValue = gbg.H.resolveFormula(objectOrArray[k], groupByFormula, k); 581 582 if (!gbg.isSet(result[groupByValue])) { 583 result[groupByValue] = []; 584 } 585 586 itemK = gbg.H.resolveFormula(objectOrArray[k], keyFormula, k); 587 588 if (!itemK) { 589 itemK = result.length; 590 } 591 592 if (typeof valueField == 'object') { 593 for (let valueFieldK in valueField) { 594 itemV[valueFieldK] = gbg.H.resolveFormula(objectOrArray[k], valueField[valueFieldK], null); 595 } 596 } else { 597 itemV = gbg.H.resolveFormula(objectOrArray[k], valueField, null); 598 } 599 600 if (itemV) { 601 result[itemK] = itemV; 602 } 603 604 } 605 606 return result; 607 608}*/ 609 610/* 611let testObj = { 612 x: { 613 y: { 614 z: 'myResult' 615 } 616 } 617}; 618 619let testH = [ 620 {func: 'get', src: testObj, arg: 'x.y.z', res: 'myResult' }, 621 {func: 'get', src: testObj, arg: 'x.y.y', res: undefined }, 622]; 623 624console.log('gbg.H tests ----------------------'); 625 626var i = 0; 627for (let test of testH) { 628 i++; 629 let res = gbg.TESTS._test(gbg.H, test); 630 if (JSON.stringify(res) === JSON.stringify(test.res)) { 631 console.log('test #' + i + ' : ok'); 632 } else { 633 console.log('test #' + i + ' : nok --- expected : ', JSON.stringify(test.res), ' | actual : ', JSON.stringify(res)); 634 } 635} 636 637console.log( 638 gbg.H.format( [ {id: 1, name: 'My name1', 'prop': 'MyProp1'}, {id: 2, name: 'My name2', 'prop': 'MyProp2'} ], 639 '{id}', 640 '{name} - {prop}' 641 ) 642); 643console.log( 644 gbg.H.format( [ {id: 1, name: 'My name1', 'prop': 'MyProp1'}, {id: 2, name: 'My name2', 'prop': 'MyProp2'} ], 645 null, 646 { id: '{id}', text: '{name} - {prop}' } 647 ) 648); 649 650const maxSpeed = { 651 car: 300, 652 bike: 60, 653 motorbike: 200, 654 airplane: 1000, 655 helicopter: 400, 656 rocket: 8 * 60 * 60 657}; 658 659let sortable = Object.entries(maxSpeed) 660 .sort(([,a],[,b]) => a-b) 661 .reduce((r, [k, v]) => ({ ...r, [k]: v }), {}); 662 663console.log(sortable); 664console.log(Object.entries(maxSpeed)); 665sortable = Object.entries(maxSpeed) 666 .sort(function([,a],[,b]) { return a - b;}) 667 .reduce(function(previous, current, index, array) { 668 array['x'] = current; 669 }, {}); 670 //.reduce(function(r, [k, v]) { return { ...r, [k]: v }; } ); 671 //.reduce((r, [k, v]) => ({ ...r, [k]: v }), {}); 672 673console.log(sortable); 674*/
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.