├── docs ├── .nojekyll ├── fonts │ ├── OpenSans-Bold-webfont.eot │ ├── OpenSans-Bold-webfont.woff │ ├── OpenSans-Italic-webfont.eot │ ├── OpenSans-Light-webfont.eot │ ├── OpenSans-Light-webfont.woff │ ├── OpenSans-Italic-webfont.woff │ ├── OpenSans-Regular-webfont.eot │ ├── OpenSans-Regular-webfont.woff │ ├── OpenSans-Semibold-webfont.eot │ ├── OpenSans-Semibold-webfont.ttf │ ├── OpenSans-Semibold-webfont.woff │ ├── OpenSans-BoldItalic-webfont.eot │ ├── OpenSans-BoldItalic-webfont.woff │ ├── OpenSans-LightItalic-webfont.eot │ ├── OpenSans-LightItalic-webfont.woff │ ├── OpenSans-SemiboldItalic-webfont.eot │ ├── OpenSans-SemiboldItalic-webfont.ttf │ └── OpenSans-SemiboldItalic-webfont.woff ├── icons │ ├── home.svg │ └── search.svg ├── styles │ ├── collapse.css │ ├── prettify-jsdoc.css │ ├── prettify-tomorrow.css │ └── jsdoc-default.css ├── index.html ├── scripts │ ├── linenumber.js │ ├── prettify │ │ ├── lang-css.js │ │ ├── Apache-License-2.0.txt │ │ └── prettify.js │ └── pagelocation.js ├── README.md ├── module.exports.html ├── Code.js.html ├── scripts_Exports.js.html ├── scripts_Code.js.html ├── modules_Dottie.js.html └── Dottie.js.html ├── .gitignore ├── src ├── modules │ ├── bridge.js │ └── Dottie.js └── scripts │ ├── appsscript.json │ └── Exports.js ├── project ├── appsscript.json ├── TestBundle.js ├── Exports.js └── Bundle.js ├── jsdoc-config.json ├── rollup.config.js ├── LICENSE ├── tests.gs ├── package.json ├── README.md └── tests └── test.js /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .clasp.json 4 | -------------------------------------------------------------------------------- /src/modules/bridge.js: -------------------------------------------------------------------------------- 1 | import DotObject from 'dot-object'; 2 | export {DotObject}; 3 | -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-Bold-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-Bold-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-Italic-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-Light-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-Light-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-Italic-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-Regular-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-Regular-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Semibold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-Semibold-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Semibold-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-Semibold-webfont.ttf -------------------------------------------------------------------------------- /docs/fonts/OpenSans-Semibold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-Semibold-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-BoldItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-BoldItalic-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-BoldItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-BoldItalic-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-LightItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-LightItalic-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-LightItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-LightItalic-webfont.woff -------------------------------------------------------------------------------- /docs/fonts/OpenSans-SemiboldItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-SemiboldItalic-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/OpenSans-SemiboldItalic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf -------------------------------------------------------------------------------- /docs/fonts/OpenSans-SemiboldItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/classroomtechtools/dottie/HEAD/docs/fonts/OpenSans-SemiboldItalic-webfont.woff -------------------------------------------------------------------------------- /project/appsscript.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeZone": "Asia/Hong_Kong", 3 | "dependencies": { 4 | }, 5 | "exceptionLogging": "STACKDRIVER", 6 | "runtimeVersion": "V8" 7 | } 8 | -------------------------------------------------------------------------------- /src/scripts/appsscript.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeZone": "Asia/Hong_Kong", 3 | "dependencies": { 4 | }, 5 | "exceptionLogging": "STACKDRIVER", 6 | "runtimeVersion": "V8" 7 | } 8 | -------------------------------------------------------------------------------- /docs/icons/home.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /project/TestBundle.js: -------------------------------------------------------------------------------- 1 | /* Bundle as defined from all files in tests/serverside/*.js */ 2 | function Test(remote=true) { 3 | 4 | (function () { 5 | 'use strict'; 6 | 7 | 8 | 9 | }()); 10 | try { return Log.get() } catch (e) {} 11 | } 12 | -------------------------------------------------------------------------------- /docs/icons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/styles/collapse.css: -------------------------------------------------------------------------------- 1 | @media only screen and (min-width: 681px) { 2 | nav > ul > li:hover .methods, 3 | .active .methods { 4 | display: block; 5 | } 6 | 7 | .methods { 8 | display: none; 9 | } 10 | 11 | nav > ul > li { 12 | padding: 20px 0; 13 | } 14 | 15 | nav > ul > li > a { 16 | padding: 0; 17 | } 18 | 19 | nav > ul > li.active a { 20 | margin-bottom: 10px; 21 | } 22 | 23 | nav > ul > li:hover > a, 24 | nav > ul > li.active > a { 25 | margin-bottom: 15px; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /jsdoc-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": { 3 | "include": ["src/scripts/Exports.js", "src/modules/Dottie.js"] 4 | }, 5 | "plugins": ["plugins/markdown"], 6 | "templates": { 7 | "default": { 8 | outputSourceFiles: false 9 | }, 10 | "staticFiles": { 11 | "include": [ 12 | "./static" 13 | ] 14 | } 15 | "referenceTitle": "dottie by Classroom Tech Tools." 16 | }, 17 | "markdown": { 18 | "excludeTags": ["author"] 19 | }, 20 | "opts": { 21 | "destination": "./docs" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/scripts/linenumber.js: -------------------------------------------------------------------------------- 1 | /*global document */ 2 | (() => { 3 | const source = document.getElementsByClassName('prettyprint source linenums'); 4 | let i = 0; 5 | let lineNumber = 0; 6 | let lineId; 7 | let lines; 8 | let totalLines; 9 | let anchorHash; 10 | 11 | if (source && source[0]) { 12 | anchorHash = document.location.hash.substring(1); 13 | lines = source[0].getElementsByTagName('li'); 14 | totalLines = lines.length; 15 | 16 | for (; i < totalLines; i++) { 17 | lineNumber++; 18 | lineId = `line${lineNumber}`; 19 | lines[i].id = lineId; 20 | if (lineId === anchorHash) { 21 | lines[i].className += ' selected'; 22 | } 23 | } 24 | } 25 | })(); 26 | -------------------------------------------------------------------------------- /docs/scripts/prettify/lang-css.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", 2 | /^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); 3 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import multi from '@rollup/plugin-multi-entry'; 4 | 5 | const production = !process.env.ROLLUP_WATCH; 6 | const plugins = [multi(), resolve(), commonjs()]; 7 | 8 | export default [{ 9 | input: ['src/modules/*.js','src/modules/builtins/*.js'], 10 | treeshake: true, 11 | output: { 12 | format: 'cjs', 13 | file: './project/Bundle.js', 14 | banner: '/* Bundle as defined from all files in src/modules/*.js */\nconst Import = Object.create(null);\n', 15 | intro: '(function (exports, window) {\n// provide global (danger zone)\nexports.__window = window;', 16 | outro: '})(Import, this);\ntry{exports.Import = Import;}catch(e){}' 17 | }, 18 | plugins 19 | }, { 20 | input: ['src/tests/*.js'], 21 | treeshake: true, 22 | output: { 23 | format: 'iife', 24 | file: './project/TestBundle.js', 25 | banner: '/* Bundle as defined from all files in tests/serverside/*.js */\nfunction Test(remote=true) {\n', 26 | footer: 'try { return Log.get() } catch (e) {} \n}' 27 | }, 28 | plugins 29 | }]; 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Adam Morris 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests.gs: -------------------------------------------------------------------------------- 1 | function testObjectManip() { 2 | // set 3 | const setObj = set({}, 'path.to.value', 100); 4 | if (setObj.path.to.value !== 100) throw new Error("failed!"); 5 | 6 | // get 7 | const getValue = get(setObj, 'path.to.value'); 8 | if (getValue !== 100) throw new Error('failed'); 9 | if (get(setObj, 'not.present') !== null) throw new Error("not null"); 10 | 11 | // move 12 | const moveObj = set({}, 'from.this.here', '200'); 13 | const result = move(moveObj, 'from.this.here', 'to.that.there'); 14 | if (result.to.that.there !== '200') throw new Error("failed"); 15 | 16 | // copy 17 | const copyObj = set({}, 'path.to.value', 300); 18 | const copiedObj = copy(copyObj, 'path.to.value', {}, 'copied.to.here'); 19 | if (get(copiedObj, 'copied.to.here') !== 300) throw new Error("failed"); 20 | 21 | // transfer 22 | const transferObj = set({}, 'path.to.value', 100); 23 | const transferredObj = transfer(transferObj, 'path.to.value', {}, 'transferred.to.here'); 24 | if (get(transferObj, 'path.to.value') !== null) throw new Error("null"); 25 | 26 | // expand 27 | const expandObj = {'path.to.value': 100}; 28 | const expandedObj = expand(expandObj); 29 | if (get(expandedObj, 'path.to.value') !== 100) throw new Error("failed"); 30 | 31 | // delete_ 32 | const deleteObj = set({}, 'path.to.value', 100); 33 | delete_(deleteObj, 'path.to.value'); 34 | if (get(deleteObj, 'path.to.value') !== null) throw new Error("failed"); 35 | 36 | // remove 37 | const removeObj = set({}, 'path.to.value', 100); 38 | remove(removeObj, 'path.to.value'); 39 | if (get(removeObj, 'path.to.value') !== null) throw new Error("failed"); 40 | 41 | // transform 42 | const transformObj = set({}, 'original.path.to.value', 100); 43 | const transformedObj = transform(transformObj, {'original.path.to.value': 'new.path.to.value'}); 44 | if (get(transformedObj, 'new.path.to.value') !== 100) throw new Error('failed'); 45 | } 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@classroomtechtools/dottie", 3 | "version": "1.4.2", 4 | "description": "Build jsons with sanity.", 5 | "main": "src/modules/Dottie.js", 6 | "type": "module", 7 | "scripts": { 8 | "dev": "rollup -c -w", 9 | "start": "sirv build/svelte", 10 | "test": "npm run bundle && ava", 11 | "deploy": "run-s bundle clasp:push", 12 | "build": "run-s rm-artifacts bundle generate", 13 | "bundle": "rollup -c", 14 | "rm-artifacts": "rm -fr ./project/*", 15 | "generate": "run-p generate:*", 16 | "generate:remote": "cp -r ./src/scripts/* ./project/", 17 | "clasp:create": "clasp create --rootDir ./project", 18 | "clasp:login": "clasp login", 19 | "clasp:logout": "clasp logout", 20 | "clasp:logs": "clasp logs", 21 | "clasp:push": "npm run bundle && clasp push", 22 | "clasp:tests": "clasp run tests", 23 | "generate-docs": "docsify init ./docs" 24 | }, 25 | "ava": { 26 | "verbose": true 27 | }, 28 | "keywords": [ 29 | "appscripts" 30 | ], 31 | "author": "Adam Morris (https://classroomtechtools.com)", 32 | "license": "MIT", 33 | "devDependencies": { 34 | "@classroomtechtools/unittesting": "^2.0.3", 35 | "@google/clasp": "^2.3.1", 36 | "@rollup/plugin-commonjs": "^19.0.0", 37 | "@rollup/plugin-json": "^4.1.0", 38 | "@rollup/plugin-multi-entry": "^4.0.0", 39 | "@rollup/plugin-node-resolve": "^13.0.0", 40 | "ava": "^3.15.0", 41 | "npm-run-all": "^4.1.5", 42 | "rollup": "^2.48.0", 43 | "rollup-plugin-hypothetical": "^2.1.0" 44 | }, 45 | "dependencies": { 46 | "dot-object": "^2.1.4", 47 | "jsdoc": "^3.6.6" 48 | }, 49 | "directories": { 50 | "test": "tests" 51 | }, 52 | "repository": { 53 | "type": "git", 54 | "url": "git+https://github.com/classroomtechtools/dottie.git" 55 | }, 56 | "bugs": { 57 | "url": "https://github.com/classroomtechtools/dottie/issues" 58 | }, 59 | "homepage": "https://github.com/classroomtechtools/dottie#readme" 60 | } 61 | -------------------------------------------------------------------------------- /docs/styles/prettify-jsdoc.css: -------------------------------------------------------------------------------- 1 | /* JSDoc prettify.js theme */ 2 | 3 | /* plain text */ 4 | .pln { 5 | color: #000000; 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | /* string content */ 11 | .str { 12 | color: #006400; 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | 17 | /* a keyword */ 18 | .kwd { 19 | color: #000000; 20 | font-weight: bold; 21 | font-style: normal; 22 | } 23 | 24 | /* a comment */ 25 | .com { 26 | font-weight: normal; 27 | font-style: italic; 28 | } 29 | 30 | /* a type name */ 31 | .typ { 32 | color: #000000; 33 | font-weight: normal; 34 | font-style: normal; 35 | } 36 | 37 | /* a literal value */ 38 | .lit { 39 | color: #006400; 40 | font-weight: normal; 41 | font-style: normal; 42 | } 43 | 44 | /* punctuation */ 45 | .pun { 46 | color: #000000; 47 | font-weight: bold; 48 | font-style: normal; 49 | } 50 | 51 | /* lisp open bracket */ 52 | .opn { 53 | color: #000000; 54 | font-weight: bold; 55 | font-style: normal; 56 | } 57 | 58 | /* lisp close bracket */ 59 | .clo { 60 | color: #000000; 61 | font-weight: bold; 62 | font-style: normal; 63 | } 64 | 65 | /* a markup tag name */ 66 | .tag { 67 | color: #006400; 68 | font-weight: normal; 69 | font-style: normal; 70 | } 71 | 72 | /* a markup attribute name */ 73 | .atn { 74 | color: #006400; 75 | font-weight: normal; 76 | font-style: normal; 77 | } 78 | 79 | /* a markup attribute value */ 80 | .atv { 81 | color: #006400; 82 | font-weight: normal; 83 | font-style: normal; 84 | } 85 | 86 | /* a declaration */ 87 | .dec { 88 | color: #000000; 89 | font-weight: bold; 90 | font-style: normal; 91 | } 92 | 93 | /* a variable name */ 94 | .var { 95 | color: #000000; 96 | font-weight: normal; 97 | font-style: normal; 98 | } 99 | 100 | /* a function name */ 101 | .fun { 102 | color: #000000; 103 | font-weight: bold; 104 | font-style: normal; 105 | } 106 | 107 | /* Specify class=linenums on a pre to get line numbering */ 108 | ol.linenums { 109 | margin-top: 0; 110 | margin-bottom: 0; 111 | } 112 | -------------------------------------------------------------------------------- /docs/styles/prettify-tomorrow.css: -------------------------------------------------------------------------------- 1 | /* Tomorrow Theme */ 2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */ 3 | /* Pretty printing styles. Used with prettify.js. */ 4 | /* SPAN elements with the classes below are added by prettyprint. */ 5 | /* plain text */ 6 | .pln { 7 | color: #4d4d4c; } 8 | 9 | @media screen { 10 | /* string content */ 11 | .str { 12 | color: #718c00; } 13 | 14 | /* a keyword */ 15 | .kwd { 16 | color: #8959a8; } 17 | 18 | /* a comment */ 19 | .com { 20 | color: #8e908c; } 21 | 22 | /* a type name */ 23 | .typ { 24 | color: #4271ae; } 25 | 26 | /* a literal value */ 27 | .lit { 28 | color: #f5871f; } 29 | 30 | /* punctuation */ 31 | .pun { 32 | color: #4d4d4c; } 33 | 34 | /* lisp open bracket */ 35 | .opn { 36 | color: #4d4d4c; } 37 | 38 | /* lisp close bracket */ 39 | .clo { 40 | color: #4d4d4c; } 41 | 42 | /* a markup tag name */ 43 | .tag { 44 | color: #c82829; } 45 | 46 | /* a markup attribute name */ 47 | .atn { 48 | color: #f5871f; } 49 | 50 | /* a markup attribute value */ 51 | .atv { 52 | color: #3e999f; } 53 | 54 | /* a declaration */ 55 | .dec { 56 | color: #f5871f; } 57 | 58 | /* a variable name */ 59 | .var { 60 | color: #c82829; } 61 | 62 | /* a function name */ 63 | .fun { 64 | color: #4271ae; } } 65 | /* Use higher contrast and text-weight for printable form. */ 66 | @media print, projection { 67 | .str { 68 | color: #060; } 69 | 70 | .kwd { 71 | color: #006; 72 | font-weight: bold; } 73 | 74 | .com { 75 | color: #600; 76 | font-style: italic; } 77 | 78 | .typ { 79 | color: #404; 80 | font-weight: bold; } 81 | 82 | .lit { 83 | color: #044; } 84 | 85 | .pun, .opn, .clo { 86 | color: #440; } 87 | 88 | .tag { 89 | color: #006; 90 | font-weight: bold; } 91 | 92 | .atn { 93 | color: #404; } 94 | 95 | .atv { 96 | color: #060; } } 97 | /* Style */ 98 | /* 99 | pre.prettyprint { 100 | background: white; 101 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 102 | font-size: 12px; 103 | line-height: 1.5; 104 | border: 1px solid #ccc; 105 | padding: 10px; } 106 | */ 107 | 108 | /* Specify class=linenums on a pre to get line numbering */ 109 | ol.linenums { 110 | margin-top: 0; 111 | margin-bottom: 0; } 112 | 113 | /* IE indents via margin-left */ 114 | li.L0, 115 | li.L1, 116 | li.L2, 117 | li.L3, 118 | li.L4, 119 | li.L5, 120 | li.L6, 121 | li.L7, 122 | li.L8, 123 | li.L9 { 124 | /* */ } 125 | 126 | /* Alternate shading for lines */ 127 | li.L1, 128 | li.L3, 129 | li.L5, 130 | li.L7, 131 | li.L9 { 132 | /* */ } 133 | -------------------------------------------------------------------------------- /docs/scripts/pagelocation.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | $(document).ready(function () { 4 | var currentSectionNav, target; 5 | 6 | // If an anchor hash is in the URL highlight the menu item 7 | highlightActiveHash(); 8 | // If a specific page section is in the URL highlight the menu item 9 | highlightActiveSection(); 10 | 11 | // If a specific page section is in the URL scroll that section up to the top 12 | currentSectionNav = $('#' + getCurrentSectionName() + '-nav'); 13 | 14 | if (currentSectionNav.position()) { 15 | $('nav').scrollTop(currentSectionNav.position().top); 16 | } 17 | 18 | // function to scroll to anchor when clicking an anchor linl 19 | $('a[href*="#"]:not([href="#"])').click(function () { 20 | /* eslint-disable no-invalid-this */ 21 | if (location.pathname.replace(/^\//, '') === this.pathname.replace(/^\//, '') && location.hostname === this.hostname) { 22 | target = $(this.hash); 23 | target = target.length ? target : $('[name=' + this.hash.slice(1) + ']'); 24 | if (target.length) { 25 | $('html, body').animate({ 26 | scrollTop: target.offset().top 27 | }, 1000); 28 | } 29 | } 30 | /* eslint-enable no-invalid-this */ 31 | }); 32 | }); 33 | 34 | // If a new anchor section is selected, change the hightlighted menu item 35 | $(window).bind('hashchange', function (event) { 36 | highlightActiveHash(event); 37 | }); 38 | 39 | function highlightActiveHash(event) { 40 | var oldUrl, oldSubSectionElement; 41 | 42 | // check for and remove old hash active state 43 | if (event && event.originalEvent.oldURL) { 44 | oldUrl = event.originalEvent.oldURL; 45 | 46 | if (oldUrl.indexOf('#') > -1) { 47 | oldSubSectionElement = $('#' + getCurrentSectionName() + '-' + oldUrl.substring(oldUrl.indexOf('#') + 1) + '-nav'); 48 | 49 | if (oldSubSectionElement) { 50 | oldSubSectionElement.removeClass('active'); 51 | } 52 | } 53 | } 54 | 55 | if (getCurrentHashName()) { 56 | $('#' + getCurrentSectionName() + '-' + getCurrentHashName() + '-nav').addClass('active'); 57 | } 58 | } 59 | 60 | function highlightActiveSection() { 61 | var pageId = getCurrentSectionName(); 62 | 63 | $('#' + pageId + '-nav').addClass('active'); 64 | } 65 | 66 | function getCurrentSectionName() { 67 | var path = window.location.pathname; 68 | var pageUrl = path.split('/').pop(); 69 | 70 | var sectionName = pageUrl.substring(0, pageUrl.indexOf('.')); 71 | 72 | // remove the wodr module- if its in the url 73 | sectionName = sectionName.replace('module-', ''); 74 | 75 | return sectionName; 76 | } 77 | 78 | function getCurrentHashName() { 79 | var pageSubSectionId; 80 | var pageSubSectionHash = window.location.hash; 81 | 82 | if (pageSubSectionHash) { 83 | pageSubSectionId = pageSubSectionHash.substring(1).replace('.', ''); 84 | 85 | return pageSubSectionId; 86 | } 87 | 88 | return false; 89 | } 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dottie.gs 2 | 3 | Convert an array of jsons to spreadsheet-friendly 2d arrays, and back again. Work with nested objects. Made a cinch. 4 | 5 | With thanks to [dot-object](https://github.com/rhalff/dot-object). 6 | 7 | ## Quickstart 8 | 9 | - Script ID: `1k_EGzQ6FvfMlyifgPxcBqgPe3TQSWLFGF9VrDWAGU1wfOFnOFsRbI8V_` 10 | - Default identifier is `dottie` 11 | - See [Documentation](https://classroomtechtools.github.io/dottie/) 12 | 13 | 14 | ## Usage 15 | 16 | - Use it to flatten jsons from apis into 2-d arrays, and back again 17 | - Use it to work with objects, such as copying properties 18 | - Advanced: Use it inline 19 | 20 | ### Use it to flatten jsons into arrays, and back again 21 | 22 | #### dottie.jsonsToRows 23 | 24 | Takes an array of json objects and converts into a spreadsheet-friendly 2d array. The columns are named with dot notation according to the path of the properties. The first row contains the headers/columns (in alphabetical order) and the remaining rows are the values. 25 | 26 | #### Examples: 27 | 28 | ```js 29 | const jsons = [ 30 | {one: {two: 2}}, 31 | {one: {two: 2, three: 3}}, 32 | {another: 'one'} 33 | ]; 34 | const result = dottie.jsonsToRows(jsons); 35 | Logger.log(result); 36 | ``` 37 | 38 | Result is (formatted for readability): 39 | 40 | ```js 41 | [ 42 | ['another', 'one.three', 'one.two'], 43 | [ null, null, 2.0], 44 | [ null, 3.0, 2.0], 45 | [ 'one', null, null] 46 | ] 47 | ``` 48 | 49 | > Note that `.jsonsToRows` doesn't try to normalize or adjust the values in any way. If you need it to output in some specific manner, the idea is to adjust the jsons themselves to produce the desired result 50 | 51 | For example, we have the following structure, but you want to ensure that the spreadsheet has consistent amount of columns: 52 | 53 | ```js 54 | const jsons = [ 55 | { 56 | value: 'value', 57 | arr: [ 58 | {first: 'first'}, 59 | {second: 'second'} 60 | ] 61 | } 62 | ] 63 | for (const json of jsons) { 64 | json.arr.length = 5; // guaranteed to be five columns long! 65 | } 66 | dottie.jsonsToRows(jsons); 67 | ``` 68 | 69 | You can also define the order in which the columns are given in the second paramter: 70 | 71 | ```js 72 | const jsons = /* ... */; 73 | dottie.jsonsToRows(jsons, ['id', 'name']); 74 | ``` 75 | 76 | > If there are more columns than `id` and `name`, the remaining columns will be output after those, in alphabetical order. 77 | 78 | #### dottie.rowsToJsons 79 | 80 | This is the reverse of `dottie.jsonsToRows`. 81 | 82 | ### Use it to work with objects 83 | 84 | Working with jsons can be bit difficult. The following is much more readable (and easier to edit) than writing out the object in long form: 85 | 86 | ```js 87 | const cards = {}; 88 | const path = 'cards[0].sections[0].widgets[0].keyValue'; 89 | dottie.set(cards, `${path}.topLabel`, 'Ticket no.'); 90 | dottie.set(cards, `${path}.content`, ticketId.toString()); 91 | dottie.set(cards, `${path}.contentMultiline`, false); 92 | dottie.set(cards, `${path}.bottomLabel`, item.priority.toUpperCase()); 93 | dottie.set(cards, `${path}.icon`, 'TICKET'); 94 | Logger.log(cards); 95 | /* 96 | { 97 | cards: [ 98 | { 99 | sections: [ 100 | { 101 | widgets: [ 102 | { 103 | keyValue: { 104 | topLabel: 'Ticket no.', 105 | content: '', 106 | contentMultiline: false, 107 | bottomLabel: 'HIGH' 108 | } 109 | ] 110 | } 111 | ] 112 | } 113 | ] 114 | */ 115 | ``` 116 | 117 | Example usage 118 | 119 | ```js 120 | const obj = dottie.set({}, 'path.to.value', 100); 121 | Logger.log(obj); 122 | /* 123 | { 124 | path: { 125 | to: { 126 | value: 100 127 | } 128 | } 129 | } 130 | */ 131 | const value = dottie.get(obj, 'path.to.value'); 132 | Logger.log(value); 133 | /* 134 | 100 135 | */ 136 | 137 | const obj = dottie.set({}, 'path.to.array[0].name', 'Bob'); 138 | Logger.log(obj); 139 | /* 140 | { 141 | path: { 142 | to: { 143 | array: [ 144 | {name: "Bob"} 145 | ] 146 | } 147 | } 148 | } 149 | */ 150 | ``` 151 | 152 | 153 | 154 | ### Advanced: Usage of `.augment` 155 | 156 | If using `dottie` namespace doesn't fit your brain, you can do this: 157 | 158 | ```js 159 | dottie.augment(Object, Array); 160 | ``` 161 | 162 | That will let you use `{}.dottie.` alternative syntax. Note that parameters are not positional: 163 | 164 | ```js 165 | const obj = {}.dottie.set({path: 'path.to.value', value: 100}); 166 | Logger.log(obj); 167 | /* 168 | { 169 | path: { 170 | to: { 171 | value: 100 172 | } 173 | } 174 | } 175 | */ 176 | const value = obj.dottie.get({path: 'path.to.value'}); 177 | Logger.log(value); 178 | /* 179 | 100 180 | */ 181 | 182 | const obj = {}.dottie.set({path: 'path.to.array[0].name', value: 'Bob'}); 183 | Logger.log(obj); 184 | /* 185 | { 186 | path: { 187 | to: { 188 | array: [ 189 | {name: "Bob"} 190 | ] 191 | } 192 | } 193 | } 194 | */ 195 | ``` 196 | 197 | ## Notes 198 | 199 | ### Errors thrown 200 | 201 | It is the author's opinion that a library should throw errors if its API is used wrongly, instead of failing at some obscure code path. This ensures that the developer understands how to use the library correctly. 202 | 203 | For that reason, type-checking is enabled. Dottie throws an error with explanation if: 204 | 205 | * its methods are passed with incompatible or unexpected types 206 | * if required parameters are not passed 207 | * more than the necessary parameters are passed 208 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # dottie.gs 2 | 3 | Convert an array of jsons to spreadsheet-friendly 2d arrays, and back again. Work with nested objects. Made a cinch. 4 | 5 | With thanks to [dot-object](https://github.com/rhalff/dot-object). 6 | 7 | ## Quickstart 8 | 9 | - Script ID: `1k_EGzQ6FvfMlyifgPxcBqgPe3TQSWLFGF9VrDWAGU1wfOFnOFsRbI8V_` 10 | - Default identifier is `dottie` 11 | - See [Documentation](https://classroomtechtools.github.io/dottie/) 12 | 13 | 14 | ## Usage 15 | 16 | - Use it to flatten jsons from apis into 2-d arrays, and back again 17 | - Use it to work with objects, such as copying properties 18 | - Advanced: Use it inline 19 | 20 | ### Use it to flatten jsons into arrays, and back again 21 | 22 | #### dottie.jsonsToRows 23 | 24 | Takes an array of json objects and converts into a spreadsheet-friendly 2d array. The columns are named with dot notation according to the path of the properties. The first row contains the headers/columns (in alphabetical order) and the remaining rows are the values. 25 | 26 | #### Examples: 27 | 28 | ```js 29 | const jsons = [ 30 | {one: {two: 2}}, 31 | {one: {two: 2, three: 3}}, 32 | {another: 'one'} 33 | ]; 34 | const result = dottie.jsonsToRows(jsons); 35 | Logger.log(result); 36 | ``` 37 | 38 | Result is (formatted for readability): 39 | 40 | ```js 41 | [ 42 | ['another', 'one.three', 'one.two'], 43 | [ null, null, 2.0], 44 | [ null, 3.0, 2.0], 45 | [ 'one', null, null] 46 | ] 47 | ``` 48 | 49 | > Note that `.jsonsToRows` doesn't try to normalize or adjust the values in any way. If you need it to output in some specific manner, the idea is to adjust the jsons themselves to produce the desired result 50 | 51 | For example, we have the following structure, but you want to ensure that the spreadsheet has consistent amount of columns: 52 | 53 | ```js 54 | const jsons = [ 55 | { 56 | value: 'value', 57 | arr: [ 58 | {first: 'first'}, 59 | {second: 'second'} 60 | ] 61 | } 62 | ] 63 | for (const json of jsons) { 64 | json.arr.length = 5; // guaranteed to be five columns long! 65 | } 66 | dottie.jsonsToRows(jsons); 67 | ``` 68 | 69 | You can also define the order in which the columns are given in the second paramter: 70 | 71 | ```js 72 | const jsons = /* ... */; 73 | dottie.jsonsToRows(jsons, ['id', 'name']); 74 | ``` 75 | 76 | > If there are more columns than `id` and `name`, the remaining columns will be output after those, in alphabetical order. 77 | 78 | #### dottie.rowsToJsons 79 | 80 | This is the reverse of `dottie.jsonsToRows`. 81 | 82 | ### Use it to work with objects 83 | 84 | Working with jsons can be bit difficult. The following is much more readable (and easier to edit) than writing out the object in long form: 85 | 86 | ```js 87 | const cards = {}; 88 | const path = 'cards[0].sections[0].widgets[0].keyValue'; 89 | dottie.set(cards, `${path}.topLabel`, 'Ticket no.'); 90 | dottie.set(cards, `${path}.content`, ticketId.toString()); 91 | dottie.set(cards, `${path}.contentMultiline`, false); 92 | dottie.set(cards, `${path}.bottomLabel`, item.priority.toUpperCase()); 93 | dottie.set(cards, `${path}.icon`, 'TICKET'); 94 | Logger.log(cards); 95 | /* 96 | { 97 | cards: [ 98 | { 99 | sections: [ 100 | { 101 | widgets: [ 102 | { 103 | keyValue: { 104 | topLabel: 'Ticket no.', 105 | content: '', 106 | contentMultiline: false, 107 | bottomLabel: 'HIGH' 108 | } 109 | ] 110 | } 111 | ] 112 | } 113 | ] 114 | */ 115 | ``` 116 | 117 | Example usage 118 | 119 | ```js 120 | const obj = dottie.set({}, 'path.to.value', 100); 121 | Logger.log(obj); 122 | /* 123 | { 124 | path: { 125 | to: { 126 | value: 100 127 | } 128 | } 129 | } 130 | */ 131 | const value = dottie.get(obj, 'path.to.value'); 132 | Logger.log(value); 133 | /* 134 | 100 135 | */ 136 | 137 | const obj = dottie.set({}, 'path.to.array[0].name', 'Bob'); 138 | Logger.log(obj); 139 | /* 140 | { 141 | path: { 142 | to: { 143 | array: [ 144 | {name: "Bob"} 145 | ] 146 | } 147 | } 148 | } 149 | */ 150 | ``` 151 | 152 | 153 | 154 | ### Advanced: Usage of `.augment` 155 | 156 | If using `dottie` namespace doesn't fit your brain, you can do this: 157 | 158 | ```js 159 | dottie.augment(Object, Array); 160 | ``` 161 | 162 | That will let you use `{}.dottie.` alternative syntax. Note that parameters are not positional: 163 | 164 | ```js 165 | const obj = {}.dottie.set({path: 'path.to.value', value: 100}); 166 | Logger.log(obj); 167 | /* 168 | { 169 | path: { 170 | to: { 171 | value: 100 172 | } 173 | } 174 | } 175 | */ 176 | const value = obj.dottie.get({path: 'path.to.value'}); 177 | Logger.log(value); 178 | /* 179 | 100 180 | */ 181 | 182 | const obj = {}.dottie.set({path: 'path.to.array[0].name', value: 'Bob'}); 183 | Logger.log(obj); 184 | /* 185 | { 186 | path: { 187 | to: { 188 | array: [ 189 | {name: "Bob"} 190 | ] 191 | } 192 | } 193 | } 194 | */ 195 | ``` 196 | 197 | ## Notes 198 | 199 | ### Errors thrown 200 | 201 | It is the author's opinion that a library should throw errors if its API is used wrongly, instead of failing at some obscure code path. This ensures that the developer understands how to use the library correctly. 202 | 203 | For that reason, type-checking is enabled. Dottie throws an error with explanation if: 204 | 205 | * its methods are passed with incompatible or unexpected types 206 | * if required parameters are not passed 207 | * more than the necessary parameters are passed 208 | -------------------------------------------------------------------------------- /docs/module.exports.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | exports - Documentation 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 47 | 48 | 49 | 50 |
51 | 67 |
68 | 69 | 78 | 79 |
80 | 81 |

82 | exports 83 |

84 | 85 | 86 | 87 | 88 |
89 |
90 | 91 |

92 | 93 | exports 94 | 95 |

96 | 97 | 98 |
99 |

Global methods interface with these methods. If augment is used, these methods apply to Objects and Arrays

100 |
101 | 102 | 103 |
104 | 105 |
106 |
107 | 108 | 109 | 110 | 111 | 112 |

Constructor

113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 |

122 | new exports() 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 | 162 | 163 | 164 | 165 | 166 |
Source:
167 |
168 | 173 |
174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 |
182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 |
198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 |
217 |
218 | 219 | 220 | 221 | 222 |
223 | 224 |
225 | 226 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /docs/styles/jsdoc-default.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Open Sans'; 3 | font-weight: normal; 4 | font-style: normal; 5 | src: url('../fonts/OpenSans-Regular-webfont.eot'); 6 | src: 7 | local('Open Sans'), 8 | local('OpenSans'), 9 | url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'), 10 | url('../fonts/OpenSans-Regular-webfont.woff') format('woff'), 11 | url('../fonts/OpenSans-Regular-webfont.svg#open_sansregular') format('svg'); 12 | } 13 | 14 | @font-face { 15 | font-family: 'Open Sans Light'; 16 | font-weight: normal; 17 | font-style: normal; 18 | src: url('../fonts/OpenSans-Light-webfont.eot'); 19 | src: 20 | local('Open Sans Light'), 21 | local('OpenSans Light'), 22 | url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'), 23 | url('../fonts/OpenSans-Light-webfont.woff') format('woff'), 24 | url('../fonts/OpenSans-Light-webfont.svg#open_sanslight') format('svg'); 25 | } 26 | 27 | html 28 | { 29 | overflow: auto; 30 | background-color: #fff; 31 | font-size: 14px; 32 | } 33 | 34 | body 35 | { 36 | font-family: 'Open Sans', sans-serif; 37 | line-height: 1.5; 38 | color: #4d4e53; 39 | background-color: white; 40 | } 41 | 42 | a, a:visited, a:active { 43 | color: #0095dd; 44 | text-decoration: none; 45 | } 46 | 47 | a:hover { 48 | text-decoration: underline; 49 | } 50 | 51 | header 52 | { 53 | display: block; 54 | padding: 0px 4px; 55 | } 56 | 57 | tt, code, kbd, samp { 58 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 59 | } 60 | 61 | .class-description { 62 | font-size: 130%; 63 | line-height: 140%; 64 | margin-bottom: 1em; 65 | margin-top: 1em; 66 | } 67 | 68 | .class-description:empty { 69 | margin: 0; 70 | } 71 | 72 | #main { 73 | float: left; 74 | width: 70%; 75 | } 76 | 77 | article dl { 78 | margin-bottom: 40px; 79 | } 80 | 81 | article img { 82 | max-width: 100%; 83 | } 84 | 85 | section 86 | { 87 | display: block; 88 | background-color: #fff; 89 | padding: 12px 24px; 90 | border-bottom: 1px solid #ccc; 91 | margin-right: 30px; 92 | } 93 | 94 | .variation { 95 | display: none; 96 | } 97 | 98 | .signature-attributes { 99 | font-size: 60%; 100 | color: #aaa; 101 | font-style: italic; 102 | font-weight: lighter; 103 | } 104 | 105 | nav 106 | { 107 | display: block; 108 | float: right; 109 | margin-top: 28px; 110 | width: 30%; 111 | box-sizing: border-box; 112 | border-left: 1px solid #ccc; 113 | padding-left: 16px; 114 | } 115 | 116 | nav ul { 117 | font-family: 'Lucida Grande', 'Lucida Sans Unicode', arial, sans-serif; 118 | font-size: 100%; 119 | line-height: 17px; 120 | padding: 0; 121 | margin: 0; 122 | list-style-type: none; 123 | } 124 | 125 | nav ul a, nav ul a:visited, nav ul a:active { 126 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 127 | line-height: 18px; 128 | color: #4D4E53; 129 | } 130 | 131 | nav h3 { 132 | margin-top: 12px; 133 | } 134 | 135 | nav li { 136 | margin-top: 6px; 137 | } 138 | 139 | footer { 140 | display: block; 141 | padding: 6px; 142 | margin-top: 12px; 143 | font-style: italic; 144 | font-size: 90%; 145 | } 146 | 147 | h1, h2, h3, h4 { 148 | font-weight: 200; 149 | margin: 0; 150 | } 151 | 152 | h1 153 | { 154 | font-family: 'Open Sans Light', sans-serif; 155 | font-size: 48px; 156 | letter-spacing: -2px; 157 | margin: 12px 24px 20px; 158 | } 159 | 160 | h2, h3.subsection-title 161 | { 162 | font-size: 30px; 163 | font-weight: 700; 164 | letter-spacing: -1px; 165 | margin-bottom: 12px; 166 | } 167 | 168 | h3 169 | { 170 | font-size: 24px; 171 | letter-spacing: -0.5px; 172 | margin-bottom: 12px; 173 | } 174 | 175 | h4 176 | { 177 | font-size: 18px; 178 | letter-spacing: -0.33px; 179 | margin-bottom: 12px; 180 | color: #4d4e53; 181 | } 182 | 183 | h5, .container-overview .subsection-title 184 | { 185 | font-size: 120%; 186 | font-weight: bold; 187 | letter-spacing: -0.01em; 188 | margin: 8px 0 3px 0; 189 | } 190 | 191 | h6 192 | { 193 | font-size: 100%; 194 | letter-spacing: -0.01em; 195 | margin: 6px 0 3px 0; 196 | font-style: italic; 197 | } 198 | 199 | table 200 | { 201 | border-spacing: 0; 202 | border: 0; 203 | border-collapse: collapse; 204 | } 205 | 206 | td, th 207 | { 208 | border: 1px solid #ddd; 209 | margin: 0px; 210 | text-align: left; 211 | vertical-align: top; 212 | padding: 4px 6px; 213 | display: table-cell; 214 | } 215 | 216 | thead tr 217 | { 218 | background-color: #ddd; 219 | font-weight: bold; 220 | } 221 | 222 | th { border-right: 1px solid #aaa; } 223 | tr > th:last-child { border-right: 1px solid #ddd; } 224 | 225 | .ancestors, .attribs { color: #999; } 226 | .ancestors a, .attribs a 227 | { 228 | color: #999 !important; 229 | text-decoration: none; 230 | } 231 | 232 | .clear 233 | { 234 | clear: both; 235 | } 236 | 237 | .important 238 | { 239 | font-weight: bold; 240 | color: #950B02; 241 | } 242 | 243 | .yes-def { 244 | text-indent: -1000px; 245 | } 246 | 247 | .type-signature { 248 | color: #aaa; 249 | } 250 | 251 | .name, .signature { 252 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 253 | } 254 | 255 | .details { margin-top: 14px; border-left: 2px solid #DDD; } 256 | .details dt { width: 120px; float: left; padding-left: 10px; padding-top: 6px; } 257 | .details dd { margin-left: 70px; } 258 | .details ul { margin: 0; } 259 | .details ul { list-style-type: none; } 260 | .details li { margin-left: 30px; padding-top: 6px; } 261 | .details pre.prettyprint { margin: 0 } 262 | .details .object-value { padding-top: 0; } 263 | 264 | .description { 265 | margin-bottom: 1em; 266 | margin-top: 1em; 267 | } 268 | 269 | .code-caption 270 | { 271 | font-style: italic; 272 | font-size: 107%; 273 | margin: 0; 274 | } 275 | 276 | .source 277 | { 278 | border: 1px solid #ddd; 279 | width: 80%; 280 | overflow: auto; 281 | } 282 | 283 | .prettyprint.source { 284 | width: inherit; 285 | } 286 | 287 | .source code 288 | { 289 | font-size: 100%; 290 | line-height: 18px; 291 | display: block; 292 | padding: 4px 12px; 293 | margin: 0; 294 | background-color: #fff; 295 | color: #4D4E53; 296 | } 297 | 298 | .prettyprint code span.line 299 | { 300 | display: inline-block; 301 | } 302 | 303 | .prettyprint.linenums 304 | { 305 | padding-left: 70px; 306 | -webkit-user-select: none; 307 | -moz-user-select: none; 308 | -ms-user-select: none; 309 | user-select: none; 310 | } 311 | 312 | .prettyprint.linenums ol 313 | { 314 | padding-left: 0; 315 | } 316 | 317 | .prettyprint.linenums li 318 | { 319 | border-left: 3px #ddd solid; 320 | } 321 | 322 | .prettyprint.linenums li.selected, 323 | .prettyprint.linenums li.selected * 324 | { 325 | background-color: lightyellow; 326 | } 327 | 328 | .prettyprint.linenums li * 329 | { 330 | -webkit-user-select: text; 331 | -moz-user-select: text; 332 | -ms-user-select: text; 333 | user-select: text; 334 | } 335 | 336 | .params .name, .props .name, .name code { 337 | color: #4D4E53; 338 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 339 | font-size: 100%; 340 | } 341 | 342 | .params td.description > p:first-child, 343 | .props td.description > p:first-child 344 | { 345 | margin-top: 0; 346 | padding-top: 0; 347 | } 348 | 349 | .params td.description > p:last-child, 350 | .props td.description > p:last-child 351 | { 352 | margin-bottom: 0; 353 | padding-bottom: 0; 354 | } 355 | 356 | .disabled { 357 | color: #454545; 358 | } 359 | -------------------------------------------------------------------------------- /project/Exports.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns the library itself; only useful for internal purposes such as testing 3 | * @ignore 4 | * @returns {DottieLib} 5 | */ 6 | function lib() { 7 | const {Dottie} = Import; 8 | return Dottie; 9 | } 10 | 11 | 12 | /** 13 | * Sets `value` at the location in `obj` as determined by `path` 14 | * @param {Object} obj 15 | * @param {String} path 16 | * @param {Any} value 17 | * @returns {Object} 18 | * @example 19 | const path = 'path.to.key'; 20 | let obj = {}; 21 | obj = dottie.set(obj, path, 'value'); 22 | Logger.log(obj.path.to.key); // 'value' 23 | */ 24 | function set(obj, path, value) { 25 | const {Dottie} = Import; 26 | return Dottie.set({path, value, obj}); 27 | } 28 | 29 | 30 | /** 31 | * Returns the value at location indicated by `path` of `obj` 32 | * @param {Object} obj 33 | * @param {String} path 34 | * @return {Any} 35 | * @example 36 | const path = 'path.to.key'; 37 | let obj = {}; 38 | obj = dottie.set(obj, path, 'value'); 39 | const result = dottie.get(obj, path); 40 | Logger.log(result); // 'value' 41 | */ 42 | function get(obj, path) { 43 | const {Dottie} = Import; 44 | return Dottie.get({path, obj}); 45 | } 46 | 47 | 48 | /** 49 | * Move a property within one object to another location. In-place operation, returns obj. If sourcePath is undefined, nothing changed 50 | * @param {Object} obj 51 | * @param {String} sourcePath 52 | * @param {String} destPath 53 | * @return {Object} 54 | * @example 55 | const path = 'path.to.key'; 56 | const diffPath = 'a.different.path.to.key'; 57 | let obj = {}; 58 | obj = dottie.set(obj, path, 'value'); 59 | obj = dottie.move(obj, path, diffPath); 60 | const first = dottie.get(obj, path); 61 | const second = dottie.get(obj, diffPath); 62 | Logger.log(first); // undefined 63 | Logger.log(second); // 'value' 64 | */ 65 | function move(obj, sourcePath, destPath) { 66 | const {Dottie} = Import; 67 | return Dottie.move({sourcePath, destPath, obj}) 68 | } 69 | 70 | 71 | /** 72 | * Copy property from one object to another object. If sourcePath is undefined, nothing changed. It returns the destination object, but the operation is in-place. 73 | * @param {Object} obj 74 | * @param {String} sourcePath 75 | * @param {Object} target 76 | * @param {String} destPath 77 | * @returns {Object} 78 | * @example 79 | const path = 'path.to.key'; 80 | const source = dottie.set({}, path, 'value'); 81 | const dest = {}; 82 | dottie.copy(source, path, dest, 'new.path.to.key'); 83 | const present = dottie.get(source, path); 84 | const value = dottie.get(dest, path); 85 | Logger.log(present); // undefined 86 | Logger.log(value); // 'value' 87 | */ 88 | function copy (obj, sourcePath, target, destPath) { 89 | const {Dottie} = Import; 90 | return Dottie.copy({sourcePath, destPath, obj, target}); 91 | } 92 | 93 | 94 | /** 95 | * Transfer property from one object to another. Removes from sourceObject. If sourcePath is undefined, nothing happens. Same as copy except does not remove from source. 96 | * @param {Object} obj 97 | * @param {String} sourcePath 98 | * @param {Object} target 99 | * @param {String} destPath 100 | * @return {Object} 101 | * @example 102 | const path = 'path.to.key'; 103 | const source = dottie.set({}, path, 'value'); 104 | const dest = {}; 105 | dottie.copy(source, dest, path, 'new.path.to.key'); 106 | const present = dottie.get(source, path); 107 | const value = dottie.get(dest, path); 108 | Logger.log(present); // undefined 109 | Logger.log(value); // 'value' 110 | */ 111 | function transfer (obj, sourcePath, target, destPath) { 112 | const {Dottie} = Import; 113 | return Dottie.transfer({sourcePath, destPath, obj, target}); 114 | } 115 | 116 | 117 | /** 118 | * Converts an object with dotted-key/value pairs to it's expanded/normal version 119 | * @param {Object} obj 120 | * @return {Object} 121 | */ 122 | function expand (obj) { 123 | const {Dottie} = Import; 124 | return Dottie.expand({obj}); 125 | } 126 | 127 | 128 | /** 129 | * Remove a value using dot notation (and keep array indexes) 130 | * @param {Object} obj 131 | * @param {String} path 132 | * @return {Any} 133 | */ 134 | function remove(obj, path) { 135 | const {Dottie} = Import; 136 | return Dottie.remove({obj, path}); 137 | } 138 | 139 | 140 | /** 141 | * Delete a value using dot notation (and adjust array indexes) 142 | * @param {Object} obj 143 | * @param {Object} path 144 | * @return {Object} 145 | */ 146 | function delete_(obj, path) { 147 | const {Dottie} = Import; 148 | return Dottie.delete_({obj, path}); 149 | } 150 | 151 | 152 | /** 153 | * Transform properties 154 | * @param {Object} obj 155 | * @param {Object} recipe 156 | * return {Object} 157 | */ 158 | function transform(obj, recipe) { 159 | const {Dottie} = Import; 160 | return Dottie.transform({recipe, obj}); 161 | } 162 | 163 | 164 | /** 165 | * Convert object to dotted-key/value pair 166 | * @param {Object} obj 167 | * @return {Object} 168 | */ 169 | function dot(obj) { 170 | const {Dottie} = Import; 171 | return Dottie.dot({obj}); 172 | } 173 | 174 | 175 | /** 176 | * Convert an array of jsons to a 2d array that can be used to populate a Google Spreadsheet with unique headers. The retuned object's first element is an array of header values whose string values are derived via path notation, and any nested objects or arrays follow the naming convention provided by dots (for objects) and brackets (for arrays). 177 | * @param {Object[]} jsons - An array of objects, which can contain native values or nested objects with native values 178 | * @param {Array} [priorityHeaders=[]] - A list of headers you want to appear first as the headers. Note, if header does not appear in any row, it will not appear at all even though you've specified it as priority. 179 | * @param {Boolean} [deleteNulls=false] - If true, do not include any columns whose values are null 180 | * @param {Boolean} [deleteEmptyArrays=true] - If true, make sure that columns that have empty arrays are not shown as null fields 181 | * @return {Array[]} 182 | * @example 183 | // simple example 184 | const jsons = [{ 185 | obj: { 186 | key: 'value' 187 | }, 188 | arr: [1, 2] 189 | }]; 190 | const result = dottie.jsonsToRows(jsons); 191 | Logger.log(result); 192 | // [ ['obj.key', 'arr[0]', 'arry[1]'], 193 | // ['value', 1, 2] 194 | * @example 195 | // example with null objects and empty arrays 196 | const jsons = [{ 197 | obj: { 198 | key: null 199 | }, 200 | arr: [] 201 | }, true, true]; 202 | const result = dottie.jsonsToRows(jsons); 203 | Logger.log(result); 204 | // [ 205 | // ['obj.key'], 206 | // [null] 207 | // ] 208 | */ 209 | function jsonsToRows (jsons, priorityHeaders=[], deleteNulls=false, deleteEmptyArrays=true) { 210 | const {Dottie} = Import; 211 | return Dottie.jsonsToRows({jsons, priorityHeaders, deleteNulls, deleteEmptyArrays}); 212 | } 213 | 214 | 215 | /** 216 | * 217 | * @param {Array[]} rows - first item is array string headers in dot notation, each subsequent row represents an item whose values coorespond to the header's path 218 | * @return {Object[]} 219 | * @example 220 | const headers = ['one', 'two.value', 'arr[0].name']; 221 | const rows = [ 222 | 1, 2, 'Name' 223 | ]; 224 | const jsons = dottie.rowsToJsons([headers, ...rows]); 225 | Logger.log(jsons); 226 | // [{ 227 | one: 1, 228 | two: { 229 | value: 2 230 | }, 231 | arr: [ 232 | { 233 | name: 'Name' 234 | } 235 | ] 236 | }] 237 | */ 238 | 239 | function rowsToJsons(rows) { 240 | const {Dottie} = Import; 241 | return Dottie.rowsToJsons({rows}); 242 | } 243 | 244 | 245 | /** 246 | * Alternative way to utilize library, where you use methods augmented on the Object and Array prototype, and use named parameters on the method calls. Optional "advanced use" mode. 247 | * @param {Object} Object - Pass in `Object` 248 | * @param {Array} Array - Pass in `Array` 249 | * @example 250 | dottie.augment(Object, Array); 251 | const path = 'path.to.key'; 252 | // now can use methods on objects and arrays 253 | const obj = {}.dottie.set({path, value: 'value'}); 254 | const arr = [].dottie.jsonsToRows({jsons: [{'key': 'value'}]}); 255 | Logger.log(obj.dottie.get({path})); // 'value' 256 | */ 257 | function augment (Object, Array) { 258 | const {Dottie} = Import; 259 | Dottie.augment(Object, Array); 260 | } 261 | -------------------------------------------------------------------------------- /src/scripts/Exports.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Returns the library itself; only useful for internal purposes such as testing 3 | * @ignore 4 | * @returns {DottieLib} 5 | */ 6 | function lib() { 7 | const {Dottie} = Import; 8 | return Dottie; 9 | } 10 | 11 | 12 | /** 13 | * Sets `value` at the location in `obj` as determined by `path` 14 | * @param {Object} obj 15 | * @param {String} path 16 | * @param {Any} value 17 | * @returns {Object} 18 | * @example 19 | const path = 'path.to.key'; 20 | let obj = {}; 21 | obj = dottie.set(obj, path, 'value'); 22 | Logger.log(obj.path.to.key); // 'value' 23 | */ 24 | function set(obj, path, value) { 25 | const {Dottie} = Import; 26 | return Dottie.set({path, value, obj}); 27 | } 28 | 29 | 30 | /** 31 | * Returns the value at location indicated by `path` of `obj` 32 | * @param {Object} obj 33 | * @param {String} path 34 | * @return {Any} 35 | * @example 36 | const path = 'path.to.key'; 37 | let obj = {}; 38 | obj = dottie.set(obj, path, 'value'); 39 | const result = dottie.get(obj, path); 40 | Logger.log(result); // 'value' 41 | */ 42 | function get(obj, path) { 43 | const {Dottie} = Import; 44 | return Dottie.get({path, obj}); 45 | } 46 | 47 | 48 | /** 49 | * Move a property within one object to another location. In-place operation, returns obj. If sourcePath is undefined, nothing changed 50 | * @param {Object} obj 51 | * @param {String} sourcePath 52 | * @param {String} destPath 53 | * @return {Object} 54 | * @example 55 | const path = 'path.to.key'; 56 | const diffPath = 'a.different.path.to.key'; 57 | let obj = {}; 58 | obj = dottie.set(obj, path, 'value'); 59 | obj = dottie.move(obj, path, diffPath); 60 | const first = dottie.get(obj, path); 61 | const second = dottie.get(obj, diffPath); 62 | Logger.log(first); // undefined 63 | Logger.log(second); // 'value' 64 | */ 65 | function move(obj, sourcePath, destPath) { 66 | const {Dottie} = Import; 67 | return Dottie.move({sourcePath, destPath, obj}) 68 | } 69 | 70 | 71 | /** 72 | * Copy property from one object to another object. If sourcePath is undefined, nothing changed. It returns the destination object, but the operation is in-place. 73 | * @param {Object} obj 74 | * @param {String} sourcePath 75 | * @param {Object} target 76 | * @param {String} destPath 77 | * @returns {Object} 78 | * @example 79 | const path = 'path.to.key'; 80 | const source = dottie.set({}, path, 'value'); 81 | const dest = {}; 82 | dottie.copy(source, path, dest, 'new.path.to.key'); 83 | const present = dottie.get(source, path); 84 | const value = dottie.get(dest, path); 85 | Logger.log(present); // undefined 86 | Logger.log(value); // 'value' 87 | */ 88 | function copy (obj, sourcePath, target, destPath) { 89 | const {Dottie} = Import; 90 | return Dottie.copy({sourcePath, destPath, obj, target}); 91 | } 92 | 93 | 94 | /** 95 | * Transfer property from one object to another. Removes from sourceObject. If sourcePath is undefined, nothing happens. Same as copy except does not remove from source. 96 | * @param {Object} obj 97 | * @param {String} sourcePath 98 | * @param {Object} target 99 | * @param {String} destPath 100 | * @return {Object} 101 | * @example 102 | const path = 'path.to.key'; 103 | const source = dottie.set({}, path, 'value'); 104 | const dest = {}; 105 | dottie.copy(source, dest, path, 'new.path.to.key'); 106 | const present = dottie.get(source, path); 107 | const value = dottie.get(dest, path); 108 | Logger.log(present); // undefined 109 | Logger.log(value); // 'value' 110 | */ 111 | function transfer (obj, sourcePath, target, destPath) { 112 | const {Dottie} = Import; 113 | return Dottie.transfer({sourcePath, destPath, obj, target}); 114 | } 115 | 116 | 117 | /** 118 | * Converts an object with dotted-key/value pairs to it's expanded/normal version 119 | * @param {Object} obj 120 | * @return {Object} 121 | */ 122 | function expand (obj) { 123 | const {Dottie} = Import; 124 | return Dottie.expand({obj}); 125 | } 126 | 127 | 128 | /** 129 | * Remove a value using dot notation (and keep array indexes) 130 | * @param {Object} obj 131 | * @param {String} path 132 | * @return {Any} 133 | */ 134 | function remove(obj, path) { 135 | const {Dottie} = Import; 136 | return Dottie.remove({obj, path}); 137 | } 138 | 139 | 140 | /** 141 | * Delete a value using dot notation (and adjust array indexes) 142 | * @param {Object} obj 143 | * @param {Object} path 144 | * @return {Object} 145 | */ 146 | function delete_(obj, path) { 147 | const {Dottie} = Import; 148 | return Dottie.delete_({obj, path}); 149 | } 150 | 151 | 152 | /** 153 | * Transform properties 154 | * @param {Object} obj 155 | * @param {Object} recipe 156 | * return {Object} 157 | */ 158 | function transform(obj, recipe) { 159 | const {Dottie} = Import; 160 | return Dottie.transform({recipe, obj}); 161 | } 162 | 163 | 164 | /** 165 | * Convert object to dotted-key/value pair 166 | * @param {Object} obj 167 | * @return {Object} 168 | */ 169 | function dot(obj) { 170 | const {Dottie} = Import; 171 | return Dottie.dot({obj}); 172 | } 173 | 174 | 175 | /** 176 | * Convert an array of jsons to a 2d array that can be used to populate a Google Spreadsheet with unique headers. The retuned object's first element is an array of header values whose string values are derived via path notation, and any nested objects or arrays follow the naming convention provided by dots (for objects) and brackets (for arrays). 177 | * @param {Object[]} jsons - An array of objects, which can contain native values or nested objects with native values 178 | * @param {Array} [priorityHeaders=[]] - A list of headers you want to appear first as the headers. Note, if header does not appear in any row, it will not appear at all even though you've specified it as priority. 179 | * @param {Boolean} [deleteNulls=false] - If true, do not include any columns whose values are null 180 | * @param {Boolean} [deleteEmptyArrays=true] - If true, make sure that columns that have empty arrays are not shown as null fields 181 | * @return {Array[]} 182 | * @example 183 | // simple example 184 | const jsons = [{ 185 | obj: { 186 | key: 'value' 187 | }, 188 | arr: [1, 2] 189 | }]; 190 | const result = dottie.jsonsToRows(jsons); 191 | Logger.log(result); 192 | // [ ['obj.key', 'arr[0]', 'arry[1]'], 193 | // ['value', 1, 2] 194 | * @example 195 | // example with null objects and empty arrays 196 | const jsons = [{ 197 | obj: { 198 | key: null 199 | }, 200 | arr: [] 201 | }, true, true]; 202 | const result = dottie.jsonsToRows(jsons); 203 | Logger.log(result); 204 | // [ 205 | // ['obj.key'], 206 | // [null] 207 | // ] 208 | */ 209 | function jsonsToRows (jsons, priorityHeaders=[], deleteNulls=false, deleteEmptyArrays=true) { 210 | const {Dottie} = Import; 211 | return Dottie.jsonsToRows({jsons, priorityHeaders, deleteNulls, deleteEmptyArrays}); 212 | } 213 | 214 | 215 | /** 216 | * 217 | * @param {Array[]} rows - first item is array string headers in dot notation, each subsequent row represents an item whose values coorespond to the header's path 218 | * @return {Object[]} 219 | * @example 220 | const headers = ['one', 'two.value', 'arr[0].name']; 221 | const rows = [ 222 | 1, 2, 'Name' 223 | ]; 224 | const jsons = dottie.rowsToJsons([headers, ...rows]); 225 | Logger.log(jsons); 226 | // [{ 227 | one: 1, 228 | two: { 229 | value: 2 230 | }, 231 | arr: [ 232 | { 233 | name: 'Name' 234 | } 235 | ] 236 | }] 237 | */ 238 | 239 | function rowsToJsons(rows) { 240 | const {Dottie} = Import; 241 | return Dottie.rowsToJsons({rows}); 242 | } 243 | 244 | 245 | /** 246 | * Alternative way to utilize library, where you use methods augmented on the Object and Array prototype, and use named parameters on the method calls. Optional "advanced use" mode. 247 | * @param {Object} Object - Pass in `Object` 248 | * @param {Array} Array - Pass in `Array` 249 | * @example 250 | dottie.augment(Object, Array); 251 | const path = 'path.to.key'; 252 | // now can use methods on objects and arrays 253 | const obj = {}.dottie.set({path, value: 'value'}); 254 | const arr = [].dottie.jsonsToRows({jsons: [{'key': 'value'}]}); 255 | Logger.log(obj.dottie.get({path})); // 'value' 256 | */ 257 | function augment (Object, Array) { 258 | const {Dottie} = Import; 259 | Dottie.augment(Object, Array); 260 | } 261 | -------------------------------------------------------------------------------- /docs/Code.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Code.js - Documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 49 | 50 | 51 | 52 |
53 | 69 |
70 | 71 | 80 | 81 |
82 | 83 |

84 | Code.js 85 |

86 | 87 | 88 | 89 | 90 | 91 |
92 |
93 |
/**
 94 |  * lib
 95 |  * @returns {DottieLib}
 96 |  */
 97 | function lib() {
 98 |   return Dottie;
 99 | }
100 | 
101 | 
102 | /**
103 |  * To convert manually per string use
104 |  * @param {Object} obj
105 |  * @param {String} path
106 |  * @param {Any} value
107 |  * @returns {Object}
108 |  */
109 | function set(obj, path, value) {
110 |   return Dottie.set({path, value, obj});
111 | }
112 | 
113 | 
114 | /**
115 |  * Picks a value from the object without removing it. Returns null if not present.
116 |  * @param {Object} obj
117 |  * @param {String} path
118 |  * @return {Any}
119 |  */
120 | function get(obj, path) {
121 |   const {Dottie} = Import;
122 |   return Dottie.get({path, obj});
123 | }
124 | 
125 | 
126 | /**
127 |  * Move a property within one object to another location. In-place operation, returns obj. If sourcePath is undefined, nothing changed
128 |  * @param {Object} obj
129 |  * @param {String} sourcePath
130 |  * @param {String} destPath
131 |  * @return {Object}
132 |  */
133 | function move(obj, sourcePath, destPath) {
134 |   const {Dottie} = Import;
135 |   return Dottie.move({sourcePath, destPath, obj})
136 | }
137 | 
138 | 
139 | /**
140 |  * Copy property from one object to another object. If sourcePath is undefined, nothing changed
141 |  * @param {Object} obj
142 |  * @param {String} sourcePath
143 |  * @param {Object} destObject
144 |  * @param {String} destPath
145 |  * @returns {Object}
146 |  */
147 | function copy (obj, sourcePath, destObject, destPath) {
148 |   const {Dottie} = Import;
149 |   return Dottie.copy({sourcePath, destPath, obj, destObject});
150 | }
151 | 
152 | 
153 | /**
154 |  * Transfer property from one object to another. Removes from sourceObject. If sourcePath is undefined, nothing happens
155 |  * @param {Object} obj
156 |  * @param {String} sourcePath
157 |  * @param {Object} target
158 |  * @param {String} destPath
159 |  * @return {Object}
160 |  */
161 | function transfer (obj, sourcePath, target, destPath) {
162 |   const {Dottie} = Import;
163 |   return Dottie.transfer({sourcePath, destPath, obj, target});
164 | }
165 | 
166 | 
167 | /**
168 |  * Converts an object with dotted-key/value pairs to it's expanded/normal version
169 |  * @param {Object} obj
170 |  * @return {Object}
171 |  */
172 | function expand (obj) {
173 |   const {Dottie} = Import;
174 |   return Dottie.expand({obj});
175 | }
176 | 
177 | 
178 | /**
179 |  * Remove a value using dot notation (and keep array indexes)
180 |  * @param {Object} obj
181 |  * @param {String} path
182 |  * @return {Any}
183 |  */
184 | function remove(obj, path) {
185 |   const {Dottie} = Import;
186 |   return Dottie.remove({obj, path});
187 | }
188 | 
189 | 
190 | /**
191 |  * Delete a value using dot notation (and adjust array indexes)
192 |  * @param {Object} obj
193 |  * @param {Object} path
194 |  * @return {Object}
195 |  */
196 | function delete_(obj, path) {
197 |   const {Dottie} = Import;
198 |   return Dottie.delete_({obj, path});
199 | }
200 | 
201 | 
202 | /**
203 |  * Transform properties
204 |  * @param {Object} obj
205 |  * @param {Object} recipe
206 |  * return {Object}
207 |  */
208 | function transform(obj, recipe) {
209 |   const {Dottie} = Import;
210 |   return Dottie.transform({recipe, obj});
211 | }
212 | 
213 | 
214 | /**
215 |  * Convert object to dotted-key/value pair
216 |  * @param {Object} obj
217 |  * @return {Object}
218 |  */
219 | function dot(obj) {
220 |   const {Dottie} = Import;
221 |   return Dottie.dot({obj});
222 | }
223 | 
224 | 
225 | /**
226 |  * Convert an array of jsons to 2d array
227 |  * @param {Object[]} jsons
228 |  * @return {Array[]}
229 |  */
230 | function jsonsToRows (jsons) {
231 |   const {Dottie} = Import;
232 |   return Dottie.jsonsToRows({jsons});
233 | }
234 | 
235 | 
236 | /**
237 |  *
238 |  * @param {Array[]} rows
239 |  * @return {Object[]}
240 |  */
241 | function rowsToJsons(rows) {
242 |   const {Dottie} = Import;
243 |   return Dottie.rowsToJsons({rows});
244 | }
245 | 
246 | 
247 | /**
248 |  * Add dottie namespace to Object and Array prototypes; advanced usage
249 |  * @param {Object} Object
250 |  * @param {Array} Array
251 |  */
252 | function augment (Object, Array) {
253 |   const {Dottie} = Import;
254 |   Dottie.augment(Object, Array);
255 | }
256 | 
257 |
258 |
259 | 260 | 261 | 262 | 263 |
264 | 265 |
266 | 267 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | -------------------------------------------------------------------------------- /tests/test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import {Dottie} from '../src/modules/Dottie.js'; 3 | 4 | let obj = {}; 5 | let path, value; 6 | 7 | test("Dottie.set", t => { 8 | path = 'path.to.value'; 9 | Dottie.set({obj, path, value: 100}); 10 | t.true(obj.path.to.value == 100); 11 | path = 'path.to.array[1].name'; 12 | Dottie.set({obj, path, value: 'name'}); 13 | t.true(obj.path.to.array[1].name == 'name'); 14 | }); 15 | 16 | test("Dottie.get", t => { 17 | path = 'path.to.value'; 18 | Dottie.set({obj, path, value: 100}); 19 | value = Dottie.get({obj, path}); 20 | t.true(value == 100); 21 | path = 'not.present'; 22 | value = Dottie.get({obj, path}); 23 | t.true(value === null); 24 | }); 25 | 26 | test("Dottie.move", t => { 27 | obj = {}; 28 | path = 'from.this'; 29 | Dottie.set({obj, path, value: 'value'}); 30 | let ppath = 'to.this'; 31 | Dottie.move({obj, sourcePath: path, destPath: ppath}); 32 | value = Dottie.get({obj, path:ppath}); 33 | t.true(value == 'value'); 34 | }); 35 | 36 | test("Dottie.copy", t => { 37 | obj = {}; 38 | let oobj = {}; 39 | path = 'path.to.value'; 40 | let ppath = 'different.path'; 41 | Dottie.set({obj, path, value: 300}); 42 | Dottie.copy({obj, sourcePath: path, destPath: ppath, target: oobj}); 43 | value = Dottie.get({obj: oobj, path: ppath}); 44 | t.true(value == 300); 45 | }); 46 | 47 | test("Dottie.transfer", t => { 48 | obj = {}; 49 | let oobj = {}; 50 | path = 'path.to.value'; 51 | let ppath = 'different.path.to.value'; 52 | Dottie.set({obj, path, value: 100}); 53 | Dottie.transfer({obj, sourcePath: path, target: oobj, destPath: ppath}); 54 | value = Dottie.get({obj, path}); 55 | let vvalue = Dottie.get({obj: oobj, path: ppath}); 56 | t.true(value === null); 57 | t.true(vvalue == 100); 58 | }); 59 | 60 | test("Dottie.expand", t => { 61 | obj = {'path.to.value': 100}; 62 | let result = Dottie.expand({obj}); 63 | t.true(result.path.to.value === 100); 64 | }); 65 | 66 | test("Dottie.delete", t => { 67 | obj = {}; 68 | path = 'path.to.value'; 69 | Dottie.set({obj, path, value: 100}); 70 | Dottie.delete({obj, path}); 71 | value = Dottie.get({obj, path}); 72 | t.true(value === null); 73 | }); 74 | 75 | test("Dottie.remove", t => { 76 | obj = {}; 77 | Dottie.set({obj, path, value: 100}); 78 | Dottie.remove({obj, path}); 79 | value = Dottie.get({obj, path}); 80 | t.true(value == null); 81 | }); 82 | 83 | test("Dottie.transform", t => { 84 | obj = { 85 | "id": 1, 86 | "contact": { 87 | "firstName": "John", 88 | "lastName": "Doe", 89 | "email": "example@gmail.com", 90 | }, 91 | }; 92 | let recipe = { 93 | 'id': 'nr', 94 | 'contact.firstName': 'name.first', 95 | 'contact.lastName': 'name.last', 96 | 'contact.email': 'email' 97 | }; 98 | let result = Dottie.transform({obj, recipe}); 99 | t.true(result.name.first == 'John'); 100 | }); 101 | 102 | test("Dottie.dot", t => { 103 | obj = { 104 | id: 'my-id', 105 | nes: { ted: { value: true } }, 106 | other: { nested: { stuff: 5 } }, 107 | some: { array: ['A', 'B'] } 108 | }; 109 | let result = Dottie.dot({obj}); 110 | t.true(result["nes.ted.value"]); 111 | }); 112 | 113 | test("Dottie.jsonsToRows", t => { 114 | obj = {}; 115 | const jsons = [ 116 | {func: function hey() { return 'yo'; }, array: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'hi', 'i', 'k', 'l', 'm', 'n', 'o', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'hi', 'i', 'k', 'l', 'm', 'n', 'o'], one: {two: 2}}, 117 | {array: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'hi', 'i', 'k', 'l', 'm', 'n', 'o', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'hi', 'i', 'k', 'l', 'm', 'n', 'o'], one: {two: 2, three: 3}}, 118 | {another: 'one'} 119 | ]; 120 | const result = Dottie.jsonsToRows({jsons, priorityHeaders: []}); 121 | t.true(result.length == 4); 122 | }); 123 | 124 | test("Dottie.rowsToJsons", t => { 125 | const rows = [ 126 | [ 127 | 'another', 'array[0]', 'array[10]', 128 | 'array[11]', 'array[12]', 'array[13]', 129 | 'array[14]', 'array[15]', 'array[16]', 130 | 'array[17]', 'array[18]', 'array[19]', 131 | 'array[1]', 'array[20]', 'array[21]', 132 | 'array[22]', 'array[23]', 'array[24]', 133 | 'array[25]', 'array[26]', 'array[27]', 134 | 'array[2]', 'array[3]', 'array[4]', 135 | 'array[5]', 'array[6]', 'array[7]', 136 | 'array[8]', 'array[9]', 'func', 137 | 'one.three', 'one.two' 138 | ], 139 | [ 140 | null, 'a', 'l', 141 | 'm', 'n', 'o', 142 | 'a', 'b', 'c', 143 | 'd', 'e', 'f', 144 | 'b', 'g', 'hi', 145 | 'i', 'k', 'l', 146 | 'm', 'n', 'o', 147 | 'c', 'd', 'e', 148 | 'f', 'g', 'hi', 149 | 'i', 'k', '', 150 | null, 2 151 | ], 152 | [ 153 | null, 'a', 'l', 'm', 'n', 'o', 154 | 'a', 'b', 'c', 'd', 'e', 'f', 155 | 'b', 'g', 'hi', 'i', 'k', 'l', 156 | 'm', 'n', 'o', 'c', 'd', 'e', 157 | 'f', 'g', 'hi', 'i', 'k', null, 158 | 3, 2 159 | ], 160 | [ 161 | 'one', null, null, null, null, 162 | null, null, null, null, null, 163 | null, null, null, null, null, 164 | null, null, null, null, null, 165 | null, null, null, null, null, 166 | null, null, null, null, null, 167 | null, null 168 | ] 169 | ]; 170 | const result = Dottie.rowsToJsons({rows}); 171 | t.true(result[2].another == 'one'); 172 | }); 173 | 174 | test("Dottie.augment", t=> { 175 | const params = [ [Object, Array], [] ]; 176 | params.forEach(function (param) { 177 | Dottie.augment(...param); 178 | obj = {}; 179 | path = 'path.to.value'; 180 | obj.dottie.set({path, value: 100}); 181 | t.true(obj.path.to.value === 100); 182 | value = obj.dottie.get({path}); 183 | t.true(value == 100); 184 | obj.dottie.move({path, destPath: 'new.path'}); 185 | t.true(obj.new.path == 100); 186 | let target = {}; 187 | obj.dottie.copy({path: 'new.path', destPath: 'copied.path', target}); 188 | t.true(target.copied.path == 100); 189 | obj.dottie.transfer({path: 'new.path', destPath: 'transfered.to.here', target}); 190 | t.true(target.transfered.to.here === 100); 191 | obj = {'path.to.value': 100}; 192 | obj.dottie.expand(); 193 | t.true(obj.path.to.value === 100); 194 | obj.dottie.delete({path: 'path.to'}); 195 | t.true(Object.keys(obj.path).length === 0); 196 | obj = {'path.to.value': 100}; 197 | obj.dottie.expand(); 198 | obj.dottie.remove({path: 'path.to'}); 199 | t.true(Object.keys(obj.path).length === 0); 200 | 201 | const jsons = [{'name.firstname': 'Adam', 'path.to.value': 100},{'name.firstname': 'Beth', 'path.to.value': 200}].map(json => json.dottie.expand()); 202 | const rows = jsons.dottie.jsonsToRows(); 203 | }); 204 | 205 | }); 206 | 207 | test("deleteNulls: true will delete nulls", t=> { 208 | 209 | obj = {}; 210 | const jsons = [ 211 | {array: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'hi', 'i', 'k', 'l', 'm', 'n', 'o', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'hi', 'i', 'k', 'l', 'm', 'n', 'o'], one: {two: null}, id: 5}, 212 | {array: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'hi', 'i', 'k', 'l', 'm', 'n', 'o', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'hi', 'i', 'k', 'l', 'm', 'n', 'o'], one: {two: 2, three: 3}, id: 2}, 213 | {another: 'one', one: null, id: 1} 214 | ]; 215 | const rows = Dottie.jsonsToRows({jsons, deleteNulls: true, priorityHeaders: ['id']}); 216 | t.true(rows[0][0] === 'id') 217 | t.true(!rows[0].includes('one')); 218 | 219 | }); 220 | 221 | test("eliminate empty lists", t=> { 222 | 223 | obj = {}; 224 | const jsons = [ 225 | {array: [], one: {two: null}, id: 5}, 226 | {array: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'hi', 'i', 'k', 'l', 'm', 'n', 'o', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'hi', 'i', 'k', 'l', 'm', 'n', 'o'], one: {two: 2, three: 3}, id: 2}, 227 | {another: 'one', one: null, id: 1} 228 | ]; 229 | const rows = Dottie.jsonsToRows({jsons, priorityHeaders: ['id']}); 230 | t.true(!rows[0].includes('array')); 231 | 232 | }); 233 | 234 | test("deleteNulls: false (default) will not have null values", t=> { 235 | 236 | obj = {}; 237 | const jsons = [ 238 | {array: [], one: {two: null}, id: 5}, 239 | {array: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'hi', 'i', 'k', 'l', 'm', 'n', 'o', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'hi', 'i', 'k', 'l', 'm', 'n', 'o'], one: {two: 2, three: 3}, id: 2}, 240 | {another: 'one', one: null, id: 1} 241 | ]; 242 | const rows = Dottie.jsonsToRows({jsons, priorityHeaders: ['id']}); 243 | t.true(rows[3][2] === null); 244 | }); 245 | 246 | test("keep empty lists when deleteEmptyArrays is false", t=> { 247 | 248 | obj = {}; 249 | const jsons = [ 250 | {array: [], one: {two: null}, id: 5}, 251 | {array: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'hi', 'i', 'k', 'l', 'm', 'n', 'o', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'hi', 'i', 'k', 'l', 'm', 'n', 'o'], one: {two: 2, three: 3}, id: 2}, 252 | {another: 'one', one: null, id: 1} 253 | ]; 254 | const rows = Dottie.jsonsToRows({jsons, priorityHeaders: ['id'], deleteEmptyArrays: false}); 255 | t.true(rows[0].includes('array')); 256 | }); 257 | 258 | test("false is represented", t=> { 259 | 260 | obj = {}; 261 | const jsons = [ 262 | {bool: true}, 263 | {bool: false} 264 | ]; 265 | const rows = Dottie.jsonsToRows({jsons, priorityHeaders: ['id']}); 266 | t.true(rows[2][0]===false); 267 | 268 | }); 269 | 270 | -------------------------------------------------------------------------------- /docs/scripts_Exports.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Source: scripts/Exports.js 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Source: scripts/Exports.js

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
/**
 30 |  * Returns the library itself; only useful for internal purposes such as testing
 31 |  * @ignore
 32 |  * @returns {DottieLib}
 33 |  */
 34 | function lib() {
 35 |   const {Dottie} = Import;
 36 |   return Dottie;
 37 | }
 38 | 
 39 | 
 40 | /**
 41 |  * Sets `value` at the location in `obj` as determined by `path`
 42 |  * @param {Object} obj
 43 |  * @param {String} path
 44 |  * @param {Any} value
 45 |  * @returns {Object}
 46 |  * @example
 47 | const path = 'path.to.key';
 48 | let obj = {};
 49 | obj = dottie.set(obj, path, 'value');
 50 | Logger.log(obj.path.to.key);  // 'value'
 51 |  */
 52 | function set(obj, path, value) {
 53 |   const {Dottie} = Import;
 54 |   return Dottie.set({path, value, obj});
 55 | }
 56 | 
 57 | 
 58 | /**
 59 |  * Returns the value at location indicated by `path` of `obj`
 60 |  * @param {Object} obj
 61 |  * @param {String} path
 62 |  * @return {Any}
 63 |  * @example
 64 | const path = 'path.to.key';
 65 | let obj = {};
 66 | obj = dottie.set(obj, path, 'value');
 67 | const result = dottie.get(obj, path);
 68 | Logger.log(result);  // 'value'
 69 |  */
 70 | function get(obj, path) {
 71 |   const {Dottie} = Import;
 72 |   return Dottie.get({path, obj});
 73 | }
 74 | 
 75 | 
 76 | /**
 77 |  * Move a property within one object to another location. In-place operation, returns obj. If sourcePath is undefined, nothing changed
 78 |  * @param {Object} obj
 79 |  * @param {String} sourcePath
 80 |  * @param {String} destPath
 81 |  * @return {Object}
 82 |  * @example
 83 | const path = 'path.to.key';
 84 | const diffPath = 'a.different.path.to.key';
 85 | let obj = {};
 86 | obj = dottie.set(obj, path, 'value');
 87 | obj = dottie.move(obj, path, diffPath);
 88 | const first = dottie.get(obj, path);
 89 | const second = dottie.get(obj, diffPath);
 90 | Logger.log(first);  // undefined
 91 | Logger.log(second);  // 'value'
 92 |  */
 93 | function move(obj, sourcePath, destPath) {
 94 |   const {Dottie} = Import;
 95 |   return Dottie.move({sourcePath, destPath, obj})
 96 | }
 97 | 
 98 | 
 99 | /**
100 |  * Copy property from one object to another object. If sourcePath is undefined, nothing changed. It returns the destination object, but the operation is in-place.
101 |  * @param {Object} obj
102 |  * @param {String} sourcePath
103 |  * @param {Object} target
104 |  * @param {String} destPath
105 |  * @returns {Object}
106 |  * @example
107 | const path = 'path.to.key';
108 | const source = dottie.set({}, path, 'value');
109 | const dest = {};
110 | dottie.copy(source, path, dest, 'new.path.to.key');
111 | const present = dottie.get(source, path);
112 | const value = dottie.get(dest, path);
113 | Logger.log(present);  // undefined
114 | Logger.log(value);  // 'value'
115 |  */
116 | function copy (obj, sourcePath, target, destPath) {
117 |   const {Dottie} = Import;
118 |   return Dottie.copy({sourcePath, destPath, obj, target});
119 | }
120 | 
121 | 
122 | /**
123 |  * Transfer property from one object to another. Removes from sourceObject. If sourcePath is undefined, nothing happens. Same as copy except does not remove from source.
124 |  * @param {Object} obj
125 |  * @param {String} sourcePath
126 |  * @param {Object} target
127 |  * @param {String} destPath
128 |  * @return {Object}
129 |  * @example
130 | const path = 'path.to.key';
131 | const source = dottie.set({}, path, 'value');
132 | const dest = {};
133 | dottie.copy(source, dest, path, 'new.path.to.key');
134 | const present = dottie.get(source, path);
135 | const value = dottie.get(dest, path);
136 | Logger.log(present);  // undefined
137 | Logger.log(value);  // 'value'
138 |  */
139 | function transfer (obj, sourcePath, target, destPath) {
140 |   const {Dottie} = Import;
141 |   return Dottie.transfer({sourcePath, destPath, obj, target});
142 | }
143 | 
144 | 
145 | /**
146 |  * Converts an object with dotted-key/value pairs to it's expanded/normal version
147 |  * @param {Object} obj
148 |  * @return {Object}
149 |  */
150 | function expand (obj) {
151 |   const {Dottie} = Import;
152 |   return Dottie.expand({obj});
153 | }
154 | 
155 | 
156 | /**
157 |  * Remove a value using dot notation (and keep array indexes)
158 |  * @param {Object} obj
159 |  * @param {String} path
160 |  * @return {Any}
161 |  */
162 | function remove(obj, path) {
163 |   const {Dottie} = Import;
164 |   return Dottie.remove({obj, path});
165 | }
166 | 
167 | 
168 | /**
169 |  * Delete a value using dot notation (and adjust array indexes)
170 |  * @param {Object} obj
171 |  * @param {Object} path
172 |  * @return {Object}
173 |  */
174 | function delete_(obj, path) {
175 |   const {Dottie} = Import;
176 |   return Dottie.delete_({obj, path});
177 | }
178 | 
179 | 
180 | /**
181 |  * Transform properties
182 |  * @param {Object} obj
183 |  * @param {Object} recipe
184 |  * return {Object}
185 |  */
186 | function transform(obj, recipe) {
187 |   const {Dottie} = Import;
188 |   return Dottie.transform({recipe, obj});
189 | }
190 | 
191 | 
192 | /**
193 |  * Convert object to dotted-key/value pair
194 |  * @param {Object} obj
195 |  * @return {Object}
196 |  */
197 | function dot(obj) {
198 |   const {Dottie} = Import;
199 |   return Dottie.dot({obj});
200 | }
201 | 
202 | 
203 | /**
204 |  * Convert an array of jsons to a 2d array that can be used to populate a Google Spreadsheet with unique headers. The retuned object's first element is an array of header values whose string values are derived via path notation, and any nested objects or arrays follow the naming convention provided by dots (for objects) and brackets (for arrays).
205 |  * @param {Object[]} jsons - An array of objects, which can contain native values or nested objects with native values
206 |  * @param {Array} [priorityHeaders=[]] - A list of headers you want to appear first as the headers. Note, if header does not appear in any row, it will not appear at all even though you've specified it as priority.
207 |  * @param {Boolean} [deleteNulls=false] - If true, do not include any columns whose values are null
208 |  * @param {Boolean} [deleteEmptyArrays=true] - If true, make sure that columns that have empty arrays are not shown as null fields
209 |  * @return {Array[]}
210 |  * @example
211 |  // simple example
212 |  const jsons = [{
213 |    obj: {
214 |      key: 'value'
215 |    },
216 |    arr: [1, 2]
217 |  }];
218 |  const result = dottie.jsonsToRows(jsons);
219 |  Logger.log(result);
220 |  // [ ['obj.key', 'arr[0]', 'arry[1]'],
221 |  //   ['value',   1,        2]
222 |   * @example
223 |  // example with null objects and empty arrays
224 |  const jsons = [{
225 |    obj: {
226 |      key: null
227 |    },
228 |    arr: []
229 |  }, true, true];
230 |  const result = dottie.jsonsToRows(jsons);
231 |  Logger.log(result);
232 |  // [
233 |  //   ['obj.key'],
234 |  //   [null]
235 |  // ]
236 |  */
237 | function jsonsToRows (jsons, priorityHeaders=[], deleteNulls=false, deleteEmptyArrays=true) {
238 |   const {Dottie} = Import;
239 |   return Dottie.jsonsToRows({jsons, priorityHeaders, deleteNulls, deleteEmptyArrays});
240 | }
241 | 
242 | 
243 | /**
244 |  *
245 |  * @param {Array[]} rows - first item is array string headers in dot notation, each subsequent row represents an item whose values coorespond to the header's path
246 |  * @return {Object[]}
247 |  * @example
248 | const headers = ['one', 'two.value', 'arr[0].name'];
249 | const rows = [
250 |   1,   2,   'Name'
251 | ];
252 | const jsons = dottie.rowsToJsons([headers, ...rows]);
253 | Logger.log(jsons);
254 | // [{
255 |   one: 1,
256 |   two: {
257 |    value: 2
258 |   },
259 |   arr: [
260 |     {
261 |       name: 'Name'
262 |     }
263 |   ]
264 | }]
265 |  */
266 | 
267 | function rowsToJsons(rows) {
268 |   const {Dottie} = Import;
269 |   return Dottie.rowsToJsons({rows});
270 | }
271 | 
272 | 
273 | /**
274 |  * Alternative way to utilize library, where you use methods augmented on the Object and Array prototype, and use named parameters on the method calls. Optional "advanced use" mode.
275 |  * @param {Object} Object - Pass in `Object`
276 |  * @param {Array} Array - Pass in `Array`
277 |  * @example
278 | dottie.augment(Object, Array);
279 | const path = 'path.to.key';
280 | // now can use methods on objects and arrays
281 | const obj = {}.dottie.set({path, value: 'value'});
282 | const arr = [].dottie.jsonsToRows({jsons: [{'key': 'value'}]});
283 | Logger.log(obj.dottie.get({path}));  // 'value'
284 |  */
285 | function augment (Object, Array) {
286 |   const {Dottie} = Import;
287 |   Dottie.augment(Object, Array);
288 | }
289 | 
290 |
291 |
292 | 293 | 294 | 295 | 296 |
297 | 298 | 301 | 302 |
303 | 304 |
305 | Documentation generated by JSDoc 3.6.7 on Thu Jul 15 2021 20:35:52 GMT+0800 (Malaysia Time) 306 |
307 | 308 | 309 | 310 | 311 | 312 | -------------------------------------------------------------------------------- /docs/scripts/prettify/Apache-License-2.0.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/modules/Dottie.js: -------------------------------------------------------------------------------- 1 | import DotObject from 'dot-object'; 2 | 3 | function Interface_ (name='', params={}) { 4 | class I_ { 5 | constructor (n, p) { 6 | this.name = `dottie.${n}`; 7 | this.params = p; 8 | } 9 | 10 | get req() { 11 | throw Error(`Missing at least one required parameter for ${this.name}: ${Object.keys(this.params)}`); 12 | } 13 | 14 | extra(kwargs) { 15 | if (Object.entries(kwargs).length > 0) 16 | throw Error(`Extra parameters passed to ${this.name}: ${Object.entries(kwargs)}`); 17 | } 18 | 19 | typecheck(args) { 20 | // arguments is an object-like array, need to flatten it so that we represent it as viewed from function scope 21 | let argObj = {}; 22 | for (const prop in args) { 23 | argObj = {...argObj, ...args[prop]}; 24 | } 25 | 26 | // now that both have matching types, let's go 27 | for (const prop in this.params) { 28 | if (this.params[prop] === 'any') continue; // type of 'any' special meaning is to skip it 29 | if (argObj[prop] === undefined) continue; 30 | if (this.params[prop] === 'array') { 31 | if (!Array.isArray(argObj[prop])) throw new Error(`Type mismatch in ${this.name}: "${prop}". Expected array but got ${typeof(argObj)} instead`); 32 | } else if (typeof(argObj[prop]) !== this.params[prop]) { 33 | throw new Error(`Type mismatch in ${this.name}: "${prop}". Expected ${this.params[prop]} but got ${typeof(argObj[prop])} instead`); 34 | } 35 | } 36 | } 37 | } 38 | return new I_(name, params); 39 | } 40 | 41 | 42 | // create interfaces that allows us to enforce type checks 43 | const MoveI = Interface_('move', {sourcePath: 'string', destPath: 'string', obj: 'object'}); 44 | const CopyI = Interface_('copy', {obj: 'object', sourcePath: 'string', target: 'object', destPath: 'string'}); 45 | const TransferI = Interface_('transfer', {sourcePath: 'string', destPath: 'string', obj: 'object', target: 'object'}); 46 | const ExpandI = Interface_('expand', {obj: 'object'}); 47 | const GetI = Interface_('get', {path: 'string', obj: 'object'}); 48 | const SetI = Interface_('set', {path: 'string', obj: 'object', value: 'any'}); 49 | const DeleteI = Interface_('delete', {path: 'string', obj: 'object'}); 50 | const RemoveI = Interface_('remove', {path: 'string', obj: 'object'}); 51 | const TransformI = Interface_('transform', {recipe: 'object', obj: 'object'}); 52 | const DotI = Interface_('dot', {obj: 'object'}); 53 | const JsonsI = Interface_('jsons', {jsons: 'array', priorityHeaders: 'array', deleteNulls: 'boolean', deleteEmptyArrays: 'boolean'}); 54 | const RowsI = Interface_('rows', {rows: 'array'}); 55 | 56 | /** 57 | * NOTE: This is the advanced section, consider using global methods at left to get started. 58 | * 59 | * The global methods are just light wrappers for these methods. You can use these methods directly by calling `augment`, which augmented the `Object` and `Array` with a `dottie` namespace which bears these methods. 60 | * @class 61 | */ 62 | class Dottie { 63 | 64 | /** 65 | * No need to call this constructor 66 | */ 67 | constructor () { 68 | 69 | } 70 | 71 | /** 72 | * @see {@link move} 73 | * @param {Object} params 74 | * @param {String} params.sourcePath 75 | * @param {String} params.destPath 76 | * @param {Object} params.obj 77 | * @return {Object} 78 | */ 79 | static move ({sourcePath=MoveI.req, destPath=MoveI.req, obj=MoveI.req, ...kwargs}={}) { 80 | MoveI.extra(kwargs); 81 | MoveI.typecheck(arguments); 82 | return DotObject.move(sourcePath, destPath, obj); 83 | } 84 | 85 | /** 86 | * @see {@link copy} 87 | * @param {Object} params 88 | * @param {Object} params.obj 89 | * @param {String} params.sourcePath 90 | * @param {String} params.target 91 | * @param {String} params.destPath 92 | * @return {Object} 93 | */ 94 | static copy ({obj=CopyI.req, sourcePath=CopyI.req, target=CopyI.req, destPath=CopyI.req, ...kwargs}={}) { 95 | CopyI.extra(kwargs); 96 | CopyI.typecheck(arguments); 97 | return DotObject.copy(sourcePath, destPath, obj, target); 98 | } 99 | 100 | /** 101 | * @see {@link transfer} 102 | * @param {Object} params 103 | * @param {String} params.sourcePath 104 | * @param {String} params.destPath 105 | * @param {Object} params.obj 106 | * @param {Object} params.target 107 | * @return {Object} 108 | */ 109 | static transfer ({sourcePath=TransferI.req, destPath=TransferI.req, obj=TransferI.req, target=TransferI.req, ...kwargs}={}) { 110 | TransferI.extra(kwargs); 111 | TransferI.typecheck(arguments); 112 | return DotObject.transfer(sourcePath, destPath, obj, target); 113 | } 114 | 115 | /** 116 | * @see {@link expand} 117 | * @param {Object} params 118 | * @param {Object} params.obj 119 | * @returns {Object} 120 | */ 121 | static expand ({obj=ExpandI.req, ...kwargs}={}) { 122 | ExpandI.extra(kwargs); 123 | ExpandI.typecheck(arguments); 124 | return DotObject.object(obj); 125 | } 126 | 127 | /** 128 | * @see {@link get} 129 | * @param {Object} params 130 | * @param {String} params.path 131 | * @param {Object} params.obj 132 | * @returns {Any} 133 | */ 134 | static get ({path=GetI.req, obj=GetI.req, ...kwargs}={}) { 135 | GetI.extra(kwargs); 136 | GetI.typecheck(arguments); 137 | const result = DotObject.pick(path, obj); 138 | if (result === undefined) return null; 139 | return result; 140 | } 141 | 142 | /** 143 | * @see {@link set} 144 | * @param {Object} params 145 | * @param {String} params.path 146 | * @param {Any} params.value 147 | * @param {Object} params.obj 148 | * @returns {Object} 149 | */ 150 | static set ({path=SetI.req, value=SetI.req, obj=SetI.req, ...kwargs}={}) { 151 | SetI.extra(kwargs); 152 | SetI.typecheck(arguments); 153 | return DotObject.str(path, value, obj); 154 | } 155 | 156 | /** 157 | * @see {@link delete_} 158 | * @param {Object} params 159 | * @param {String} params.path 160 | * @param {Object} params.obj 161 | * @returns {Object} 162 | */ 163 | static delete ({path=DeleteI.req, obj=DeleteI.req, ...kwargs}={}) { 164 | DeleteI.extra(kwargs); 165 | DeleteI.typecheck(arguments); 166 | return DotObject.delete(path, obj); 167 | } 168 | 169 | /** 170 | * @see {@link remove} 171 | * @param {Object} params 172 | * @param {String} params.path 173 | * @param {Object} params.obj 174 | * @returns {Object} 175 | */ 176 | static remove ({path=RemoveI.req, obj=RemoveI.req, ...kwargs}={}) { 177 | RemoveI.extra(kwargs); 178 | RemoveI.typecheck(arguments); 179 | return DotObject.remove(path, obj); 180 | } 181 | 182 | /** 183 | * @see {@link set} 184 | * @param {Object} params 185 | * @param {String} params.path 186 | * @param {Any} params.value 187 | * @param {Object} params.obj 188 | * @returns {Object} 189 | */ 190 | static transform ({recipe=TransformI.req, obj=TransformI.req, ...kwargs}={}) { 191 | TransformI.extra(kwargs); 192 | TransformI.typecheck(arguments); 193 | return DotObject.transform(recipe, obj); 194 | } 195 | 196 | /** 197 | * @see {@link dot} 198 | * @param {Object} params 199 | * @param {Object} params.obj 200 | * @returns {Object} 201 | */ 202 | static dot ({obj=DotI.req, ...kwargs}={}) { 203 | DotI.extra(kwargs); 204 | DotI.typecheck(arguments); 205 | return DotObject.dot(obj); 206 | } 207 | 208 | /** 209 | * @see {@link jsonsToRows} 210 | * @param {Object} params 211 | * @param {Object[]} params.jsons 212 | * @param {String[]} [params.priorityHeaders=[]] 213 | * @param {Boolean} [params.deleteNulls=false] 214 | * @param {Boolean} [params.deleteEmptyArrays=true] 215 | * @returns {Array[]} 216 | */ 217 | static jsonsToRows ({jsons=JsonsI.req, priorityHeaders=[], deleteNulls=false, deleteEmptyArrays=true, ...kwargs}={}) { 218 | JsonsI.extra(kwargs); 219 | JsonsI.typecheck(arguments); 220 | const headers = []; 221 | const values = []; 222 | 223 | // save values as dotted objects and store headers 224 | // remove any nulls 225 | for (const json of jsons) { 226 | const value = DotObject.dot(json); 227 | for (const [k, v] of Object.entries(value)) { 228 | if ( (deleteNulls && v === null) || (deleteEmptyArrays && Array.isArray(v) && v.length===0) ) { 229 | delete value[k]; 230 | } 231 | } 232 | headers.push(Object.keys(value)); 233 | values.push(value); 234 | } 235 | 236 | // row1 will consist of unique header columns, sorted alphabetically with id first 237 | // use array generics for most efficient manner of doing this 238 | const row1 = [...new Set([].concat(...headers))]; 239 | row1.sort(); 240 | for (const [i, h] of priorityHeaders.entries()) { 241 | const idx = row1.indexOf(h); 242 | if (idx !== -1) { 243 | row1.splice(idx, 1); // remove 244 | row1.splice(i, 0, h); // insert at front 245 | } 246 | 247 | } 248 | 249 | // the rest of the rows consits of the values in each column, or null if not present 250 | const rows = values.map(value => row1.map(column => value[column] === false ? false : value[column] || null)); 251 | 252 | // concat the arrays efficiently for return 253 | return [row1, ...rows]; 254 | } 255 | 256 | /** 257 | * @see {@link rowsToJsons} 258 | * @param {Object} params 259 | * @param {Any[][]} params.rows 260 | * @returns {Any[]} 261 | */ 262 | static rowsToJsons ({rows=RowsI.req, ...kwargs}={}) { 263 | RowsI.extra(kwargs); 264 | RowsI.typecheck(arguments); 265 | const headers = rows[0]; 266 | const objects = []; 267 | 268 | // go through row 1 to end 269 | for (const row of rows.slice(1)) { 270 | const dotted = row.reduce( 271 | function (acc, value, idx) { 272 | // safeguard if for some reason empty string for column or null 273 | const column = headers[idx] || null; 274 | if (!column) return acc; 275 | 276 | // set and return 277 | acc[column] = value; 278 | return acc; 279 | }, {} 280 | ); 281 | const obj = DotObject.object(dotted); 282 | objects.push(obj); 283 | } 284 | return objects; 285 | } 286 | 287 | /* 288 | Sets up {}.dottie and [].dottie methods 289 | @param {Any} O - Pass your `Object` 290 | @param {Any} A - Pass your `Array` 291 | */ 292 | static augment (O=null, A=null) { 293 | if (O === null) O = Object; 294 | if (A === null) A = Array; 295 | const me = this; 296 | !A.prototype.dottie && Object.defineProperty(A.prototype, 'dottie', { 297 | get: function () { 298 | return { 299 | jsonsToRows: ({priorityHeaders=[], ...kwargs}={}) => { 300 | return me.jsonsToRows({jsons:this, priorityHeaders}); 301 | }, 302 | rowsToJsons: ({...kwargs}={}) => { 303 | return me.rowsToJsons({rows: this}); 304 | } 305 | }; 306 | } 307 | }); 308 | 309 | !O.prototype.dottie && Object.defineProperty(O.prototype, 'dottie', { 310 | get: function () { 311 | return { 312 | set: ({path, value}={}) => { 313 | return DotObject.str(path, value, this); 314 | }, 315 | get: ({path}={}) => { 316 | return DotObject.pick(path, this); 317 | }, 318 | move: ({path, destPath}={}) => { 319 | return DotObject.move(path, destPath, this); 320 | }, 321 | copy: ({path, destPath, target}={}) => { 322 | return DotObject.copy(path, destPath, this, target); 323 | }, 324 | transfer: ({path, destPath, target}={}) => { 325 | return DotObject.transfer(path, destPath, this, target); 326 | }, 327 | expand: () => { 328 | return DotObject.object(this); 329 | }, 330 | delete: ({path}={}) => { 331 | return DotObject.delete(path, this); 332 | }, 333 | remove: ({path}) => { 334 | return DotObject.remove(path, this); 335 | }, 336 | transform: ({recipe}={}) => { 337 | return DotObject.transform(recipe, this); 338 | }, 339 | dot: ({target, path}={}) => { 340 | return DotObject.dot(this, target, path); 341 | } 342 | }; 343 | } 344 | }); 345 | } 346 | 347 | } 348 | 349 | export {Dottie}; 350 | -------------------------------------------------------------------------------- /docs/scripts/prettify/prettify.js: -------------------------------------------------------------------------------- 1 | var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= 3 | [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), 9 | l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, 11 | q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, 12 | "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), 13 | a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} 14 | for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ 21 | I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), 22 | ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", 23 | /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), 24 | ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", 25 | hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= 26 | !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | scripts/Code.js - Documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 49 | 50 | 51 | 52 |
53 | 69 |
70 | 71 | 80 | 81 |
82 | 83 |

84 | scripts/Code.js 85 |

86 | 87 | 88 | 89 | 90 | 91 |
92 |
93 |
/**
 94 |  * Returns the library itself; only useful for internal purposes such as testing
 95 |  * @ignore
 96 |  * @returns {DottieLib}
 97 |  */
 98 | function lib() {
 99 |   const {Dottie} = Import;
100 |   return Dottie;
101 | }
102 | 
103 | 
104 | /**
105 |  * Sets `value` at the location in `obj` as determined by `path`
106 |  * @param {Object} obj
107 |  * @param {String} path
108 |  * @param {Any} value
109 |  * @returns {Object}
110 |  * @example
111 | const path = 'path.to.key';
112 | let obj = {};
113 | obj = dottie.set(obj, path, 'value');
114 | Logger.log(obj.path.to.key);  // 'value'
115 |  */
116 | function set(obj, path, value) {
117 |   const {Dottie} = Import;
118 |   return Dottie.set({path, value, obj});
119 | }
120 | 
121 | 
122 | /**
123 |  * Returns the value at location indicated by `path` of `obj`
124 |  * @param {Object} obj
125 |  * @param {String} path
126 |  * @return {Any}
127 |  * @example
128 | const path = 'path.to.key';
129 | let obj = {};
130 | obj = dottie.set(obj, path, 'value');
131 | const result = dottie.get(obj, path);
132 | Logger.log(result);  // 'value'
133 |  */
134 | function get(obj, path) {
135 |   const {Dottie} = Import;
136 |   return Dottie.get({path, obj});
137 | }
138 | 
139 | 
140 | /**
141 |  * Move a property within one object to another location. In-place operation, returns obj. If sourcePath is undefined, nothing changed
142 |  * @param {Object} obj
143 |  * @param {String} sourcePath
144 |  * @param {String} destPath
145 |  * @return {Object}
146 |  * @example
147 | const path = 'path.to.key';
148 | const diffPath = 'a.different.path.to.key';
149 | let obj = {};
150 | obj = dottie.set(obj, path, 'value');
151 | obj = dottie.move(obj, path, diffPath);
152 | const first = dottie.get(obj, path);
153 | const second = dottie.get(obj, diffPath);
154 | Logger.log(first);  // undefined
155 | Logger.log(second);  // 'value'
156 |  */
157 | function move(obj, sourcePath, destPath) {
158 |   const {Dottie} = Import;
159 |   return Dottie.move({sourcePath, destPath, obj})
160 | }
161 | 
162 | 
163 | /**
164 |  * Copy property from one object to another object. If sourcePath is undefined, nothing changed. It returns the destination object, but the operation is in-place.
165 |  * @param {Object} obj
166 |  * @param {String} sourcePath
167 |  * @param {Object} target
168 |  * @param {String} destPath
169 |  * @returns {Object}
170 |  * @example
171 | const path = 'path.to.key';
172 | const source = dottie.set({}, path, 'value');
173 | const dest = {};
174 | dottie.copy(source, path, dest, 'new.path.to.key');
175 | const present = dottie.get(source, path);
176 | const value = dottie.get(dest, path);
177 | Logger.log(present);  // undefined
178 | Logger.log(value);  // 'value'
179 |  */
180 | function copy (obj, sourcePath, target, destPath) {
181 |   const {Dottie} = Import;
182 |   return Dottie.copy({sourcePath, destPath, obj, target});
183 | }
184 | 
185 | 
186 | /**
187 |  * Transfer property from one object to another. Removes from sourceObject. If sourcePath is undefined, nothing happens. Same as copy except does not remove from source.
188 |  * @param {Object} obj
189 |  * @param {String} sourcePath
190 |  * @param {Object} target
191 |  * @param {String} destPath
192 |  * @return {Object}
193 |  * @example
194 | const path = 'path.to.key';
195 | const source = dottie.set({}, path, 'value');
196 | const dest = {};
197 | dottie.copy(source, dest, path, 'new.path.to.key');
198 | const present = dottie.get(source, path);
199 | const value = dottie.get(dest, path);
200 | Logger.log(present);  // undefined
201 | Logger.log(value);  // 'value'
202 |  */
203 | function transfer (obj, sourcePath, target, destPath) {
204 |   const {Dottie} = Import;
205 |   return Dottie.transfer({sourcePath, destPath, obj, target});
206 | }
207 | 
208 | 
209 | /**
210 |  * Converts an object with dotted-key/value pairs to it's expanded/normal version
211 |  * @param {Object} obj
212 |  * @return {Object}
213 |  */
214 | function expand (obj) {
215 |   const {Dottie} = Import;
216 |   return Dottie.expand({obj});
217 | }
218 | 
219 | 
220 | /**
221 |  * Remove a value using dot notation (and keep array indexes)
222 |  * @param {Object} obj
223 |  * @param {String} path
224 |  * @return {Any}
225 |  */
226 | function remove(obj, path) {
227 |   const {Dottie} = Import;
228 |   return Dottie.remove({obj, path});
229 | }
230 | 
231 | 
232 | /**
233 |  * Delete a value using dot notation (and adjust array indexes)
234 |  * @param {Object} obj
235 |  * @param {Object} path
236 |  * @return {Object}
237 |  */
238 | function delete_(obj, path) {
239 |   const {Dottie} = Import;
240 |   return Dottie.delete_({obj, path});
241 | }
242 | 
243 | 
244 | /**
245 |  * Transform properties
246 |  * @param {Object} obj
247 |  * @param {Object} recipe
248 |  * return {Object}
249 |  */
250 | function transform(obj, recipe) {
251 |   const {Dottie} = Import;
252 |   return Dottie.transform({recipe, obj});
253 | }
254 | 
255 | 
256 | /**
257 |  * Convert object to dotted-key/value pair
258 |  * @param {Object} obj
259 |  * @return {Object}
260 |  */
261 | function dot(obj) {
262 |   const {Dottie} = Import;
263 |   return Dottie.dot({obj});
264 | }
265 | 
266 | 
267 | /**
268 |  * Convert an array of jsons to a 2d array that can be used to populate a Google Spreadsheet with unique headers. The retuned object's first element are the header values whose string values are derived via path notation, and any nested objects or arrays follow the naming convention provided by dots (for objects) and brackets (for arrays).
269 |  * @param {Object[]} jsons - An array of objects, which can contain native values or nested objects with native values
270 |  * @param {Array} [priorityHeaders=[]] - A list of headers you want to appear first as the headers. Note, if header does not appear in any row, it will not appear at all even though you've specified it as priority.
271 |  * @param {Boolean} [deleteNulls=true]
272 |  * @param {Boolean} [deleteEmptyArrays=true]
273 |  * @return {Array[]}
274 |  * @example
275 |  const jsons = [{
276 |    obj: {
277 |      key: 'value'
278 |    },
279 |    arr: [1, 2]
280 |  }];
281 |  const result = dottie.jsonsToRows(jsons);
282 |  Logger.log(result);
283 |  // [ ['obj.key', 'arr[0]', 'arry[1]'],
284 |  //   ['value',   1,        2]
285 |  */
286 | function jsonsToRows (jsons, priorityHeaders=[], deleteNulls=true, deleteEmptyArrays=true) {
287 |   const {Dottie} = Import;
288 |   return Dottie.jsonsToRows({jsons, priorityHeaders, deleteNulls, deleteEmptyArrays});
289 | }
290 | 
291 | 
292 | /**
293 |  *
294 |  * @param {Array[]} rows
295 |  * @return {Object[]}
296 |  */
297 | function rowsToJsons(rows) {
298 |   const {Dottie} = Import;
299 |   return Dottie.rowsToJsons({rows, priorityHeaders});
300 | }
301 | 
302 | 
303 | /**
304 |  * Alternative way to utilize library, where you use methods augmented on the Object and Array prototype, and use named parameters on the method calls. Optional "advanced use" mode.
305 |  * @param {Object} Object - Pass in `Object`
306 |  * @param {Array} Array - Pass in `Array`
307 |  * @example
308 | dottie.augment(Object, Array);
309 | const path = 'path.to.key';
310 | // now can use methods on objects and arrays
311 | const obj = {}.dottie.set({path, value: 'value'});
312 | const arr = [].dottie.jsonsToRows({jsons: [{'key': 'value'}]});
313 | Logger.log(obj.dottie.get({path}));  // 'value'
314 |  */
315 | function augment (Object, Array) {
316 |   const {Dottie} = Import;
317 |   Dottie.augment(Object, Array);
318 | }
319 | 
320 |
321 |
322 | 323 | 324 | 325 | 326 |
327 | 328 |
329 | 330 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | -------------------------------------------------------------------------------- /docs/modules_Dottie.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSDoc: Source: modules/Dottie.js 6 | 7 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |

Source: modules/Dottie.js

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
import DotObject from 'dot-object';
 30 | 
 31 | function Interface_ (name='', params={}) {
 32 |   class I_ {
 33 |     constructor (n, p) {
 34 |       this.name = `dottie.${n}`;
 35 |       this.params = p;
 36 |     }
 37 | 
 38 |     get req() {
 39 |       throw Error(`Missing at least one required parameter for ${this.name}: ${Object.keys(this.params)}`);
 40 |     }
 41 | 
 42 |     extra(kwargs) {
 43 |       if (Object.entries(kwargs).length > 0)
 44 |         throw Error(`Extra parameters passed to ${this.name}: ${Object.entries(kwargs)}`);
 45 |     }
 46 | 
 47 |     typecheck(args) {
 48 |       // arguments is an object-like array, need to flatten it so that we represent it as viewed from function scope
 49 |       let argObj = {};
 50 |       for (const prop in args) {
 51 |         argObj = {...argObj, ...args[prop]};
 52 |       }
 53 | 
 54 |       // now that both have matching types, let's go
 55 |       for (const prop in this.params) {
 56 |         if (this.params[prop] === 'any') continue;  // type of 'any' special meaning is to skip it
 57 |         if (argObj[prop] === undefined) continue;
 58 |         if (this.params[prop] === 'array') {
 59 |           if (!Array.isArray(argObj[prop])) throw new Error(`Type mismatch in ${this.name}: "${prop}". Expected array but got ${typeof(argObj)} instead`);
 60 |         } else if (typeof(argObj[prop]) !== this.params[prop]) {
 61 |           throw new Error(`Type mismatch in ${this.name}: "${prop}". Expected ${this.params[prop]} but got ${typeof(argObj[prop])} instead`);
 62 |         }
 63 |       }
 64 |     }
 65 |   }
 66 |   return new I_(name, params);
 67 | }
 68 | 
 69 | 
 70 | // create interfaces that allows us to enforce type checks
 71 | const MoveI = Interface_('move', {sourcePath: 'string', destPath: 'string', obj: 'object'});
 72 | const CopyI = Interface_('copy', {obj: 'object', sourcePath: 'string', target: 'object', destPath: 'string'});
 73 | const TransferI = Interface_('transfer', {sourcePath: 'string', destPath: 'string', obj: 'object', target: 'object'});
 74 | const ExpandI = Interface_('expand', {obj: 'object'});
 75 | const GetI = Interface_('get', {path: 'string', obj: 'object'});
 76 | const SetI = Interface_('set', {path: 'string', obj: 'object', value: 'any'});
 77 | const DeleteI = Interface_('delete', {path: 'string', obj: 'object'});
 78 | const RemoveI = Interface_('remove', {path: 'string', obj: 'object'});
 79 | const TransformI = Interface_('transform', {recipe: 'object', obj: 'object'});
 80 | const DotI = Interface_('dot', {obj: 'object'});
 81 | const JsonsI = Interface_('jsons', {jsons: 'array', priorityHeaders: 'array', deleteNulls: 'boolean', deleteEmptyArrays: 'boolean'});
 82 | const RowsI = Interface_('rows', {rows: 'array'});
 83 | 
 84 | /**
 85 |  * NOTE: This is the advanced section, consider using global methods at left to get started.
 86 |  *
 87 |  * The global methods are just light wrappers for these methods. You can use these methods directly by calling `augment`, which augmented the `Object` and `Array` with a `dottie` namespace which bears these methods.
 88 |  * @class
 89 |  */
 90 | class Dottie {
 91 | 
 92 |   /**
 93 |    * No need to call this constructor
 94 |    */
 95 |   constructor () {
 96 | 
 97 |   }
 98 | 
 99 |   /**
100 |    * @see {@link move}
101 |    * @param {Object} params
102 |    * @param {String} params.sourcePath
103 |    * @param {String} params.destPath
104 |    * @param {Object} params.obj
105 |    * @return {Object}
106 |    */
107 |   static move ({sourcePath=MoveI.req, destPath=MoveI.req, obj=MoveI.req, ...kwargs}={}) {
108 |     MoveI.extra(kwargs);
109 |     MoveI.typecheck(arguments);
110 |     return DotObject.move(sourcePath, destPath, obj);
111 |   }
112 | 
113 |   /**
114 |    * @see {@link copy}
115 |    * @param {Object} params
116 |    * @param {Object} params.obj
117 |    * @param {String} params.sourcePath
118 |    * @param {String} params.target
119 |    * @param {String} params.destPath
120 |    * @return {Object}
121 |    */
122 |   static copy ({obj=CopyI.req, sourcePath=CopyI.req, target=CopyI.req, destPath=CopyI.req, ...kwargs}={}) {
123 |     CopyI.extra(kwargs);
124 |     CopyI.typecheck(arguments);
125 |     return DotObject.copy(sourcePath, destPath, obj, target);
126 |   }
127 | 
128 |   /**
129 |    * @see {@link transfer}
130 |    * @param {Object} params
131 |    * @param {String} params.sourcePath
132 |    * @param {String} params.destPath
133 |    * @param {Object} params.obj
134 |    * @param {Object} params.target
135 |    * @return {Object}
136 |    */
137 |   static transfer ({sourcePath=TransferI.req, destPath=TransferI.req, obj=TransferI.req, target=TransferI.req, ...kwargs}={}) {
138 |     TransferI.extra(kwargs);
139 |     TransferI.typecheck(arguments);
140 |     return DotObject.transfer(sourcePath, destPath, obj, target);
141 |   }
142 | 
143 |   /**
144 |    * @see {@link expand}
145 |    * @param {Object} params
146 |    * @param {Object} params.obj
147 |    * @returns {Object}
148 |    */
149 |   static expand ({obj=ExpandI.req, ...kwargs}={}) {
150 |     ExpandI.extra(kwargs);
151 |     ExpandI.typecheck(arguments);
152 |     return DotObject.object(obj);
153 |   }
154 | 
155 |   /**
156 |    * @see {@link get}
157 |    * @param {Object} params
158 |    * @param {String} params.path
159 |    * @param {Object} params.obj
160 |    * @returns {Any}
161 |    */
162 |   static get ({path=GetI.req, obj=GetI.req, ...kwargs}={}) {
163 |     GetI.extra(kwargs);
164 |     GetI.typecheck(arguments);
165 |     const result = DotObject.pick(path, obj);
166 |     if (result === undefined) return null;
167 |     return result;
168 |   }
169 | 
170 |   /**
171 |    * @see {@link set}
172 |    * @param {Object} params
173 |    * @param {String} params.path
174 |    * @param {Any} params.value
175 |    * @param {Object} params.obj
176 |    * @returns {Object}
177 |    */
178 |   static set ({path=SetI.req, value=SetI.req, obj=SetI.req, ...kwargs}={}) {
179 |     SetI.extra(kwargs);
180 |     SetI.typecheck(arguments);
181 |     return DotObject.str(path, value, obj);
182 |   }
183 | 
184 |   /**
185 |    * @see {@link delete_}
186 |    * @param {Object} params
187 |    * @param {String} params.path
188 |    * @param {Object} params.obj
189 |    * @returns {Object}
190 |    */
191 |   static delete ({path=DeleteI.req, obj=DeleteI.req, ...kwargs}={}) {
192 |     DeleteI.extra(kwargs);
193 |     DeleteI.typecheck(arguments);
194 |     return DotObject.delete(path, obj);
195 |   }
196 | 
197 |   /**
198 |    * @see {@link remove}
199 |    * @param {Object} params
200 |    * @param {String} params.path
201 |    * @param {Object} params.obj
202 |    * @returns {Object}
203 |    */
204 |   static remove ({path=RemoveI.req, obj=RemoveI.req, ...kwargs}={}) {
205 |     RemoveI.extra(kwargs);
206 |     RemoveI.typecheck(arguments);
207 |     return DotObject.remove(path, obj);
208 |   }
209 | 
210 |   /**
211 |    * @see {@link set}
212 |    * @param {Object} params
213 |    * @param {String} params.path
214 |    * @param {Any} params.value
215 |    * @param {Object} params.obj
216 |    * @returns {Object}
217 |    */
218 |   static transform ({recipe=TransformI.req, obj=TransformI.req, ...kwargs}={}) {
219 |     TransformI.extra(kwargs);
220 |     TransformI.typecheck(arguments);
221 |     return DotObject.transform(recipe, obj);
222 |   }
223 | 
224 |   /**
225 |    * @see {@link dot}
226 |    * @param {Object} params
227 |    * @param {Object} params.obj
228 |    * @returns {Object}
229 |    */
230 |   static dot ({obj=DotI.req, ...kwargs}={}) {
231 |     DotI.extra(kwargs);
232 |     DotI.typecheck(arguments);
233 |     return DotObject.dot(obj);
234 |   }
235 | 
236 |   /**
237 |    * @see {@link jsonsToRows}
238 |    * @param {Object} params
239 |    * @param {Object[]} params.jsons
240 |    * @param {String[]} [params.priorityHeaders=[]]
241 |    * @param {Boolean} [params.deleteNulls=false]
242 |    * @param {Boolean} [params.deleteEmptyArrays=true]
243 |    * @returns {Array[]}
244 |    */
245 |   static jsonsToRows ({jsons=JsonsI.req, priorityHeaders=[], deleteNulls=false, deleteEmptyArrays=true, ...kwargs}={}) {
246 |     JsonsI.extra(kwargs);
247 |     JsonsI.typecheck(arguments);
248 |     const headers = [];
249 |     const values = [];
250 | 
251 |     // save values as dotted objects and store headers
252 |     // remove any nulls
253 |     for (const json of jsons) {
254 |       const value = DotObject.dot(json);
255 |       for (const [k, v] of Object.entries(value)) {
256 |         if ( (deleteNulls && v === null) || (deleteEmptyArrays && Array.isArray(v) && v.length===0) ) {
257 |           delete value[k];
258 |         }
259 |       }
260 |       headers.push(Object.keys(value));
261 |       values.push(value);
262 |     }
263 | 
264 |     // row1 will consist of unique header columns, sorted alphabetically with id first
265 |     // use array generics for most efficient manner of doing this
266 |     const row1 = [...new Set([].concat(...headers))];
267 |     row1.sort();
268 |     for (const [i, h] of priorityHeaders.entries()) {
269 |       const idx = row1.indexOf(h);
270 |       if (idx !== -1) {
271 |         row1.splice(idx, 1);      // remove
272 |         row1.splice(i, 0, h);  // insert at front
273 |       }
274 | 
275 |     }
276 | 
277 |     // the rest of the rows consits of the values in each column, or null if not present
278 |     const rows = values.map(value => row1.map(column => value[column] === false ? false : value[column] || null));
279 | 
280 |     // concat the arrays efficiently for return
281 |     return [row1, ...rows];
282 |   }
283 | 
284 |   /**
285 |    * @see {@link rowsToJsons}
286 |    * @param {Object} params
287 |    * @param {Any[][]} params.rows
288 |    * @returns {Any[]}
289 |    */
290 |   static rowsToJsons ({rows=RowsI.req, ...kwargs}={}) {
291 |     RowsI.extra(kwargs);
292 |     RowsI.typecheck(arguments);
293 |     const headers = rows[0];
294 |     const objects = [];
295 | 
296 |     // go through row 1 to end
297 |     for (const row of rows.slice(1)) {
298 |       const dotted = row.reduce(
299 |         function (acc, value, idx) {
300 |           // safeguard if for some reason empty string for column or null
301 |           const column = headers[idx] || null;
302 |           if (!column) return acc;
303 | 
304 |           // set and return
305 |           acc[column] = value;
306 |           return acc;
307 |         }, {}
308 |       );
309 |       const obj = DotObject.object(dotted);
310 |       objects.push(obj);
311 |     }
312 |     return objects;
313 |   }
314 | 
315 |   /*
316 |     Sets up {}.dottie and [].dottie methods
317 |     @param {Any} O - Pass your `Object`
318 |     @param {Any} A - Pass your `Array`
319 |    */
320 |   static augment (O=null, A=null) {
321 |     if (O === null) O = Object;
322 |     if (A === null) A = Array;
323 |     const me = this;
324 |     !A.prototype.dottie && Object.defineProperty(A.prototype, 'dottie', {
325 |       get: function () {
326 |         return {
327 |           jsonsToRows: ({priorityHeaders=[], ...kwargs}={}) => {
328 |             return me.jsonsToRows({jsons:this, priorityHeaders});
329 |           },
330 |           rowsToJsons: ({...kwargs}={}) => {
331 |             return me.rowsToJsons({rows: this});
332 |           }
333 |         };
334 |       }
335 |     });
336 | 
337 |     !O.prototype.dottie && Object.defineProperty(O.prototype, 'dottie', {
338 |       get: function () {
339 |         return {
340 |           set: ({path, value}={}) => {
341 |             return DotObject.str(path, value, this);
342 |           },
343 |           get: ({path}={}) => {
344 |             return DotObject.pick(path, this);
345 |           },
346 |           move: ({path, destPath}={}) => {
347 |             return DotObject.move(path, destPath, this);
348 |           },
349 |           copy: ({path, destPath, target}={}) => {
350 |             return DotObject.copy(path, destPath, this, target);
351 |           },
352 |           transfer: ({path, destPath, target}={}) => {
353 |             return DotObject.transfer(path, destPath, this, target);
354 |           },
355 |           expand: () => {
356 |             return DotObject.object(this);
357 |           },
358 |           delete: ({path}={}) => {
359 |             return DotObject.delete(path, this);
360 |           },
361 |           remove: ({path}) => {
362 |             return DotObject.remove(path, this);
363 |           },
364 |           transform: ({recipe}={}) => {
365 |             return DotObject.transform(recipe, this);
366 |           },
367 |           dot: ({target, path}={}) => {
368 |             return DotObject.dot(this, target, path);
369 |           }
370 |         };
371 |       }
372 |     });
373 |   }
374 | 
375 | }
376 | 
377 | export {Dottie};
378 | 
379 |
380 |
381 | 382 | 383 | 384 | 385 |
386 | 387 | 390 | 391 |
392 | 393 |
394 | Documentation generated by JSDoc 3.6.7 on Thu Jul 15 2021 20:35:52 GMT+0800 (Malaysia Time) 395 |
396 | 397 | 398 | 399 | 400 | 401 | -------------------------------------------------------------------------------- /docs/Dottie.js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Dottie.js - Documentation 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 49 | 50 | 51 | 52 |
53 | 69 |
70 | 71 | 80 | 81 |
82 | 83 |

84 | Dottie.js 85 |

86 | 87 | 88 | 89 | 90 | 91 |
92 |
93 |
import DotObject from 'dot-object';
 94 | 
 95 | function Interface_ (name='', params={}) {
 96 |   class I_ {
 97 |     constructor (n, p) {
 98 |       this.name = `dottie.${n}`;
 99 |       this.params = p;
100 |     }
101 | 
102 |     get req() {
103 |       throw Error(`Missing at least one required parameter for ${this.name}: ${Object.keys(this.params)}`);
104 |     }
105 | 
106 |     extra(kwargs) {
107 |       if (Object.entries(kwargs).length > 0)
108 |         throw Error(`Extra parameters passed to ${this.name}: ${Object.entries(kwargs)}`);
109 |     }
110 | 
111 |     typecheck(args) {
112 |       // arguments is an object-like array, need to flatten it so that we represent it as viewed from function scope
113 |       let argObj = {};
114 |       for (const prop in args) {
115 |         argObj = {...argObj, ...args[prop]};
116 |       }
117 | 
118 |       // now that both have matching types, let's go
119 |       for (const prop in this.params) {
120 |         if (this.params[prop] === 'any') continue;  // type of 'any' special meaning is to skip it
121 |         if (argObj[prop] === undefined) continue;
122 |         if (this.params[prop] === 'array') {
123 |           if (!Array.isArray(argObj[prop])) throw new Error(`Type mismatch in ${this.name}: "${prop}". Expected array but got ${typeof(argObj)} instead`);
124 |         } else if (typeof(argObj[prop]) !== this.params[prop]) {
125 |           throw new Error(`Type mismatch in ${this.name}: "${prop}". Expected ${this.params[prop]} but got ${typeof(argObj[prop])} instead`);
126 |         }
127 |       }
128 |     }
129 |   }
130 |   return new I_(name, params);
131 | }
132 | 
133 | 
134 | // create interfaces that allows us to enforce type checks
135 | const MoveI = Interface_('move', {sourcePath: 'string', destPath: 'string', obj: 'object'});
136 | const CopyI = Interface_('copy', {obj: 'object', sourcePath: 'string', target: 'object', destPath: 'string'});
137 | const TransferI = Interface_('transfer', {sourcePath: 'string', destPath: 'string', obj: 'object', target: 'object'});
138 | const ExpandI = Interface_('expand', {obj: 'object'});
139 | const GetI = Interface_('get', {path: 'string', obj: 'object'});
140 | const SetI = Interface_('set', {path: 'string', obj: 'object', value: 'any'});
141 | const DeleteI = Interface_('delete', {path: 'string', obj: 'object'});
142 | const RemoveI = Interface_('remove', {path: 'string', obj: 'object'});
143 | const TransformI = Interface_('transform', {recipe: 'object', obj: 'object'});
144 | const DotI = Interface_('dot', {obj: 'object'});
145 | const JsonsI = Interface_('jsons', {jsons: 'array', priorityHeaders: 'array', deleteNulls: 'boolean', deleteEmptyArrays: 'boolean'});
146 | const RowsI = Interface_('rows', {rows: 'array'});
147 | 
148 | /**
149 |  * Internal class object. Global methods interface with these methods. If `augment` is used, these methods are augmented to Objects and Arrays with these function signatures.
150 |  * @class
151 |  */
152 | class Dottie {
153 | 
154 |   /**
155 |    * No instances are made, all these methods are static
156 |    */
157 |   constructor () {
158 | 
159 |   }
160 | 
161 |   /**
162 |    * @see {@link move}
163 |    * @param {Object} namedParameters
164 |    * @param {String} namedParameters.sourcePath
165 |    * @param {String} namedParameters.destPath
166 |    * @param {Object} namedParameters.obj
167 |    * @return {Object}
168 |    */
169 |   static move ({sourcePath=MoveI.req, destPath=MoveI.req, obj=MoveI.req, ...kwargs}={}) {
170 |     MoveI.extra(kwargs);
171 |     MoveI.typecheck(arguments);
172 |     return DotObject.move(sourcePath, destPath, obj);
173 |   }
174 | 
175 |   /**
176 |    * @see {@link copy}
177 |    * @param {Object} namedParameters
178 |    * @param {Object} namedParameters.obj
179 |    * @param {String} namedParameters.sourcePath
180 |    * @param {String} namedParameters.target
181 |    * @param {String} namedParameters.destPath
182 |    * @return {Object}
183 |    */
184 |   static copy ({obj=CopyI.req, sourcePath=CopyI.req, target=CopyI.req, destPath=CopyI.req, ...kwargs}={}) {
185 |     CopyI.extra(kwargs);
186 |     CopyI.typecheck(arguments);
187 |     return DotObject.copy(sourcePath, destPath, obj, target);
188 |   }
189 | 
190 |   /**
191 |    * @see {@link transfer}
192 |    * @param {Object} namedParameters
193 |    * @param {String} namedParameters.sourcePath
194 |    * @param {String} namedParameters.destPath
195 |    * @param {Object} namedParameters.obj
196 |    * @param {Object} namedParameters.target
197 |    * @return {Object}
198 |    */
199 |   static transfer ({sourcePath=TransferI.req, destPath=TransferI.req, obj=TransferI.req, target=TransferI.req, ...kwargs}={}) {
200 |     TransferI.extra(kwargs);
201 |     TransferI.typecheck(arguments);
202 |     return DotObject.transfer(sourcePath, destPath, obj, target);
203 |   }
204 | 
205 |   /**
206 |    * @see {@link expand}
207 |    * @param {Object} namedParameters
208 |    * @param {Object} namedParameters.obj
209 |    * @returns {Object}
210 |    */
211 |   static expand ({obj=ExpandI.req, ...kwargs}={}) {
212 |     ExpandI.extra(kwargs);
213 |     ExpandI.typecheck(arguments);
214 |     return DotObject.object(obj);
215 |   }
216 | 
217 |   /**
218 |    * @see {@link get}
219 |    * @param {Object} namedParameters
220 |    * @param {String} namedParameters.path
221 |    * @param {Object} namedParameters.obj
222 |    * @returns {Any}
223 |    */
224 |   static get ({path=GetI.req, obj=GetI.req, ...kwargs}={}) {
225 |     GetI.extra(kwargs);
226 |     GetI.typecheck(arguments);
227 |     const result = DotObject.pick(path, obj);
228 |     if (result === undefined) return null;
229 |     return result;
230 |   }
231 | 
232 |   /**
233 |    * @see {@link set}
234 |    * @param {Object} namedParameters
235 |    * @param {String} namedParameters.path
236 |    * @param {Any} namedParameters.value
237 |    * @param {Object} namedParameters.obj
238 |    * @returns {Object}
239 |    */
240 |   static set ({path=SetI.req, value=SetI.req, obj=SetI.req, ...kwargs}={}) {
241 |     SetI.extra(kwargs);
242 |     SetI.typecheck(arguments);
243 |     return DotObject.str(path, value, obj);
244 |   }
245 | 
246 |   /**
247 |    * @see {@link delete_}
248 |    * @param {Object} namedParameters
249 |    * @param {String} namedParameters.path
250 |    * @param {Object} namedParameters.obj
251 |    * @returns {Object}
252 |    */
253 |   static delete ({path=DeleteI.req, obj=DeleteI.req, ...kwargs}={}) {
254 |     DeleteI.extra(kwargs);
255 |     DeleteI.typecheck(arguments);
256 |     return DotObject.delete(path, obj);
257 |   }
258 | 
259 |   /**
260 |    * @see {@link remove}
261 |    * @param {Object} namedParameters
262 |    * @param {String} namedParameters.path
263 |    * @param {Object} namedParameters.obj
264 |    * @returns {Object}
265 |    */
266 |   static remove ({path=RemoveI.req, obj=RemoveI.req, ...kwargs}={}) {
267 |     RemoveI.extra(kwargs);
268 |     RemoveI.typecheck(arguments);
269 |     return DotObject.remove(path, obj);
270 |   }
271 | 
272 |   /**
273 |    * @see {@link set}
274 |    * @param {Object} namedParameters
275 |    * @param {String} namedParameters.path
276 |    * @param {Any} namedParameters.value
277 |    * @param {Object} namedParameters.obj
278 |    * @returns {Object}
279 |    */
280 |   static transform ({recipe=TransformI.req, obj=TransformI.req, ...kwargs}={}) {
281 |     TransformI.extra(kwargs);
282 |     TransformI.typecheck(arguments);
283 |     return DotObject.transform(recipe, obj);
284 |   }
285 | 
286 |   /**
287 |    * @see {@link dot}
288 |    * @param {Object} namedParameters
289 |    * @param {Object} namedParameters.obj
290 |    * @returns {Object}
291 |    */
292 |   static dot ({obj=DotI.req, ...kwargs}={}) {
293 |     DotI.extra(kwargs);
294 |     DotI.typecheck(arguments);
295 |     return DotObject.dot(obj);
296 |   }
297 | 
298 |   /**
299 |    * @see {@link jsonsToRows}
300 |    * @param {Object} namedParameters
301 |    * @param {Object[]} namedParameters.jsons
302 |    * @param {String[]} [namedParameters.priorityHeaders=[]]
303 |    * @param {Boolean} [namedParameters.deleteNulls=false]
304 |    * @param {Boolean} [namedParameters.deleteEmptyArrays=true]
305 |    * @returns {Array[]}
306 |    */
307 |   static jsonsToRows ({jsons=JsonsI.req, priorityHeaders=[], deleteNulls=false, deleteEmptyArrays=true, ...kwargs}={}) {
308 |     JsonsI.extra(kwargs);
309 |     JsonsI.typecheck(arguments);
310 |     const headers = [];
311 |     const values = [];
312 | 
313 |     // save values as dotted objects and store headers
314 |     // remove any nulls
315 |     for (const json of jsons) {
316 |       const value = DotObject.dot(json);
317 |       for (const [k, v] of Object.entries(value)) {
318 |         if ( (deleteNulls && v === null) || (deleteEmptyArrays && Array.isArray(v) && v.length===0) ) {
319 |           delete value[k];
320 |         }
321 |       }
322 |       headers.push(Object.keys(value));
323 |       values.push(value);
324 |     }
325 | 
326 |     // row1 will consist of unique header columns, sorted alphabetically with id first
327 |     // use array generics for most efficient manner of doing this
328 |     const row1 = [...new Set([].concat(...headers))];
329 |     row1.sort();
330 |     for (const [i, h] of priorityHeaders.entries()) {
331 |       const idx = row1.indexOf(h);
332 |       if (idx !== -1) {
333 |         row1.splice(idx, 1);      // remove
334 |         row1.splice(i, 0, h);  // insert at front
335 |       }
336 | 
337 |     }
338 | 
339 |     // the rest of the rows consits of the values in each column, or null if not present
340 |     const rows = values.map(value => row1.map(column => value[column] === false ? false : value[column] || null));
341 | 
342 |     // concat the arrays efficiently for return
343 |     return [row1, ...rows];
344 |   }
345 | 
346 |   /**
347 |    * @see {@link rowsToJsons}
348 |    * @param {Object} namedParameters
349 |    * @param {Any[][]} namedParameters.rows
350 |    * @returns {Any[]}
351 |    */
352 |   static rowsToJsons ({rows=RowsI.req, ...kwargs}={}) {
353 |     RowsI.extra(kwargs);
354 |     RowsI.typecheck(arguments);
355 |     const headers = rows[0];
356 |     const objects = [];
357 | 
358 |     // go through row 1 to end
359 |     for (const row of rows.slice(1)) {
360 |       const dotted = row.reduce(
361 |         function (acc, value, idx) {
362 |           // safeguard if for some reason empty string for column or null
363 |           const column = headers[idx] || null;
364 |           if (!column) return acc;
365 | 
366 |           // set and return
367 |           acc[column] = value;
368 |           return acc;
369 |         }, {}
370 |       );
371 |       const obj = DotObject.object(dotted);
372 |       objects.push(obj);
373 |     }
374 |     return objects;
375 |   }
376 | 
377 |   /*
378 |     Sets up {}.dottie and [].dottie methods
379 |     @param {Any} O - Pass your `Object`
380 |     @param {Any} A - Pass your `Array`
381 |    */
382 |   static augment (O=null, A=null) {
383 |     if (O === null) O = Object;
384 |     if (A === null) A = Array;
385 |     const me = this;
386 |     !A.prototype.dottie && Object.defineProperty(A.prototype, 'dottie', {
387 |       get: function () {
388 |         return {
389 |           jsonsToRows: ({priorityHeaders=[], ...kwargs}={}) => {
390 |             return me.jsonsToRows({jsons:this, priorityHeaders});
391 |           },
392 |           rowsToJsons: ({...kwargs}={}) => {
393 |             return me.rowsToJsons({rows: this});
394 |           }
395 |         };
396 |       }
397 |     });
398 | 
399 |     !O.prototype.dottie && Object.defineProperty(O.prototype, 'dottie', {
400 |       get: function () {
401 |         return {
402 |           set: ({path, value}={}) => {
403 |             return DotObject.str(path, value, this);
404 |           },
405 |           get: ({path}={}) => {
406 |             return DotObject.pick(path, this);
407 |           },
408 |           move: ({path, destPath}={}) => {
409 |             return DotObject.move(path, destPath, this);
410 |           },
411 |           copy: ({path, destPath, target}={}) => {
412 |             return DotObject.copy(path, destPath, this, target);
413 |           },
414 |           transfer: ({path, destPath, target}={}) => {
415 |             return DotObject.transfer(path, destPath, this, target);
416 |           },
417 |           expand: () => {
418 |             return DotObject.object(this);
419 |           },
420 |           delete: ({path}={}) => {
421 |             return DotObject.delete(path, this);
422 |           },
423 |           remove: ({path}) => {
424 |             return DotObject.remove(path, this);
425 |           },
426 |           transform: ({recipe}={}) => {
427 |             return DotObject.transform(recipe, this);
428 |           },
429 |           dot: ({target, path}={}) => {
430 |             return DotObject.dot(this, target, path);
431 |           }
432 |         };
433 |       }
434 |     });
435 |   }
436 | 
437 | }
438 | 
439 | export {Dottie};
440 | 
441 |
442 |
443 | 444 | 445 | 446 | 447 |
448 | 449 |
450 | 451 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | -------------------------------------------------------------------------------- /project/Bundle.js: -------------------------------------------------------------------------------- 1 | /* Bundle as defined from all files in src/modules/*.js */ 2 | const Import = Object.create(null); 3 | 4 | 'use strict'; 5 | 6 | (function (exports, window) { 7 | // provide global (danger zone) 8 | exports.__window = window; 9 | 10 | Object.defineProperty(exports, '__esModule', { value: true }); 11 | 12 | function _process (v, mod) { 13 | var i; 14 | var r; 15 | 16 | if (typeof mod === 'function') { 17 | r = mod(v); 18 | if (r !== undefined) { 19 | v = r; 20 | } 21 | } else if (Array.isArray(mod)) { 22 | for (i = 0; i < mod.length; i++) { 23 | r = mod[i](v); 24 | if (r !== undefined) { 25 | v = r; 26 | } 27 | } 28 | } 29 | 30 | return v 31 | } 32 | 33 | function parseKey (key, val) { 34 | // detect negative index notation 35 | if (key[0] === '-' && Array.isArray(val) && /^-\d+$/.test(key)) { 36 | return val.length + parseInt(key, 10) 37 | } 38 | return key 39 | } 40 | 41 | function isIndex (k) { 42 | return /^\d+$/.test(k) 43 | } 44 | 45 | function isObject (val) { 46 | return Object.prototype.toString.call(val) === '[object Object]' 47 | } 48 | 49 | function isArrayOrObject (val) { 50 | return Object(val) === val 51 | } 52 | 53 | function isEmptyObject (val) { 54 | return Object.keys(val).length === 0 55 | } 56 | 57 | var blacklist = ['__proto__', 'prototype', 'constructor']; 58 | var blacklistFilter = function (part) { return blacklist.indexOf(part) === -1 }; 59 | 60 | function parsePath (path, sep) { 61 | if (path.indexOf('[') >= 0) { 62 | path = path.replace(/\[/g, sep).replace(/]/g, ''); 63 | } 64 | 65 | var parts = path.split(sep); 66 | 67 | var check = parts.filter(blacklistFilter); 68 | 69 | if (check.length !== parts.length) { 70 | throw Error('Refusing to update blacklisted property ' + path) 71 | } 72 | 73 | return parts 74 | } 75 | 76 | var hasOwnProperty = Object.prototype.hasOwnProperty; 77 | 78 | function DotObject (separator, override, useArray, useBrackets) { 79 | if (!(this instanceof DotObject)) { 80 | return new DotObject(separator, override, useArray, useBrackets) 81 | } 82 | 83 | if (typeof override === 'undefined') override = false; 84 | if (typeof useArray === 'undefined') useArray = true; 85 | if (typeof useBrackets === 'undefined') useBrackets = true; 86 | this.separator = separator || '.'; 87 | this.override = override; 88 | this.useArray = useArray; 89 | this.useBrackets = useBrackets; 90 | this.keepArray = false; 91 | 92 | // contains touched arrays 93 | this.cleanup = []; 94 | } 95 | 96 | var dotDefault = new DotObject('.', false, true, true); 97 | function wrap (method) { 98 | return function () { 99 | return dotDefault[method].apply(dotDefault, arguments) 100 | } 101 | } 102 | 103 | DotObject.prototype._fill = function (a, obj, v, mod) { 104 | var k = a.shift(); 105 | 106 | if (a.length > 0) { 107 | obj[k] = obj[k] || (this.useArray && isIndex(a[0]) ? [] : {}); 108 | 109 | if (!isArrayOrObject(obj[k])) { 110 | if (this.override) { 111 | obj[k] = {}; 112 | } else { 113 | if (!(isArrayOrObject(v) && isEmptyObject(v))) { 114 | throw new Error( 115 | 'Trying to redefine `' + k + '` which is a ' + typeof obj[k] 116 | ) 117 | } 118 | 119 | return 120 | } 121 | } 122 | 123 | this._fill(a, obj[k], v, mod); 124 | } else { 125 | if (!this.override && isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) { 126 | if (!(isArrayOrObject(v) && isEmptyObject(v))) { 127 | throw new Error("Trying to redefine non-empty obj['" + k + "']") 128 | } 129 | 130 | return 131 | } 132 | 133 | obj[k] = _process(v, mod); 134 | } 135 | }; 136 | 137 | /** 138 | * 139 | * Converts an object with dotted-key/value pairs to it's expanded version 140 | * 141 | * Optionally transformed by a set of modifiers. 142 | * 143 | * Usage: 144 | * 145 | * var row = { 146 | * 'nr': 200, 147 | * 'doc.name': ' My Document ' 148 | * } 149 | * 150 | * var mods = { 151 | * 'doc.name': [_s.trim, _s.underscored] 152 | * } 153 | * 154 | * dot.object(row, mods) 155 | * 156 | * @param {Object} obj 157 | * @param {Object} mods 158 | */ 159 | DotObject.prototype.object = function (obj, mods) { 160 | var self = this; 161 | 162 | Object.keys(obj).forEach(function (k) { 163 | var mod = mods === undefined ? null : mods[k]; 164 | // normalize array notation. 165 | var ok = parsePath(k, self.separator).join(self.separator); 166 | 167 | if (ok.indexOf(self.separator) !== -1) { 168 | self._fill(ok.split(self.separator), obj, obj[k], mod); 169 | delete obj[k]; 170 | } else { 171 | obj[k] = _process(obj[k], mod); 172 | } 173 | }); 174 | 175 | return obj 176 | }; 177 | 178 | /** 179 | * @param {String} path dotted path 180 | * @param {String} v value to be set 181 | * @param {Object} obj object to be modified 182 | * @param {Function|Array} mod optional modifier 183 | */ 184 | DotObject.prototype.str = function (path, v, obj, mod) { 185 | var ok = parsePath(path, this.separator).join(this.separator); 186 | 187 | if (path.indexOf(this.separator) !== -1) { 188 | this._fill(ok.split(this.separator), obj, v, mod); 189 | } else { 190 | obj[path] = _process(v, mod); 191 | } 192 | 193 | return obj 194 | }; 195 | 196 | /** 197 | * 198 | * Pick a value from an object using dot notation. 199 | * 200 | * Optionally remove the value 201 | * 202 | * @param {String} path 203 | * @param {Object} obj 204 | * @param {Boolean} remove 205 | */ 206 | DotObject.prototype.pick = function (path, obj, remove, reindexArray) { 207 | var i; 208 | var keys; 209 | var val; 210 | var key; 211 | var cp; 212 | 213 | keys = parsePath(path, this.separator); 214 | for (i = 0; i < keys.length; i++) { 215 | key = parseKey(keys[i], obj); 216 | if (obj && typeof obj === 'object' && key in obj) { 217 | if (i === keys.length - 1) { 218 | if (remove) { 219 | val = obj[key]; 220 | if (reindexArray && Array.isArray(obj)) { 221 | obj.splice(key, 1); 222 | } else { 223 | delete obj[key]; 224 | } 225 | if (Array.isArray(obj)) { 226 | cp = keys.slice(0, -1).join('.'); 227 | if (this.cleanup.indexOf(cp) === -1) { 228 | this.cleanup.push(cp); 229 | } 230 | } 231 | return val 232 | } else { 233 | return obj[key] 234 | } 235 | } else { 236 | obj = obj[key]; 237 | } 238 | } else { 239 | return undefined 240 | } 241 | } 242 | if (remove && Array.isArray(obj)) { 243 | obj = obj.filter(function (n) { 244 | return n !== undefined 245 | }); 246 | } 247 | return obj 248 | }; 249 | /** 250 | * 251 | * Delete value from an object using dot notation. 252 | * 253 | * @param {String} path 254 | * @param {Object} obj 255 | * @return {any} The removed value 256 | */ 257 | DotObject.prototype.delete = function (path, obj) { 258 | return this.remove(path, obj, true) 259 | }; 260 | 261 | /** 262 | * 263 | * Remove value from an object using dot notation. 264 | * 265 | * Will remove multiple items if path is an array. 266 | * In this case array indexes will be retained until all 267 | * removals have been processed. 268 | * 269 | * Use dot.delete() to automatically re-index arrays. 270 | * 271 | * @param {String|Array} path 272 | * @param {Object} obj 273 | * @param {Boolean} reindexArray 274 | * @return {any} The removed value 275 | */ 276 | DotObject.prototype.remove = function (path, obj, reindexArray) { 277 | var i; 278 | 279 | this.cleanup = []; 280 | if (Array.isArray(path)) { 281 | for (i = 0; i < path.length; i++) { 282 | this.pick(path[i], obj, true, reindexArray); 283 | } 284 | if (!reindexArray) { 285 | this._cleanup(obj); 286 | } 287 | return obj 288 | } else { 289 | return this.pick(path, obj, true, reindexArray) 290 | } 291 | }; 292 | 293 | DotObject.prototype._cleanup = function (obj) { 294 | var ret; 295 | var i; 296 | var keys; 297 | var root; 298 | if (this.cleanup.length) { 299 | for (i = 0; i < this.cleanup.length; i++) { 300 | keys = this.cleanup[i].split('.'); 301 | root = keys.splice(0, -1).join('.'); 302 | ret = root ? this.pick(root, obj) : obj; 303 | ret = ret[keys[0]].filter(function (v) { 304 | return v !== undefined 305 | }); 306 | this.set(this.cleanup[i], ret, obj); 307 | } 308 | this.cleanup = []; 309 | } 310 | }; 311 | 312 | /** 313 | * Alias method for `dot.remove` 314 | * 315 | * Note: this is not an alias for dot.delete() 316 | * 317 | * @param {String|Array} path 318 | * @param {Object} obj 319 | * @param {Boolean} reindexArray 320 | * @return {any} The removed value 321 | */ 322 | DotObject.prototype.del = DotObject.prototype.remove; 323 | 324 | /** 325 | * 326 | * Move a property from one place to the other. 327 | * 328 | * If the source path does not exist (undefined) 329 | * the target property will not be set. 330 | * 331 | * @param {String} source 332 | * @param {String} target 333 | * @param {Object} obj 334 | * @param {Function|Array} mods 335 | * @param {Boolean} merge 336 | */ 337 | DotObject.prototype.move = function (source, target, obj, mods, merge) { 338 | if (typeof mods === 'function' || Array.isArray(mods)) { 339 | this.set(target, _process(this.pick(source, obj, true), mods), obj, merge); 340 | } else { 341 | merge = mods; 342 | this.set(target, this.pick(source, obj, true), obj, merge); 343 | } 344 | 345 | return obj 346 | }; 347 | 348 | /** 349 | * 350 | * Transfer a property from one object to another object. 351 | * 352 | * If the source path does not exist (undefined) 353 | * the property on the other object will not be set. 354 | * 355 | * @param {String} source 356 | * @param {String} target 357 | * @param {Object} obj1 358 | * @param {Object} obj2 359 | * @param {Function|Array} mods 360 | * @param {Boolean} merge 361 | */ 362 | DotObject.prototype.transfer = function ( 363 | source, 364 | target, 365 | obj1, 366 | obj2, 367 | mods, 368 | merge 369 | ) { 370 | if (typeof mods === 'function' || Array.isArray(mods)) { 371 | this.set( 372 | target, 373 | _process(this.pick(source, obj1, true), mods), 374 | obj2, 375 | merge 376 | ); 377 | } else { 378 | merge = mods; 379 | this.set(target, this.pick(source, obj1, true), obj2, merge); 380 | } 381 | 382 | return obj2 383 | }; 384 | 385 | /** 386 | * 387 | * Copy a property from one object to another object. 388 | * 389 | * If the source path does not exist (undefined) 390 | * the property on the other object will not be set. 391 | * 392 | * @param {String} source 393 | * @param {String} target 394 | * @param {Object} obj1 395 | * @param {Object} obj2 396 | * @param {Function|Array} mods 397 | * @param {Boolean} merge 398 | */ 399 | DotObject.prototype.copy = function (source, target, obj1, obj2, mods, merge) { 400 | if (typeof mods === 'function' || Array.isArray(mods)) { 401 | this.set( 402 | target, 403 | _process( 404 | // clone what is picked 405 | JSON.parse(JSON.stringify(this.pick(source, obj1, false))), 406 | mods 407 | ), 408 | obj2, 409 | merge 410 | ); 411 | } else { 412 | merge = mods; 413 | this.set(target, this.pick(source, obj1, false), obj2, merge); 414 | } 415 | 416 | return obj2 417 | }; 418 | 419 | /** 420 | * 421 | * Set a property on an object using dot notation. 422 | * 423 | * @param {String} path 424 | * @param {any} val 425 | * @param {Object} obj 426 | * @param {Boolean} merge 427 | */ 428 | DotObject.prototype.set = function (path, val, obj, merge) { 429 | var i; 430 | var k; 431 | var keys; 432 | var key; 433 | 434 | // Do not operate if the value is undefined. 435 | if (typeof val === 'undefined') { 436 | return obj 437 | } 438 | keys = parsePath(path, this.separator); 439 | 440 | for (i = 0; i < keys.length; i++) { 441 | key = keys[i]; 442 | if (i === keys.length - 1) { 443 | if (merge && isObject(val) && isObject(obj[key])) { 444 | for (k in val) { 445 | if (hasOwnProperty.call(val, k)) { 446 | obj[key][k] = val[k]; 447 | } 448 | } 449 | } else if (merge && Array.isArray(obj[key]) && Array.isArray(val)) { 450 | for (var j = 0; j < val.length; j++) { 451 | obj[keys[i]].push(val[j]); 452 | } 453 | } else { 454 | obj[key] = val; 455 | } 456 | } else if ( 457 | // force the value to be an object 458 | !hasOwnProperty.call(obj, key) || 459 | (!isObject(obj[key]) && !Array.isArray(obj[key])) 460 | ) { 461 | // initialize as array if next key is numeric 462 | if (/^\d+$/.test(keys[i + 1])) { 463 | obj[key] = []; 464 | } else { 465 | obj[key] = {}; 466 | } 467 | } 468 | obj = obj[key]; 469 | } 470 | return obj 471 | }; 472 | 473 | /** 474 | * 475 | * Transform an object 476 | * 477 | * Usage: 478 | * 479 | * var obj = { 480 | * "id": 1, 481 | * "some": { 482 | * "thing": "else" 483 | * } 484 | * } 485 | * 486 | * var transform = { 487 | * "id": "nr", 488 | * "some.thing": "name" 489 | * } 490 | * 491 | * var tgt = dot.transform(transform, obj) 492 | * 493 | * @param {Object} recipe Transform recipe 494 | * @param {Object} obj Object to be transformed 495 | * @param {Array} mods modifiers for the target 496 | */ 497 | DotObject.prototype.transform = function (recipe, obj, tgt) { 498 | obj = obj || {}; 499 | tgt = tgt || {}; 500 | Object.keys(recipe).forEach( 501 | function (key) { 502 | this.set(recipe[key], this.pick(key, obj), tgt); 503 | }.bind(this) 504 | ); 505 | return tgt 506 | }; 507 | 508 | /** 509 | * 510 | * Convert object to dotted-key/value pair 511 | * 512 | * Usage: 513 | * 514 | * var tgt = dot.dot(obj) 515 | * 516 | * or 517 | * 518 | * var tgt = {} 519 | * dot.dot(obj, tgt) 520 | * 521 | * @param {Object} obj source object 522 | * @param {Object} tgt target object 523 | * @param {Array} path path array (internal) 524 | */ 525 | DotObject.prototype.dot = function (obj, tgt, path) { 526 | tgt = tgt || {}; 527 | path = path || []; 528 | var isArray = Array.isArray(obj); 529 | 530 | Object.keys(obj).forEach( 531 | function (key) { 532 | var index = isArray && this.useBrackets ? '[' + key + ']' : key; 533 | if ( 534 | isArrayOrObject(obj[key]) && 535 | ((isObject(obj[key]) && !isEmptyObject(obj[key])) || 536 | (Array.isArray(obj[key]) && !this.keepArray && obj[key].length !== 0)) 537 | ) { 538 | if (isArray && this.useBrackets) { 539 | var previousKey = path[path.length - 1] || ''; 540 | return this.dot( 541 | obj[key], 542 | tgt, 543 | path.slice(0, -1).concat(previousKey + index) 544 | ) 545 | } else { 546 | return this.dot(obj[key], tgt, path.concat(index)) 547 | } 548 | } else { 549 | if (isArray && this.useBrackets) { 550 | tgt[path.join(this.separator).concat('[' + key + ']')] = obj[key]; 551 | } else { 552 | tgt[path.concat(index).join(this.separator)] = obj[key]; 553 | } 554 | } 555 | }.bind(this) 556 | ); 557 | return tgt 558 | }; 559 | 560 | DotObject.pick = wrap('pick'); 561 | DotObject.move = wrap('move'); 562 | DotObject.transfer = wrap('transfer'); 563 | DotObject.transform = wrap('transform'); 564 | DotObject.copy = wrap('copy'); 565 | DotObject.object = wrap('object'); 566 | DotObject.str = wrap('str'); 567 | DotObject.set = wrap('set'); 568 | DotObject.delete = wrap('delete'); 569 | DotObject.del = DotObject.remove = wrap('remove'); 570 | DotObject.dot = wrap('dot'); 571 | ['override', 'overwrite'].forEach(function (prop) { 572 | Object.defineProperty(DotObject, prop, { 573 | get: function () { 574 | return dotDefault.override 575 | }, 576 | set: function (val) { 577 | dotDefault.override = !!val; 578 | } 579 | }); 580 | }); 581 | ['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) { 582 | Object.defineProperty(DotObject, prop, { 583 | get: function () { 584 | return dotDefault[prop] 585 | }, 586 | set: function (val) { 587 | dotDefault[prop] = val; 588 | } 589 | }); 590 | }); 591 | 592 | DotObject._process = _process; 593 | 594 | var dotObject = DotObject; 595 | 596 | function Interface_ (name='', params={}) { 597 | class I_ { 598 | constructor (n, p) { 599 | this.name = `dottie.${n}`; 600 | this.params = p; 601 | } 602 | 603 | get req() { 604 | throw Error(`Missing at least one required parameter for ${this.name}: ${Object.keys(this.params)}`); 605 | } 606 | 607 | extra(kwargs) { 608 | if (Object.entries(kwargs).length > 0) 609 | throw Error(`Extra parameters passed to ${this.name}: ${Object.entries(kwargs)}`); 610 | } 611 | 612 | typecheck(args) { 613 | // arguments is an object-like array, need to flatten it so that we represent it as viewed from function scope 614 | let argObj = {}; 615 | for (const prop in args) { 616 | argObj = {...argObj, ...args[prop]}; 617 | } 618 | 619 | // now that both have matching types, let's go 620 | for (const prop in this.params) { 621 | if (this.params[prop] === 'any') continue; // type of 'any' special meaning is to skip it 622 | if (argObj[prop] === undefined) continue; 623 | if (this.params[prop] === 'array') { 624 | if (!Array.isArray(argObj[prop])) throw new Error(`Type mismatch in ${this.name}: "${prop}". Expected array but got ${typeof(argObj)} instead`); 625 | } else if (typeof(argObj[prop]) !== this.params[prop]) { 626 | throw new Error(`Type mismatch in ${this.name}: "${prop}". Expected ${this.params[prop]} but got ${typeof(argObj[prop])} instead`); 627 | } 628 | } 629 | } 630 | } 631 | return new I_(name, params); 632 | } 633 | 634 | 635 | // create interfaces that allows us to enforce type checks 636 | const MoveI = Interface_('move', {sourcePath: 'string', destPath: 'string', obj: 'object'}); 637 | const CopyI = Interface_('copy', {obj: 'object', sourcePath: 'string', target: 'object', destPath: 'string'}); 638 | const TransferI = Interface_('transfer', {sourcePath: 'string', destPath: 'string', obj: 'object', target: 'object'}); 639 | const ExpandI = Interface_('expand', {obj: 'object'}); 640 | const GetI = Interface_('get', {path: 'string', obj: 'object'}); 641 | const SetI = Interface_('set', {path: 'string', obj: 'object', value: 'any'}); 642 | const DeleteI = Interface_('delete', {path: 'string', obj: 'object'}); 643 | const RemoveI = Interface_('remove', {path: 'string', obj: 'object'}); 644 | const TransformI = Interface_('transform', {recipe: 'object', obj: 'object'}); 645 | const DotI = Interface_('dot', {obj: 'object'}); 646 | const JsonsI = Interface_('jsons', {jsons: 'array', priorityHeaders: 'array', deleteNulls: 'boolean', deleteEmptyArrays: 'boolean'}); 647 | const RowsI = Interface_('rows', {rows: 'array'}); 648 | 649 | /** 650 | * NOTE: This is the advanced section, consider using global methods at left to get started. 651 | * 652 | * The global methods are just light wrappers for these methods. You can use these methods directly by calling `augment`, which augmented the `Object` and `Array` with a `dottie` namespace which bears these methods. 653 | * @class 654 | */ 655 | class Dottie { 656 | 657 | /** 658 | * No need to call this constructor 659 | */ 660 | constructor () { 661 | 662 | } 663 | 664 | /** 665 | * @see {@link move} 666 | * @param {Object} params 667 | * @param {String} params.sourcePath 668 | * @param {String} params.destPath 669 | * @param {Object} params.obj 670 | * @return {Object} 671 | */ 672 | static move ({sourcePath=MoveI.req, destPath=MoveI.req, obj=MoveI.req, ...kwargs}={}) { 673 | MoveI.extra(kwargs); 674 | MoveI.typecheck(arguments); 675 | return dotObject.move(sourcePath, destPath, obj); 676 | } 677 | 678 | /** 679 | * @see {@link copy} 680 | * @param {Object} params 681 | * @param {Object} params.obj 682 | * @param {String} params.sourcePath 683 | * @param {String} params.target 684 | * @param {String} params.destPath 685 | * @return {Object} 686 | */ 687 | static copy ({obj=CopyI.req, sourcePath=CopyI.req, target=CopyI.req, destPath=CopyI.req, ...kwargs}={}) { 688 | CopyI.extra(kwargs); 689 | CopyI.typecheck(arguments); 690 | return dotObject.copy(sourcePath, destPath, obj, target); 691 | } 692 | 693 | /** 694 | * @see {@link transfer} 695 | * @param {Object} params 696 | * @param {String} params.sourcePath 697 | * @param {String} params.destPath 698 | * @param {Object} params.obj 699 | * @param {Object} params.target 700 | * @return {Object} 701 | */ 702 | static transfer ({sourcePath=TransferI.req, destPath=TransferI.req, obj=TransferI.req, target=TransferI.req, ...kwargs}={}) { 703 | TransferI.extra(kwargs); 704 | TransferI.typecheck(arguments); 705 | return dotObject.transfer(sourcePath, destPath, obj, target); 706 | } 707 | 708 | /** 709 | * @see {@link expand} 710 | * @param {Object} params 711 | * @param {Object} params.obj 712 | * @returns {Object} 713 | */ 714 | static expand ({obj=ExpandI.req, ...kwargs}={}) { 715 | ExpandI.extra(kwargs); 716 | ExpandI.typecheck(arguments); 717 | return dotObject.object(obj); 718 | } 719 | 720 | /** 721 | * @see {@link get} 722 | * @param {Object} params 723 | * @param {String} params.path 724 | * @param {Object} params.obj 725 | * @returns {Any} 726 | */ 727 | static get ({path=GetI.req, obj=GetI.req, ...kwargs}={}) { 728 | GetI.extra(kwargs); 729 | GetI.typecheck(arguments); 730 | const result = dotObject.pick(path, obj); 731 | if (result === undefined) return null; 732 | return result; 733 | } 734 | 735 | /** 736 | * @see {@link set} 737 | * @param {Object} params 738 | * @param {String} params.path 739 | * @param {Any} params.value 740 | * @param {Object} params.obj 741 | * @returns {Object} 742 | */ 743 | static set ({path=SetI.req, value=SetI.req, obj=SetI.req, ...kwargs}={}) { 744 | SetI.extra(kwargs); 745 | SetI.typecheck(arguments); 746 | return dotObject.str(path, value, obj); 747 | } 748 | 749 | /** 750 | * @see {@link delete_} 751 | * @param {Object} params 752 | * @param {String} params.path 753 | * @param {Object} params.obj 754 | * @returns {Object} 755 | */ 756 | static delete ({path=DeleteI.req, obj=DeleteI.req, ...kwargs}={}) { 757 | DeleteI.extra(kwargs); 758 | DeleteI.typecheck(arguments); 759 | return dotObject.delete(path, obj); 760 | } 761 | 762 | /** 763 | * @see {@link remove} 764 | * @param {Object} params 765 | * @param {String} params.path 766 | * @param {Object} params.obj 767 | * @returns {Object} 768 | */ 769 | static remove ({path=RemoveI.req, obj=RemoveI.req, ...kwargs}={}) { 770 | RemoveI.extra(kwargs); 771 | RemoveI.typecheck(arguments); 772 | return dotObject.remove(path, obj); 773 | } 774 | 775 | /** 776 | * @see {@link set} 777 | * @param {Object} params 778 | * @param {String} params.path 779 | * @param {Any} params.value 780 | * @param {Object} params.obj 781 | * @returns {Object} 782 | */ 783 | static transform ({recipe=TransformI.req, obj=TransformI.req, ...kwargs}={}) { 784 | TransformI.extra(kwargs); 785 | TransformI.typecheck(arguments); 786 | return dotObject.transform(recipe, obj); 787 | } 788 | 789 | /** 790 | * @see {@link dot} 791 | * @param {Object} params 792 | * @param {Object} params.obj 793 | * @returns {Object} 794 | */ 795 | static dot ({obj=DotI.req, ...kwargs}={}) { 796 | DotI.extra(kwargs); 797 | DotI.typecheck(arguments); 798 | return dotObject.dot(obj); 799 | } 800 | 801 | /** 802 | * @see {@link jsonsToRows} 803 | * @param {Object} params 804 | * @param {Object[]} params.jsons 805 | * @param {String[]} [params.priorityHeaders=[]] 806 | * @param {Boolean} [params.deleteNulls=false] 807 | * @param {Boolean} [params.deleteEmptyArrays=true] 808 | * @returns {Array[]} 809 | */ 810 | static jsonsToRows ({jsons=JsonsI.req, priorityHeaders=[], deleteNulls=false, deleteEmptyArrays=true, ...kwargs}={}) { 811 | JsonsI.extra(kwargs); 812 | JsonsI.typecheck(arguments); 813 | const headers = []; 814 | const values = []; 815 | 816 | // save values as dotted objects and store headers 817 | // remove any nulls 818 | for (const json of jsons) { 819 | const value = dotObject.dot(json); 820 | for (const [k, v] of Object.entries(value)) { 821 | if ( (deleteNulls && v === null) || (deleteEmptyArrays && Array.isArray(v) && v.length===0) ) { 822 | delete value[k]; 823 | } 824 | } 825 | headers.push(Object.keys(value)); 826 | values.push(value); 827 | } 828 | 829 | // row1 will consist of unique header columns, sorted alphabetically with id first 830 | // use array generics for most efficient manner of doing this 831 | const row1 = [...new Set([].concat(...headers))]; 832 | row1.sort(); 833 | for (const [i, h] of priorityHeaders.entries()) { 834 | const idx = row1.indexOf(h); 835 | if (idx !== -1) { 836 | row1.splice(idx, 1); // remove 837 | row1.splice(i, 0, h); // insert at front 838 | } 839 | 840 | } 841 | 842 | // the rest of the rows consits of the values in each column, or null if not present 843 | const rows = values.map(value => row1.map(column => value[column] === false ? false : value[column] || null)); 844 | 845 | // concat the arrays efficiently for return 846 | return [row1, ...rows]; 847 | } 848 | 849 | /** 850 | * @see {@link rowsToJsons} 851 | * @param {Object} params 852 | * @param {Any[][]} params.rows 853 | * @returns {Any[]} 854 | */ 855 | static rowsToJsons ({rows=RowsI.req, ...kwargs}={}) { 856 | RowsI.extra(kwargs); 857 | RowsI.typecheck(arguments); 858 | const headers = rows[0]; 859 | const objects = []; 860 | 861 | // go through row 1 to end 862 | for (const row of rows.slice(1)) { 863 | const dotted = row.reduce( 864 | function (acc, value, idx) { 865 | // safeguard if for some reason empty string for column or null 866 | const column = headers[idx] || null; 867 | if (!column) return acc; 868 | 869 | // set and return 870 | acc[column] = value; 871 | return acc; 872 | }, {} 873 | ); 874 | const obj = dotObject.object(dotted); 875 | objects.push(obj); 876 | } 877 | return objects; 878 | } 879 | 880 | /* 881 | Sets up {}.dottie and [].dottie methods 882 | @param {Any} O - Pass your `Object` 883 | @param {Any} A - Pass your `Array` 884 | */ 885 | static augment (O=null, A=null) { 886 | if (O === null) O = Object; 887 | if (A === null) A = Array; 888 | const me = this; 889 | !A.prototype.dottie && Object.defineProperty(A.prototype, 'dottie', { 890 | get: function () { 891 | return { 892 | jsonsToRows: ({priorityHeaders=[], ...kwargs}={}) => { 893 | return me.jsonsToRows({jsons:this, priorityHeaders}); 894 | }, 895 | rowsToJsons: ({...kwargs}={}) => { 896 | return me.rowsToJsons({rows: this}); 897 | } 898 | }; 899 | } 900 | }); 901 | 902 | !O.prototype.dottie && Object.defineProperty(O.prototype, 'dottie', { 903 | get: function () { 904 | return { 905 | set: ({path, value}={}) => { 906 | return dotObject.str(path, value, this); 907 | }, 908 | get: ({path}={}) => { 909 | return dotObject.pick(path, this); 910 | }, 911 | move: ({path, destPath}={}) => { 912 | return dotObject.move(path, destPath, this); 913 | }, 914 | copy: ({path, destPath, target}={}) => { 915 | return dotObject.copy(path, destPath, this, target); 916 | }, 917 | transfer: ({path, destPath, target}={}) => { 918 | return dotObject.transfer(path, destPath, this, target); 919 | }, 920 | expand: () => { 921 | return dotObject.object(this); 922 | }, 923 | delete: ({path}={}) => { 924 | return dotObject.delete(path, this); 925 | }, 926 | remove: ({path}) => { 927 | return dotObject.remove(path, this); 928 | }, 929 | transform: ({recipe}={}) => { 930 | return dotObject.transform(recipe, this); 931 | }, 932 | dot: ({target, path}={}) => { 933 | return dotObject.dot(this, target, path); 934 | } 935 | }; 936 | } 937 | }); 938 | } 939 | 940 | } 941 | 942 | exports.DotObject = dotObject; 943 | exports.Dottie = Dottie; 944 | 945 | })(Import, this); 946 | try{exports.Import = Import;}catch(e){} 947 | --------------------------------------------------------------------------------