diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..980ebdf --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "arrowParens": "always", + "bracketSpacing": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "jsxSingleQuote": true, + "useTabs": false +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f1e3e32 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + }, + "files.insertFinalNewline": true +} diff --git a/README.md b/README.md index 201f083..f97bcb5 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,20 @@ # Underpants + This activity is essentially a re-write of [underscore.js](http://underscorejs.org/). ## Why? + In the past exercises we've been writing a lot of loops over objects and arrays ourselves. Instead of doing that, we are going to write several fuctions to handle the looping for us. These functions are used every day by professional developers and we're well on our way to becoming professional developers!! This means that if we're confused about how a function should work, we can check the underscore.js documentation linked above for help. ## Instructions - - Open up index.html in a web browser. - - Notice that all the tests are failing. :) - - Open up underpants.js in a text editor and follow the instructions. - - Make all the test pass!! - - Open underpants.html in a text editor to view the code that runs the tests. + +- Open up index.html in a web browser. +- Notice that all the tests are failing. :) +- Open up underpants.js in a text editor and follow the instructions. +- Make all the test pass!! +- Open underpants.html in a text editor to view the code that runs the tests. ## Links and Resources @@ -20,31 +23,35 @@ Some quick notes that may come in handy: - [underscore documentation](http://underscorejs.org/). - Many of the functions operate on "collections." They can take both arrays or objects as their arguments and you need to be able to handle both cases. - - You can use `Array.isArray(obj)` to find out whether an object is an array. - - You can use `obj.length` to test if something is either a string or an - array. + - You can use `Array.isArray(obj)` to find out whether an object is an array. + - You can use `obj.length` to test if something is either a string or an + array. - Javascript has a built-in `Math` object that provides some very useful functions. [Math Documentation](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math). - Within a function, you can use the `arguments` variable to access all the parameters that were passed in even if they aren't named in the function definition. This is useful if you don't know how many arguments are going to be passed in in advance. - - You can count the arguments by using `arguments.length` and access each - argument using `arguments[i]`. - - The `arguments` object is very similar to an array, but note that it does - not support most array functions (such as `slice` or `push`). You can read - more about this [here](http://www.sitepoint.com/arguments-a-javascript-oddity/). + - You can count the arguments by using `arguments.length` and access each + argument using `arguments[i]`. + - The `arguments` object is very similar to an array, but note that it does + not support most array functions (such as `slice` or `push`). You can read + more about this [here](http://www.sitepoint.com/arguments-a-javascript-oddity/). ### Extra Credit: + See Instructor for instructions. + - defaults -- *once* +- _once_ - memoize - delay - shuffle ### Double Extra Credit: + See Instructor for instructions. + - once - invoke - sortBy diff --git a/index.html b/index.html index b613a3b..2dd85c2 100644 --- a/index.html +++ b/index.html @@ -1,204 +1,38 @@ - - - Underpants Library - - - - - -
- - - + + + Underpants Library + + + + + + + + + +
+ + + + + + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8a31b18 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1200 @@ +{ + "name": "underpants", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "underpants", + "version": "1.0.0", + "license": "CC", + "devDependencies": { + "chai": "^4.5.0", + "lodash": "^4.17.4", + "mocha": "^11.3.0", + "prettier": "^3.5.3", + "sinon": "^19.0.5" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/commons/node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz", + "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "lodash.get": "^4.4.2", + "type-detect": "^4.1.0" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", + "dev": true + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/just-extend": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-6.2.0.tgz", + "integrity": "sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mocha": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.3.0.tgz", + "integrity": "sha512-J0RLIM89xi8y6l77bgbX+03PeBRDQCOVQpnwOcCN7b8hCmbh6JvGI2ZDJ5WMoHz+IaPU+S4lvTd0j51GmBAdgQ==", + "dev": true, + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/mocha/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nise": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz", + "integrity": "sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.1", + "@sinonjs/text-encoding": "^0.7.3", + "just-extend": "^6.2.0", + "path-to-regexp": "^8.1.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sinon": { + "version": "19.0.5", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-19.0.5.tgz", + "integrity": "sha512-r15s9/s+ub/d4bxNXqIUmwp6imVSdTorIRaxoecYjqTVLZ8RuoXr/4EDGwIBo6Waxn7f2gnURX9zuhAfCwaF6Q==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.5", + "@sinonjs/samsam": "^8.0.1", + "diff": "^7.0.0", + "nise": "^6.1.1", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index 721ceba..f32a8f3 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "scripts": { "pretest": "npm install", "test": "mocha ./test/underpants.spec.js || :", - "posttest": "rm -rf ./test ./node_modules" + "posttest": "rm -rf ./test ./node_modules", + "prettier": "prettier . --write" }, "repository": { "type": "git", @@ -23,10 +24,10 @@ }, "homepage": "https://github.com/OperationSpark/underpants", "devDependencies": { - "chai": "^3.5.0", - "istanbul": "^0.4.5", + "chai": "^4.5.0", "lodash": "^4.17.4", - "mocha": "^3.4.1", - "sinon": "^2.2.0" + "mocha": "^11.3.0", + "prettier": "^3.5.3", + "sinon": "^19.0.5" } } diff --git a/spec/underpants.spec.js b/spec/underpants.spec.js new file mode 100644 index 0000000..618d43f --- /dev/null +++ b/spec/underpants.spec.js @@ -0,0 +1,813 @@ +describe('underpants library', () => { + describe('_.identity()', () => { + it('should return input value unchanged', () => { + assert.strictEqual(_.identity(14), 14); + assert.deepEqual(_.identity({ a: 'one' }), { a: 'one' }); + assert.strictEqual(_.identity('hello there'), 'hello there'); + assert.deepEqual(_.identity([1, 2, 3]), [1, 2, 3]); + }); + }); + + describe('_.typeOf()', () => { + const tests = [ + {input: 'hello world', expected: 'string'}, + {input: 100, expected: 'number'}, + {input: false, expected: 'boolean'}, + {input: undefined, expected: 'undefined'}, + {input: function(x,y){return x + y}, expected: 'function'}, + {input: null, expected: 'null'}, + {input: [1, 2, 3], expected: 'array'}, + {input: { a: 1, b: 2 }, expected: 'object'} + ]; + + tests.forEach((e, i) => { + const { input, expected } = e; + it(`should return "${expected}" for ${expected} input`, () => { + assert.equal(_.typeOf(input), expected); + }); + }) + }); + + describe('_.first()', () => { + it('should accept an argument representing the number of items to include in the output', () => { + assert.deepEqual(_.first(['a', 'b', 'c'], 2), ['a', 'b']); + }); + it('should return the first element if no numerical argument is given', () => { + assert.equal(_.first(['a', 'b', 'c']), 'a'); + }); + it('should return empty array if numerical argument is not a positive number', () => { + assert.deepEqual(_.first(['a', 'b', 'c'], -1), []); + }); + it('should return empty array if the array param is not an an array', () => { + assert.deepEqual(_.first({ a: 'b' }, 2), []); + }); + it('should return the whole array if the number is greater than the length of the array', () => { + assert.deepEqual(_.first(['a', 'b', 'c'], 5), ['a', 'b', 'c']); + }); + }); + + describe('_.last()', () => { + it('should accept an argument representing the number of items to include in the output', () => { + assert.deepEqual(_.last(['a', 'b', 'c'], 2), ['b', 'c']); + }); + it('should return the last element if no numerical argument is given', () => { + console.log(_.last); + assert.equal(_.last(['a', 'b', 'c']), 'c'); + }); + it('should return empty array if numerical argument is not a positive number', () => { + assert.deepEqual(_.last(['a', 'b', 'c'], -1), []); + }); + it("shoud return the array if the numerical argument is greater than the array's length", () => { + assert.deepEqual(_.last(['a', 'b', 'c'], 5), ['a', 'b', 'c']); + }); + it('should return empty array if array param is not an array', () => { + assert.deepEqual(_.last({ a: 'b' }, 2), []); + }); + }); + + describe('_.indexOf()', () => { + const inputData = ['a', 'b', 'c', 'd', 'b']; + it('should return the correct index when an element is found', () => { + assert.deepEqual(_.indexOf(inputData, 'b'), 1); + }); + it('should return the index of the first occurance of a found element if there are duplicates', () => { + assert.deepEqual(_.indexOf(inputData, 'b'), 1); + }); + it('should return -1 if the element is not found', () => { + assert.deepEqual(_.indexOf(inputData, 'e'), -1); + }); + it('should not have side effects', () => { + assert.deepEqual(inputData, ['a', 'b', 'c', 'd', 'b']); + }); + }); + + describe('_.contains()', () => { + const inputData = [1, '3', 4, 5, 'a', '4', 'b']; + it('should return true if a list contains an element', () => { + assert.strictEqual(_.contains(inputData, 'a'), true); + }); + it("should return false if the list doesn't contain an element", () => { + assert.strictEqual(_.contains(inputData, 'c'), false); + }); + it('should not convert types when checking', () => { + assert.strictEqual(_.contains(inputData, 3), false); + }); + it('should not have side effects', () => { + assert.deepEqual(inputData, [1, '3', 4, 5, 'a', '4', 'b']); + }); + }); + + describe('_.each()', () => { + beforeEach(() => { + sinon.spy(console, 'log'); + }); + + afterEach(() => { + console.log.restore(); + }); + it('should handle arrays', () => { + const inputArray = [1, 2, 3, 4, 5]; + const output = []; + _.each(inputArray, function (e, i, a) { + output.push(e * 10); + }); + assert.deepEqual(output, [10, 20, 30, 40, 50]); + }); + it('should handle objects', () => { + const inputObject = { a: '1', b: '2', c: '3', d: '4' }; + const output = []; + _.each(inputObject, function (v, k, o) { + output.push(v + v); + }); + assert.deepEqual(output, ['11', '22', '33', '44']); + }); + it('callback should take in current index as an argument if collection is an array', () => { + const inputArray = ['a', 'b', 'c']; + const logs = [0, 1, 2]; + const output = []; + _.each(inputArray, function (e, i, a) { + console.log(i); + output.push(e.toUpperCase()); + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + console.dir('hit this'); + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in array as an argument if collection is an array', () => { + const inputArray = ['a', 'b', 'c']; + const output = []; + _.each(inputArray, function (e, i, a) { + console.log(a); + output.push(e.toUpperCase()); + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], ['a', 'b', 'c']); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in current key as an argument if collection is an object', () => { + const inputObject = { a: 'one', b: 'two' }; + const logs = ['a', 'b']; + _.each(inputObject, (v, k, o) => { + console.log(k); + inputObject[k] = inputObject[k].toUpperCase(); + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in object as an argument if collection is an object', () => { + const inputObject = { a: 'one', b: 'two' }; + const output = []; + _.each(inputObject, (v, k, o) => { + console.log(o); + output.push(inputObject[k].toUpperCase()); + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], { a: 'one', b: 'two' }); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + }); + + describe('_.unique()', () => { + beforeEach(() => { + sinon.spy(_, 'indexOf'); + }); + afterEach(() => { + _.indexOf.restore(); + }); + const inputData = [ + 'a', + 1, + 1, + 'a', + 'c', + false, + 'b', + 5, + 'c', + null, + false, + null, + ]; + it('should return an array with no duplicates', () => { + assert.deepEqual(_.unique(inputData), ['a', 1, 'c', false, 'b', 5, null]); + }); + it('should invoke _.indexOf() method', () => { + _.unique(['a', 'a', 1, 1, 'b', 'b', 'b']); + _.indexOf.called.should.be.true; + }); + it('should not have side effects', () => { + assert.deepEqual(inputData, [ + 'a', + 1, + 1, + 'a', + 'c', + false, + 'b', + 5, + 'c', + null, + false, + null, + ]); + }); + }); + + describe('_.filter()', () => { + beforeEach(() => { + sinon.spy(console, 'log'); + sinon.spy(_, 'each'); + }); + + afterEach(() => { + console.log.restore(); + _.each.restore(); + }); + + const inputData = ['a', 1, 'b', 2, 'c', 4]; + + it('should filter elements in an array', () => { + assert.deepEqual( + _.filter(inputData, (e, i, a) => { + return typeof e === 'string'; + }), + ['a', 'b', 'c'], + ); + }); + it('callback function should take in the current index as one of its arguments', () => { + const input = ['a', 'b', 'aa']; + const logs = [0, 1, 2]; + _.filter(input, (e, i, a) => { + console.log(i); + return e.length === 1; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback function should take in the array as one its arguments', () => { + const input = ['a', 'b', 'aa']; + _.filter(input, (e, i, a) => { + console.log(a); + return e.length === 1; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], ['a', 'b', 'aa']); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('should not have side effects', () => { + assert.deepEqual(inputData, ['a', 1, 'b', 2, 'c', 4]); + }); + // it('optional - use _.each', () => { + // _.filter([1, 2, 3, 4], (e) => e % 2 === 0); + // expect(_.each.calledOnce).to.be.true; + // }); + }); + + describe('_.reject()', () => { + beforeEach(() => { + sinon.spy(console, 'log'); + }); + + afterEach(() => { + console.log.restore(); + }); + + const inputData = ['a', 1, 'b', 2, 'c', 4]; + + it('should return an array of items rejected by the callback function', () => { + assert.deepEqual( + _.reject(inputData, (e) => { + return typeof e === 'string'; + }), + [1, 2, 4], + ); + }); + it('callback function should take in the current index as an argument', () => { + const input = ['a', 'b', 'aa']; + const logs = [0, 1, 2]; + _.reject(input, (e, i, a) => { + console.log(i); + return e.length === 2; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback function should take in the array as an argument', () => { + const input = ['a', 'b', 'aa']; + _.reject(input, (e, i, a) => { + console.log(a); + return e.length === 2; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], ['a', 'b', 'aa']); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('should not have side effects', () => { + assert.deepEqual(inputData, ['a', 1, 'b', 2, 'c', 4]); + }); + }); + + describe('_.partition()', () => { + beforeEach(() => { + sinon.spy(console, 'log'); + }); + + afterEach(() => { + console.log.restore(); + }); + + const inputData = ['a', 1, 'b', 2, 'c', 4]; + + it('should create a correctly partitioned array of subarrays', () => { + assert.deepEqual( + _.partition(inputData, (e) => { + return typeof e === 'string'; + }), + [ + ['a', 'b', 'c'], + [1, 2, 4], + ], + ); + }); + it('callback function should take in the current index as an argument', () => { + const input = ['a', 1]; + const logs = [0, 1]; + _.partition(input, (e, i, a) => { + console.log(i); + return typeof e === 'string'; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback function should take in the array as an argument', () => { + const input = ['a', 1]; + const logs = [ + ['a', 1], + ['a', 1], + ]; + _.partition(input, (e, i, a) => { + console.log(a); + return typeof e === 'string'; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + + it('should not have side effects', () => { + assert.deepEqual(inputData, ['a', 1, 'b', 2, 'c', 4]); + }); + }); + + describe('_.map', () => { + beforeEach(() => { + sinon.spy(console, 'log'); + }); + + afterEach(() => { + console.log.restore(); + }); + + const inputArray = ['a', 'b', 'c', 'd']; + const inputObject = { a: 1, b: 2, c: 3, d: 4 }; + + it('should correctly map an array', () => { + const result = _.map(inputArray, (e) => e.toUpperCase()); + assert.deepEqual(result, ['A', 'B', 'C', 'D']); + }); + it('should correctly map an object', () => { + const result = _.map(inputObject, (e) => e * 10); + assert.deepEqual(result, [10, 20, 30, 40]); + }); + it('callback should take in the current index as an argument if collection is an array', () => { + const logs = [0, 1, 2, 3]; + _.map(inputArray, (e, i, a) => { + console.log(i); + return e.toUpperCase(); + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in the array as argument if collection is an array', () => { + _.map(inputArray, (e, i, a) => { + console.log(a); + return e.toUpperCase(); + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], ['a', 'b', 'c', 'd']); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in the current key as an argument if collection is an argument', () => { + const logs = ['a', 'b', 'c', 'd']; + _.map(inputObject, (v, k, o) => { + console.log(k); + return v * 10; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in the object as an argument if collection is an argument', () => { + _.map(inputObject, (v, k, o) => { + console.log(o); + return v * 10; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], inputObject); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('should not have side effects', () => { + assert.deepEqual(inputArray, ['a', 'b', 'c', 'd']); + assert.deepEqual(inputObject, { a: 1, b: 2, c: 3, d: 4 }); + }); + }); + + describe('_.pluck()', () => { + beforeEach(() => { + sinon.spy(_, 'map'); + }); + afterEach(() => { + _.map.restore(); + }); + const inputData = [ + { name: 'Ralph', age: 22 }, + { name: 'Jimmy', age: 13 }, + { name: 'Carla', age: 20 }, + ]; + it('should pluck properties from a list of objects', () => { + const result = _.pluck(inputData, 'name'); + const correct = ['Ralph', 'Jimmy', 'Carla']; + assert.deepEqual(result, correct); + }); + it('should invoke the _.map() method', () => { + _.pluck(inputData, 'age'); + expect(_.map.calledOnce).to.be.true; + }); + it('should not have side effects', () => { + _.pluck(inputData, 'name'); + assert.deepEqual(inputData, [ + { name: 'Ralph', age: 22 }, + { name: 'Jimmy', age: 13 }, + { name: 'Carla', age: 20 }, + ]); + }); + }); + + describe('_.every()', () => { + beforeEach(() => { + sinon.spy(console, 'log'); + }); + + afterEach(() => { + console.log.restore(); + }); + + const inputData = [2, 4, 6, 7, 8]; + const inputObject = { a: 'one', b: 'two', c: 'three' }; + + it('should return true when all iterations are true', () => { + const resultOne = _.every(inputData, (e) => e > 0); + const resultTwo = _.every(inputObject, (e) => typeof e === 'string'); + assert.equal(resultOne, true); + assert.equal(resultTwo, true); + }); + it('should return false when not all iterations are true', () => { + const resultOne = _.every(inputData, (e) => e % 2 === 0); + const resultTwo = _.every(inputObject, (e) => e.length === 3); + assert.equal(resultOne, false); + assert.equal(resultTwo, false); + }); + it('should return true for truthy results when no function is passed in', () => { + assert.equal(_.every(['a', 'b']), true); + assert.equal(_.every({ a: 1, b: 2 }), true); + }); + it('should return false for falsey results when no function is passed in', () => { + assert.equal(_.every(['a', 'b', null]), false); + assert.equal(_.every({ a: 1, b: 2, c: null }), false); + }); + it('callback should take in the current index as an argument if collection is an array', () => { + const input = ['a', 'b']; + const logs = [0, 1]; + _.every(input, (e, i, a) => { + console.log(i); + return typeof e === 'string'; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('should take in the array as an argument if collection is an array', () => { + const input = ['a', 'b']; + _.every(input, (e, i, a) => { + console.log(a); + return typeof e === 'string'; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], ['a', 'b']); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('should take in the current key as an argument if collection is an object', () => { + const input = { a: 1, b: 2 }; + const logs = ['a', 'b']; + _.every(input, (v, k, o) => { + console.log(k); + return typeof v === 'number'; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('should take in the object as an argument if collection is an object', () => { + const input = { a: 1, b: 2 }; + _.every(input, (v, k, o) => { + console.log(o); + return typeof v === 'number'; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], { a: 1, b: 2 }); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('should not have side effects', () => { + assert.deepEqual(inputData, [2, 4, 6, 7, 8]); + }); + }); + + describe('_.some()', () => { + beforeEach(() => { + sinon.spy(console, 'log'); + }); + + afterEach(() => { + console.log.restore(); + }); + + const inputArray = [2, 4, 6, 7, 8]; + const inputObject = { a: 'one', b: 'two', c: 'three' }; + + it('should handle objects', () => { + const result = _.some(inputObject, (e) => e.length > 3); + assert.equal(typeof result, 'boolean'); + }); + it('should return true when at least one iteration is true', () => { + const resultOne = _.some(inputArray, (e) => e % 2 !== 0); + const resultTwo = _.some(inputObject, (e) => e.length > 3); + assert.equal(resultOne, true); + assert.equal(resultTwo, true); + }); + it('should return false when no iterations are true', () => { + const resultOne = _.some(inputArray, (e) => e > 10); + const resultTwo = _.some(inputObject, (e) => e.length > 5); + assert.equal(resultOne, false); + assert.equal(resultTwo, false); + }); + it('should return true for truthy results when no function is passed in', () => { + assert.equal(_.some(inputArray), true); + assert.equal(_.some(inputObject), true); + }); + it('should return false for falsey results when no function is passed in', () => { + assert.equal(_.some([undefined, null]), false); + assert.equal(_.some({ a: undefined, b: null }), false); + }); + it('callback should take in current index as one of its arguments if collection is an array', () => { + const logs = [0, 1, 2]; + _.some([1, 2, 3], (e, i, a) => { + console.log(i); + return e > 0; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in array as one of its arguments if collection is an array', () => { + _.some([1, 2, 3], (e, i, a) => { + console.log(a); + return e > 0; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], [1, 2, 3]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in current key as one of its arguments if collection is an object', () => { + const logs = ['a', 'b']; + _.some({ a: 1, b: 2 }, (v, k, o) => { + console.log(k); + return v > 0; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in object as one of its arguments if collection is an object', () => { + _.some({ a: 1, b: 2 }, (v, k, o) => { + console.log(o); + return v > 0; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], { a: 1, b: 2 }); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('should not have side effects', () => { + assert.deepEqual(inputArray, [2, 4, 6, 7, 8]); + }); + }); + + describe('_.reduce()', () => { + beforeEach(() => { + sinon.spy(console, 'log'); + }); + + afterEach(() => { + console.log.restore(); + }); + + const inputArray = [10, 20, 30, 40]; + + it('should work with an array and a seed', () => { + const result = _.reduce( + inputArray, + (acc, current, i) => { + acc += current; + return acc; + }, + 100, + ); + assert.equal(result, 200); + }); + it('should work without a seed', () => { + const result = _.reduce(inputArray, (acc, current, i) => { + acc += current; + return acc; + }); + assert.equal(result, 100); + }); + it('should work when seed is falsey', () => { + const result = _.reduce( + inputArray, + (acc, current, i) => { + acc += current; + return acc; + }, + 0, + ); + assert.equal(result, 100); + }); + it('callback should take in current index as one of its arguments', () => { + const logs = [0, 1, 1]; + const resultOne = _.reduce( + [1, 2], + (acc, current, i) => { + console.log(i); + acc += current; + return acc; + }, + 0, + ); + const resultTwo = _.reduce([3, 4], (acc, current, i) => { + console.log(i); + acc += current; + return acc; + }); + console.dir(console.log.args); + if (console.log.args.length > 0) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('should not have side effects', () => { + assert.deepEqual(inputArray, [10, 20, 30, 40]); + }); + }); + + describe('_.extend()', () => { + it('should extend an object', () => { + const inputData = { a: 'one', b: 'two' }; + _.extend(inputData, { c: 'three', d: 'four' }); + assert.deepEqual(inputData, { + a: 'one', + b: 'two', + c: 'three', + d: 'four', + }); + }); + it('should overwrite existing properties', () => { + const inputData = { a: 'one', b: 'two' }; + _.extend(inputData, { a: 'three', d: 'four' }); + assert.deepEqual(inputData, { a: 'three', b: 'two', d: 'four' }); + }); + it('should handle any number of arguments', () => { + const inputData = { a: 'one', b: 'two' }; + _.extend( + inputData, + { c: 'three' }, + { d: 'four' }, + { e: 'five' }, + { f: 'six' }, + ); + assert.deepEqual(inputData, { + a: 'one', + b: 'two', + c: 'three', + d: 'four', + e: 'five', + f: 'six', + }); + }); + }); +}); diff --git a/test/underpants.spec.js b/test/underpants.spec.js index d57c829..e22ade7 100644 --- a/test/underpants.spec.js +++ b/test/underpants.spec.js @@ -1,349 +1,810 @@ -const - path = '../underpants', - _ = require(path), - expect = require('chai').expect, - assert = require('chai').assert, - sinon = require('sinon'); +const path = '../underpants', + _ = require(path), + expect = require('chai').expect, + assert = require('chai').assert, + sinon = require('sinon'); -describe('Underpants', function() { - - describe('identity', function() { - it('Should handle numbers.', function() { - expect(_.identity(14)).to.equal(14); - }); - it('Should handle objects.', function() { - expect(_.identity({a: 'one'})).to.eql({a: 'one'}); - }); - it('Should handle strings.', function() { - expect(_.identity('Hello there!')).to.eql('Hello there!'); - }); - it('Should handle arrays.', function() { - expect(_.identity([1,2,3])).to.eql([1,2,3]); +describe('Underpants', () => { + describe('_.identity()', () => { + it('should return input value unchanged', () => { + assert.strictEqual(_.identity(14), 14); + assert.deepEqual(_.identity({ a: 'one' }), { a: 'one' }); + assert.strictEqual(_.identity('hello there'), 'hello there'); + assert.deepEqual(_.identity([1, 2, 3]), [1, 2, 3]); }); }); - describe('typeOf', function() { - it("Should handle strings", function() { - expect(_.typeOf("a")).to.equal("string"); - }); - it("Should handle numbers", function() { - expect(_.typeOf(10)).to.equal("number"); - }); - it("Should handle arrays", function() { - expect(_.typeOf([1,3])).to.equal("array"); - }); - it("Should handle objects", function() { - expect(_.typeOf({a: "one"})).to.equal("object"); - }); - it("Should handle booleans", function() { - expect(_.typeOf(false)).to.equal("boolean"); - }); - it("Should handle undefined", function() { - expect(_.typeOf(undefined)).to.equal("undefined"); - }); - it("Should handle null", function() { - expect(_.typeOf(null)).to.equal("null"); - }); - it("Should handle functions", function() { - expect(_.typeOf(function(){})).to.equal("function"); - }); + describe('_.typeOf()', () => { + const tests = [ + {input: 'hello world', expected: 'string'}, + {input: 100, expected: 'number'}, + {input: false, expected: 'boolean'}, + {input: undefined, expected: 'undefined'}, + {input: function(x,y){return x + y}, expected: 'function'}, + {input: null, expected: 'null'}, + {input: [1, 2, 3], expected: 'array'}, + {input: { a: 1, b: 2 }, expected: 'object'} + ]; + + tests.forEach((e, i) => { + const { input, expected } = e; + it(`should return "${expected}" for ${expected} input`, () => { + assert.equal(_.typeOf(input), expected); + }); + }) }); - describe('first', function() { - it("Should return the first element if no numerical argument is given.", function() { - expect(_.first(["a","b","c"])).to.eql("a"); + describe('_.first()', () => { + it('should accept an argument representing the number of items to include in the output', () => { + assert.deepEqual(_.first(['a', 'b', 'c'], 2), ['a', 'b']); }); - it("Should accept an argument representing the number of items to include in the output.", function() { - expect(_.first(["a","b","c"],2)).to.eql(["a","b"]); + it('should return the first element if no numerical argument is given', () => { + assert.equal(_.first(['a', 'b', 'c']), 'a'); }); - it("Should return empty list if numerical argument is not a positive number.", function() { - expect(_.first(["a","b","c"], -1)).to.eql([]); + it('should return empty array if numerical argument is not a positive number', () => { + assert.deepEqual(_.first(['a', 'b', 'c'], -1), []); }); - it("Should return the whole array if numerical argument is greater than the array's length.", function() { - expect(_.first(["a","b","c"], 5)).to.eql(["a","b","c"]); + it('should return empty array if the array param is not an an array', () => { + assert.deepEqual(_.first({ a: 'b' }, 2), []); }); - it("Should return empty array if the array param is not an array.", function() { - expect(_.first({a:"b"}, 2)).to.eql([]); + it('should return the whole array if the number is greater than the length of the array', () => { + assert.deepEqual(_.first(['a', 'b', 'c'], 5), ['a', 'b', 'c']); }); }); - describe('last', function() { - it("Should return the last element if no numerical argument is given.", function() { - expect(_.last(["a","b","c"])).to.eql("c"); + describe('_.last()', () => { + it('should accept an argument representing the number of items to include in the output', () => { + assert.deepEqual(_.last(['a', 'b', 'c'], 2), ['b', 'c']); }); - it("Should accept an argument representing the number of items to include in the output.", function() { - expect(_.last(["a","b","c"], 2)).to.eql(["b","c"]); + it('should return the last element if no numerical argument is given', () => { + assert.equal(_.last(['a', 'b', 'c']), 'c'); }); - it("Should return empty list if numerical argument is not a positive number.", function() { - expect(_.last(["a","b","c"], -1)).to.eql([]); + it('should return empty array if numerical argument is not a positive number', () => { + assert.deepEqual(_.last(['a', 'b', 'c'], -1), []); }); - it("Should return the whole array if numerical argument is greater than the array's length.", function() { - expect(_.last(["a","b","c"], 5)).to.eql(["a","b","c"]); + it("shoud return the array if the numerical argument is greater than the array's length", () => { + assert.deepEqual(_.last(['a', 'b', 'c'], 5), ['a', 'b', 'c']); }); - it("Should return empty array if the array param is not an array.", function() { - expect(_.last({a:"b"}, 2)).to.eql([]); + it('should return empty array if array param is not an array', () => { + assert.deepEqual(_.last({ a: 'b' }, 2), []); }); }); - - describe('indexOf', function() { - var inputData = ["a", "b", "c", "d"]; - it("Should return the correct index when an element is found.", function() { - expect(_.indexOf(inputData, "b")).to.equal(1); + + describe('_.indexOf()', () => { + const inputData = ['a', 'b', 'c', 'd', 'b']; + it('should return the correct index when an element is found', () => { + assert.deepEqual(_.indexOf(inputData, 'b'), 1); }); - it("Should return the index of the first occurance of a found element.", function() { - expect(_.indexOf(inputData.concat("b"), "b")).to.equal(1); + it('should return the index of the first occurance of a found element if there are duplicates', () => { + assert.deepEqual(_.indexOf(inputData, 'b'), 1); }); - it("Should return -1 if the element is not found.", function() { - expect(_.indexOf(inputData, "e")).to.equal(-1); + it('should return -1 if the element is not found', () => { + assert.deepEqual(_.indexOf(inputData, 'e'), -1); }); - it("Should not have side effects.", function() { - expect(inputData).to.eql(["a", "b", "c", "d"]); + it('should not have side effects', () => { + assert.deepEqual(inputData, ['a', 'b', 'c', 'd', 'b']); }); }); - - describe('contains', function() { - var inputData = [1, "3", 4, 5, "a", "4", "b"]; - it("Should return true if a list contains an element.", function() { - expect(_.contains(inputData, "a")).to.eql(true); + + describe('_.contains()', () => { + const inputData = [1, '3', 4, 5, 'a', '4', 'b']; + it('should return true if a list contains an element', () => { + assert.strictEqual(_.contains(inputData, 'a'), true); }); - it("Should return false if a list doesn't contain an element.", function() { - expect(_.contains(inputData, "c")).to.eql(false); + it("should return false if the list doesn't contain an element", () => { + assert.strictEqual(_.contains(inputData, 'c'), false); }); - it("Should not convert types when checking.", function() { - expect(_.contains(inputData, 3)).to.eql(false); + it('should not convert types when checking', () => { + assert.strictEqual(_.contains(inputData, 3), false); }); - it("Should not have side effects.", function() { - expect(inputData).to.eql([1, "3", 4, 5, "a", "4", "b"]); + it('should not have side effects', () => { + assert.deepEqual(inputData, [1, '3', 4, 5, 'a', '4', 'b']); }); }); - describe('each', function() { - it("Should handle arrays.", function() { - var inputArray = [1, 2, 3, 4, 5]; - _.each(inputArray, function(e, i, a) { - inputArray[i] = e * a.length; + describe('_.each()', () => { + beforeEach(() => { + sinon.spy(console, 'log'); + }); + + afterEach(() => { + console.log.restore(); + }); + it('should handle arrays', () => { + const inputArray = [1, 2, 3, 4, 5]; + const output = []; + _.each(inputArray, function (e, i, a) { + output.push(e * 10); }); - expect(inputArray).to.eql([5, 10, 15, 20, 25]); + assert.deepEqual(output, [10, 20, 30, 40, 50]); }); - it("Should handle Objects.", function() { - var inputObject = { a: "1", b: "2", c: "3", d: "4" }; - _.each(inputObject, function(v, k, o) { - inputObject[v] = k + Object.keys(o).length; - delete inputObject[k]; + it('should handle objects', () => { + const inputObject = { a: '1', b: '2', c: '3', d: '4' }; + const output = []; + _.each(inputObject, function (v, k, o) { + output.push(v + v); + }); + assert.deepEqual(output, ['11', '22', '33', '44']); + }); + it('callback should take in current index as an argument if collection is an array', () => { + const inputArray = ['a', 'b', 'c']; + const logs = [0, 1, 2]; + const output = []; + _.each(inputArray, function (e, i, a) { + console.log(i); + output.push(e.toUpperCase()); + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in array as an argument if collection is an array', () => { + const inputArray = ['a', 'b', 'c']; + const output = []; + _.each(inputArray, function (e, i, a) { + console.log(a); + output.push(e.toUpperCase()); }); - expect(inputObject).to.eql({ 1: "a4", 2: "b4", 3: "c4", 4: "d4" }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], ['a', 'b', 'c']); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in current key as an argument if collection is an object', () => { + const inputObject = { a: 'one', b: 'two' }; + const logs = ['a', 'b']; + _.each(inputObject, (v, k, o) => { + console.log(k); + inputObject[k] = inputObject[k].toUpperCase(); + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in object as an argument if collection is an object', () => { + const inputObject = { a: 'one', b: 'two' }; + const output = []; + _.each(inputObject, (v, k, o) => { + console.log(o); + output.push(inputObject[k].toUpperCase()); + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], { a: 'one', b: 'two' }); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } }); }); - - describe('unique', function() { - var inputData = ["a", 1, 1, "a", "c", false, "b", 5, "c", null, false, null]; - it("Should return an array with no duplicates.", function() { - expect(_.unique(inputData)).to.eql(["a", 1, "c", false, "b", 5, null]); - }); - it("Should not have side effects.", function() { - expect(inputData).to.eql(["a", 1, 1, "a", "c", false, "b", 5, "c", null, false, null]); + + describe('_.unique()', () => { + const inputData = [ + 'a', + 1, + 1, + 'a', + 'c', + false, + 'b', + 5, + 'c', + null, + false, + null, + ]; + it('should return an array with no duplicates', () => { + assert.deepEqual(_.unique(inputData), ['a', 1, 'c', false, 'b', 5, null]); + }); + it('should invoke _.indexOf() method', () => { + const func = _.unique.toString(); + assert.equal(func.includes('_.indexOf('), true); + }); + it('should not have side effects', () => { + assert.deepEqual(inputData, [ + 'a', + 1, + 1, + 'a', + 'c', + false, + 'b', + 5, + 'c', + null, + false, + null, + ]); }); }); - describe('filter', function() { - beforeEach(function() { + describe('_.filter()', () => { + beforeEach(() => { + sinon.spy(console, 'log'); sinon.spy(_, 'each'); - // const each = sinon.spy(_.each); - sinon.spy(_, 'filter'); }); - afterEach(function() { + afterEach(() => { + console.log.restore(); _.each.restore(); - _.filter.restore(); - }); - - var inputData = ["a", 1, "b", 2, "c", 4]; - it("Should filter elements in an array.", function() { - expect(_.filter(inputData, function(e, i, a) { - return typeof e === "string" && i < a.length / 2; - })).to.eql(["a", "b"]); - }); - it("Should not have side effects.", function() { - expect(inputData).to.eql(["a", 1, "b", 2, "c", 4]); - }); - // TODO: Incorporate test to see if each is used - // xit('should use the _.each function', function() { - // var isEven = function(num) { return num % 2 === 0; }; - // expect(_.each.calledOnce).to.be.false; - // - // _.filter([1, 2, 3, 4, 5, 6], isEven); - // - // console.log('each', _.each); + }); + + const inputData = ['a', 1, 'b', 2, 'c', 4]; + + it('should filter elements in an array', () => { + assert.deepEqual( + _.filter(inputData, (e, i, a) => { + return typeof e === 'string'; + }), + ['a', 'b', 'c'], + ); + }); + it('callback function should take in the current index as one of its arguments', () => { + const input = ['a', 'b', 'aa']; + const logs = [0, 1, 2]; + _.filter(input, (e, i, a) => { + console.log(i); + return e.length === 1; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback function should take in the array as one its arguments', () => { + const input = ['a', 'b', 'aa']; + _.filter(input, (e, i, a) => { + console.log(a); + return e.length === 1; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], ['a', 'b', 'aa']); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('should not have side effects', () => { + assert.deepEqual(inputData, ['a', 1, 'b', 2, 'c', 4]); + }); + // it('optional - use _.each', () => { + // _.filter([1, 2, 3, 4], (e) => e % 2 === 0); // expect(_.each.calledOnce).to.be.true; // }); }); - describe('reject', function() { - var inputData = ["a", 1, "b", 2, "c", 4]; - it("Should reject elements in an array.", function() { - expect(_.reject(inputData, function(e, i, a) { - return typeof e === "string" || i < a.length / 2; - })).to.eql([2, 4]); + describe('_.reject()', () => { + beforeEach(() => { + sinon.spy(console, 'log'); }); - it("Should not have side effects.", function() { - expect(inputData).to.eql(["a", 1, "b", 2, "c", 4]); + + afterEach(() => { + console.log.restore(); }); - // TODO: Incorporate test to see if filter is used - }); - describe('partition', function() { - var inputData = ["a", 1, "b", 2, "c", 4]; - it("Should reject elements in an array.", function() { - expect(_.partition(inputData, (e, i, a) => typeof e === "string")) - .to.eql([["a", "b", "c"], [1, 2, 4]]); + const inputData = ['a', 1, 'b', 2, 'c', 4]; + + it('should return an array of items rejected by the callback function', () => { + assert.deepEqual( + _.reject(inputData, (e) => { + return typeof e === 'string'; + }), + [1, 2, 4], + ); + }); + it('callback function should take in the current index as an argument', () => { + const input = ['a', 'b', 'aa']; + const logs = [0, 1, 2]; + _.reject(input, (e, i, a) => { + console.log(i); + return e.length === 2; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback function should take in the array as an argument', () => { + const input = ['a', 'b', 'aa']; + _.reject(input, (e, i, a) => { + console.log(a); + return e.length === 2; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], ['a', 'b', 'aa']); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } }); - it("Should not have side effects.", function() { - expect(inputData).to.eql(["a", 1, "b", 2, "c", 4]); + it('should not have side effects', () => { + assert.deepEqual(inputData, ['a', 1, 'b', 2, 'c', 4]); }); - // TODO: Add tests to check if filter and reject are used - // xit('should use the _.filter function', function() { - // expect(_.filter.called).to.be.true; - // }); - // - // xit('should use the _.reject function', function() { - // expect(_.reject.called).to.be.true; - // }); }); - describe('map', function() { - var inputArray = ["a", "b", "c", "d"]; - var inputObject = { a: 1, b: 2, c: 3, d: 4 }; - it("Should map through arrays.", function() { - expect(_.map(inputArray, function(e, i, a) { - return e + i * a.length; - })).to.eql(["a0", "b4", "c8", "d12"]); - }); - it("Should map through Objects.", function() { - expect(_.map(inputObject, function(v, k, o) { - return k + v * Object.keys(o).length; - })).to.eql(["a4", "b8", "c12", "d16"]); - }); - it("Should not have side effects.", function() { - expect([inputArray, inputObject]).to.eql([["a", "b", "c", "d"], { a: 1, b: 2, c: 3, d: 4 }]); - }); - // TODO: add test to see if each is used - // xit('should use the _.each function', function() { - // console.log(_.map.toString()); - // expect(_.map.toString()).to.contain('_.each'); - // }); - }); + describe('_.partition()', () => { + beforeEach(() => { + sinon.spy(console, 'log'); + }); - describe('pluck', function() { - var inputData = [{ name: "Ralph", age: 22 }, { name: "Jimmy", age: 13 }, { name: "Carla", age: 20 }]; - it("Should pluck properties out of a list of objects.", function() { - expect(_.pluck(inputData, "name")).to.eql(["Ralph", "Jimmy", "Carla"]); + afterEach(() => { + console.log.restore(); }); - it("Should not have side effects.", function() { - expect(inputData).to.eql([{ name: "Ralph", age: 22 }, { name: "Jimmy", age: 13 }, { name: "Carla", age: 20 }]); + const inputData = ['a', 1, 'b', 2, 'c', 4]; + + it('should create a correctly partitioned array of subarrays', () => { + assert.deepEqual( + _.partition(inputData, (e) => { + return typeof e === 'string'; + }), + [ + ['a', 'b', 'c'], + [1, 2, 4], + ], + ); + }); + it('callback function should take in the current index as an argument', () => { + const input = ['a', 1]; + const logs = [0, 1]; + _.partition(input, (e, i, a) => { + console.log(i); + return typeof e === 'string'; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback function should take in the array as an argument', () => { + const input = ['a', 1]; + const logs = [ + ['a', 1], + ['a', 1], + ]; + _.partition(input, (e, i, a) => { + console.log(a); + return typeof e === 'string'; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } }); + it('should not have side effects', () => { + assert.deepEqual(inputData, ['a', 1, 'b', 2, 'c', 4]); + }); }); - describe('every', function() { - var inputData = [2, 4, 6, 7, 8]; - var inputDataTruthy = [1, [], true, "a"]; - var inputDataFalsy = ["", 0, false, null]; - var inputObject = { a: "one", b: "two", c: "three" }; - it("Should return true when all iterations are true", function() { - expect(_.every(inputData, function(v) { - return v % 2 === 0 || v === 7; - })).to.equal(true); + describe('_.map', () => { + beforeEach(() => { + sinon.spy(console, 'log'); }); - it("Should return false when not all iterations are true", function() { - expect(_.every(inputData, function(v) { - return v % 2 === 0; - })).to.equal(false); + + afterEach(() => { + console.log.restore(); }); - it("Should handle objects", function() { - expect(_.every(inputObject, function(v, k, o) { - return ["aone3", "btwo3", "cthree3"].indexOf(k + v + Object.keys(o).length) !== -1; - })).to.equal(true); + + const inputArray = ['a', 'b', 'c', 'd']; + const inputObject = { a: 1, b: 2, c: 3, d: 4 }; + + it('should correctly map an array', () => { + const result = _.map(inputArray, (e) => e.toUpperCase()); + assert.deepEqual(result, ['A', 'B', 'C', 'D']); + }); + it('should correctly map an object', () => { + const result = _.map(inputObject, (e) => e * 10); + assert.deepEqual(result, [10, 20, 30, 40]); + }); + it('callback should take in the current index as an argument if collection is an array', () => { + const logs = [0, 1, 2, 3]; + _.map(inputArray, (e, i, a) => { + console.log(i); + return e.toUpperCase(); + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in the array as argument if collection is an array', () => { + _.map(inputArray, (e, i, a) => { + console.log(a); + return e.toUpperCase(); + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], ['a', 'b', 'c', 'd']); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in the current key as an argument if collection is an argument', () => { + const logs = ['a', 'b', 'c', 'd']; + _.map(inputObject, (v, k, o) => { + console.log(k); + return v * 10; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in the object as an argument if collection is an argument', () => { + _.map(inputObject, (v, k, o) => { + console.log(o); + return v * 10; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], inputObject); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('should not have side effects', () => { + assert.deepEqual(inputArray, ['a', 'b', 'c', 'd']); + assert.deepEqual(inputObject, { a: 1, b: 2, c: 3, d: 4 }); }); - it("Should return true for truthy results when no function is passed in.", function() { - expect(_.every(inputDataTruthy)).to.equal(true); + }); + + describe('_.pluck()', () => { + beforeEach(() => { + sinon.spy(_, 'map'); + }); + afterEach(() => { + _.map.restore(); + }); + const inputData = [ + { name: 'Ralph', age: 22 }, + { name: 'Jimmy', age: 13 }, + { name: 'Carla', age: 20 }, + ]; + it('should pluck properties from a list of objects', () => { + const result = _.pluck(inputData, 'name'); + const correct = ['Ralph', 'Jimmy', 'Carla']; + assert.deepEqual(result, correct); + }); + it('should invoke the _.map() method', () => { + _.pluck(inputData, 'age'); + expect(_.map.calledOnce).to.be.true; + }); + it('should not have side effects', () => { + _.pluck(inputData, 'name'); + assert.deepEqual(inputData, [ + { name: 'Ralph', age: 22 }, + { name: 'Jimmy', age: 13 }, + { name: 'Carla', age: 20 }, + ]); }); - it("Should return false for falsy results when no function is passed in.", function() { - expect(_.every(inputDataFalsy)).to.equal(false); + }); + + describe('_.every()', () => { + beforeEach(() => { + sinon.spy(console, 'log'); }); - it("Should not have side effects.", function() { - expect(inputData).to.eql([2, 4, 6, 7, 8]); + + afterEach(() => { + console.log.restore(); }); - }); - describe('some', function() { - var inputData = [2, 4, 6, 7, 8]; - var inputDataTruthy = [1, [], true, "a"]; - var inputDataFalsy = ["", 0, false, null]; - var inputObject = { a: "one", b: "two", c: "three" }; - it("Should return true when at least one iteration is true", function() { - expect(_.some(inputData, function(v) { - return v === 7; - })).to.equal(true); + const inputData = [2, 4, 6, 7, 8]; + const inputObject = { a: 'one', b: 'two', c: 'three' }; + + it('should return true when all iterations are true', () => { + const resultOne = _.every(inputData, (e) => e > 0); + const resultTwo = _.every(inputObject, (e) => typeof e === 'string'); + assert.equal(resultOne, true); + assert.equal(resultTwo, true); + }); + it('should return false when not all iterations are true', () => { + const resultOne = _.every(inputData, (e) => e % 2 === 0); + const resultTwo = _.every(inputObject, (e) => e.length === 3); + assert.equal(resultOne, false); + assert.equal(resultTwo, false); + }); + it('should return true for truthy results when no function is passed in', () => { + assert.equal(_.every(['a', 'b']), true); + assert.equal(_.every({ a: 1, b: 2 }), true); + }); + it('should return false for falsey results when no function is passed in', () => { + assert.equal(_.every(['a', 'b', null]), false); + assert.equal(_.every({ a: 1, b: 2, c: null }), false); + }); + it('callback should take in the current index as an argument if collection is an array', () => { + const input = ['a', 'b']; + const logs = [0, 1]; + _.every(input, (e, i, a) => { + console.log(i); + return typeof e === 'string'; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('should take in the array as an argument if collection is an array', () => { + const input = ['a', 'b']; + _.every(input, (e, i, a) => { + console.log(a); + return typeof e === 'string'; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], ['a', 'b']); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('should take in the current value as an argument if collection is an object', () => { + const input = { a: 1, b: 2 }; + const logs = ['a', 'b']; + _.every(input, (v, k, o) => { + console.log(k); + return typeof v === 'number'; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('should take in the object as an argument if collection is an object', () => { + const input = { a: 1, b: 2 }; + _.every(input, (v, k, o) => { + console.log(o); + return typeof v === 'number'; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], { a: 1, b: 2 }); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } }); - it("Should return false when no iterations are true", function() { - expect(_.some(inputData, function(v) { - return v > 10; - })).to.equal(false); + it('should not have side effects', () => { + assert.deepEqual(inputData, [2, 4, 6, 7, 8]); }); - it("Should handle objects", function() { - expect(_.some(inputObject, function(v, k, o) { - return ["aone3", "btwo3"].indexOf(k + v + Object.keys(o).length) !== -1; - })).to.equal(true); + }); + + describe('_.some()', () => { + beforeEach(() => { + sinon.spy(console, 'log'); }); - it("Should return true for truthy results when no function is passed in.", function() { - expect(_.some(inputDataTruthy)).to.equal(true); + + afterEach(() => { + console.log.restore(); }); - it("Should return false for falsy results when no function is passed in.", function() { - expect(_.some(inputDataFalsy)).to.equal(false); + + const inputArray = [2, 4, 6, 7, 8]; + const inputObject = { a: 'one', b: 'two', c: 'three' }; + + it('should handle objects', () => { + const result = _.some(inputObject, (e) => e.length > 3); + assert.equal(typeof result, 'boolean'); + }); + it('should return true when at least one iteration is true', () => { + const resultOne = _.some(inputArray, (e) => e % 2 !== 0); + const resultTwo = _.some(inputObject, (e) => e.length > 3); + assert.equal(resultOne, true); + assert.equal(resultTwo, true); + }); + it('should return false when no iterations are true', () => { + const resultOne = _.some(inputArray, (e) => e > 10); + const resultTwo = _.some(inputObject, (e) => e.length > 5); + assert.equal(resultOne, false); + assert.equal(resultTwo, false); + }); + it('should return true for truthy results when no function is passed in', () => { + assert.equal(_.some(inputArray), true); + assert.equal(_.some(inputObject), true); + }); + it('should return false for falsey results when no function is passed in', () => { + assert.equal(_.some([undefined, null]), false); + assert.equal(_.some({ a: undefined, b: null }), false); + }); + it('callback should take in current index as one of its arguments if collection is an array', () => { + const logs = [0, 1, 2]; + _.some([1, 2, 3], (e, i, a) => { + console.log(i); + return e > 0; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in array as one of its arguments if collection is an array', () => { + _.some([1, 2, 3], (e, i, a) => { + console.log(a); + return e > 0; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], [1, 2, 3]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in current key as one of its arguments if collection is an object', () => { + const logs = ['a', 'b']; + _.some({ a: 1, b: 2 }, (v, k, o) => { + console.log(k); + return v > 0; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } + }); + it('callback should take in object as one of its arguments if collection is an object', () => { + _.some({ a: 1, b: 2 }, (v, k, o) => { + console.log(o); + return v > 0; + }); + if (console.log.args.length) { + console.log.args.forEach((e, i) => { + assert.deepEqual(e[0], { a: 1, b: 2 }); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } }); - it("Should not have side effects.", function() { - expect(inputData).to.eql([2, 4, 6, 7, 8]); + it('should not have side effects', () => { + assert.deepEqual(inputArray, [2, 4, 6, 7, 8]); }); }); - describe('reduce', function() { - var inputArray = [10,20,30,40]; - it("Should work with an array and a seed", function() { - expect(_.reduce(inputArray, function(memo, element, i){ - return memo + element + i; - }, 10)).to.equal(116); + describe('_.reduce()', () => { + beforeEach(() => { + sinon.spy(console, 'log'); }); - it("Should work without a seed", function() { - expect(_.reduce(inputArray, function(memo, element, i){ - return memo * element * (i+1); - })).to.equal(5760000); + + afterEach(() => { + console.log.restore(); }); - it("Should work when seed is falsy", function() { - expect(_.reduce(inputArray, function(memo, element, i){ - return memo * element * (i+1); - }, 0)).to.equal(0); + + const inputArray = [10, 20, 30, 40]; + + it('should work with an array and a seed', () => { + const result = _.reduce( + inputArray, + (acc, current, i) => { + acc += current; + return acc; + }, + 100, + ); + assert.equal(result, 200); + }); + it('should work without a seed', () => { + const result = _.reduce(inputArray, (acc, current, i) => { + acc += current; + return acc; + }); + assert.equal(result, 100); + }); + it('should work when seed is falsey', () => { + const result = _.reduce( + inputArray, + (acc, current, i) => { + acc += current; + return acc; + }, + 0, + ); + assert.equal(result, 100); + }); + it('callback should take in current index as one of its arguments', () => { + const logs = [0, 1, 1]; + const resultOne = _.reduce( + [1, 2], + (acc, current, i) => { + console.log(i); + acc += current; + return acc; + }, + 0, + ); + const resultTwo = _.reduce([3, 4], (acc, current, i) => { + console.log(i); + acc += current; + return acc; + }); + if (console.log.args.length > 0) { + console.log.args.forEach((e, i) => { + assert.equal(e[0], logs[i]); + }); + } else { + assert.equal(console.log.args.length > 0, true); + } }); - it("Should not have side effects", function() { - expect(inputArray).to.eql([10, 20, 30, 40]); + it('should not have side effects', () => { + assert.deepEqual(inputArray, [10, 20, 30, 40]); }); }); - describe('extend', function() { - it("Should extend an object.", function() { - var inputData = { a: "one", b: "two" }; - _.extend(inputData, { c: "three", d: "four" }); - expect(inputData).to.eql({ a: "one", b: "two", c: "three", d: "four" }); - }); - it("Should overwrite existing properties", function() { - var inputData = { a: "one", b: "two" }; - _.extend(inputData, { a: "three", d: "four" }); - expect(inputData).to.eql({ a: "three", b: "two", d: "four" }); - }); - it("Should handle any number of arguments.", function() { - var inputData = { a: "one", b: "two" }; - _.extend(inputData, { a: "three", c: "four" }, { d: "five", c: "six" }); - expect(inputData).to.eql({ a: "three", b: "two", c: "six", d: "five" }); + describe('_.extend()', () => { + it('should extend an object', () => { + const inputData = { a: 'one', b: 'two' }; + _.extend(inputData, { c: 'three', d: 'four' }); + assert.deepEqual(inputData, { + a: 'one', + b: 'two', + c: 'three', + d: 'four', + }); + }); + it('should overwrite existing properties', () => { + const inputData = { a: 'one', b: 'two' }; + _.extend(inputData, { a: 'three', d: 'four' }); + assert.deepEqual(inputData, { a: 'three', b: 'two', d: 'four' }); + }); + it('should handle any number of arguments', () => { + const inputData = { a: 'one', b: 'two' }; + _.extend( + inputData, + { c: 'three' }, + { d: 'four' }, + { e: 'five' }, + { f: 'six' }, + ); + assert.deepEqual(inputData, { + a: 'one', + b: 'two', + c: 'three', + d: 'four', + e: 'five', + f: 'six', + }); }); }); }); diff --git a/underpants.js b/underpants.js index fc531b4..a632bbb 100644 --- a/underpants.js +++ b/underpants.js @@ -3,175 +3,185 @@ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode 'use strict'; -var _ = {}; - +const _ = {}; /** -* START OF OUR LIBRARY! -* Implement each function below its instructions -*/ + * START OF OUR LIBRARY! + * Implement each function below its instructions + */ /** _.identity -* Arguments: -* 1) Any value -* Objectives: -* 1) Returns unchanged -* Examples: -* _.identity(5) === 5 -* _.identity({a: "b"}) === {a: "b"} -*/ - + * Arguments: + * 1) Any value + * Objectives: + * 1) Returns unchanged + * Examples: + * _.identity(5) === 5 + * _.identity({a: "b"}) === {a: "b"} + */ /** _.typeOf -* Arguments: -* 1) Any value -* Objectives: -* 1) Return the type of as a string -* Types are one of: -* - "string" -* - "array" -* - "object" -* - "undefined" -* - "number" -* - "boolean" -* - "null" -* - "function" -* Examples: -* _.typeOf(134) -> "number" -* _.typeOf("javascript") -> "string" -* _.typeOf([1,2,3]) -> "array" -*/ - + * Arguments: + * 1) Any value + * Objectives: + * 1) Return the type of as a string + * Types are one of: + * - "string" + * - "array" + * - "object" + * - "undefined" + * - "number" + * - "boolean" + * - "null" + * - "function" + * Examples: + * _.typeOf(134) -> "number" + * _.typeOf("javascript") -> "string" + * _.typeOf([1,2,3]) -> "array" + */ + +_.typeOf = function(value){ + if (typeof value !== 'object'){ + return typeof value; + } else if (Array.isArray(value)){ + return 'array'; + } else if (value === null){ + return 'null' + } else { + return 'object'; + } +} /** _.first -* Arguments: -* 1) An array -* 2) A number -* Objectives: -* 1) If is not an array, return [] -* 2) If is not given or not a number, return just the first element in . -* 3) Otherwise, return the first items of -* Edge Cases: -* 1) What if is negative? -* 2) What if is greater than .length? -* Examples: -* _.first("ponies", 1) -> [] -* _.first(["a", "b", "c"], "ponies") -> "a" -* _.first(["a", "b", "c"], 1) -> "a" -* _.first(["a", "b", "c"], 2) -> ["a", "b"] -*/ - + * Arguments: + * 1) An array + * 2) A number + * Objectives: + * 1) If is not an array, return [] + * 2) If is not given or not a number, return just the first element in . + * 3) Otherwise, return the first items of + * Edge Cases: + * 1) What if is negative? + * 2) What if is not provided? + * 3) What if is greater than the length of the array? + * 3) What if is not an array? + * Examples: + * _.first("ponies", 1) -> [] + * _.first(["a", "b", "c"], -1) -> [] + * _.first(["a", "b", "c"]) -> "a" + * _.first(["a", "b", "c"], 2) -> ["a", "b"] + * _.first("a", "b", "c", 5) -> ["a", "b", "c"] + */ /** _.last -* Arguments: -* 1) An array -* 2) A number -* Objectives: -* 1) If is not an array, return [] -* 2) If is not given or not a number, return just the last element in . -* 3) Otherwise, return the last items of -* Edge Cases: -* 1) What if is negative? -* 2) What if is greater than .length? -* Examples: -* _.last("ponies", 2) -> [] -* _.last(["a", "b", "c"], "ponies") -> "c" -* _.last(["a", "b", "c"], 1) -> "c" -* _.last(["a", "b", "c"], 2) -> ["b", "c"] -*/ - + * Arguments: + * 1) An array + * 2) A number + * Objectives: + * 1) If is not an array, return [] + * 2) If is not given or not a number, return just the last element in . + * 3) Otherwise, return the last items of + * Edge Cases: + * 1) What if is negative? + * 2) What if is greater than .length? + * 3) What if is not provided? + * 4) What if is not an array? + * Examples: + * _.last("ponies", 2) -> [] + * _.last(["a", "b", "c"], -1) -> [] + * _.last(["a", "b", "c"]) -> "c" + * _.last(["a", "b", "c"], 5) -> ["a", "b", "c"] + * _.last(["a", "b", "c"], 2) -> ["b", "c"] + */ /** _.indexOf -* Arguments: -* 1) An array -* 2) A value -* Objectives: -* 1) Return the index of that is the first occurrance of -* 2) Return -1 if is not in -* 3) Do not use [].indexOf()! -* Edge Cases: -* 1) What if has multiple occurances of val? -* 2) What if isn't in ? -* Examples: -* _.indexOf(["a","b","c"], "c") -> 2 -* _.indexOf(["a","b","c"], "d") -> -1 -*/ - + * Arguments: + * 1) An array + * 2) A value + * Objectives: + * 1) Return the index of that is the first occurrance of + * 2) Return -1 if is not in + * 3) Do not use [].indexOf() + * Edge Cases: + * 1) What if has multiple occurances of val? + * 2) What if isn't in ? + * Examples: + * _.indexOf(["a","b","c"], "c") -> 2 + * _.indexOf(["a","b","c"], "d") -> -1 + */ /** _.contains -* Arguments: -* 1) An array -* 2) A value -* Objectives: -* 1) Return true if contains -* 2) Return false otherwise -* 3) You must use the ternary operator in your implementation. -* Edge Cases: -* 1) did you use === ? -* 2) what if no is given? -* Examples: -* _.contains([1,"two", 3.14], "two") -> true -*/ - + * Arguments: + * 1) An array + * 2) A value + * Objectives: + * 1) Return true if contains + * 2) Return false otherwise + * 3) You must use the ternary operator in your implementation. + * Edge Cases: + * 1) did you use === ? + * 2) what if no is given? + * Examples: + * _.contains([1,"two", 3.14], "two") -> true + */ /** _.each -* Arguments: -* 1) A collection -* 2) A function -* Objectives: -* 1) if is an array, call once for each element -* with the arguments: -* the element, it's index, -* 2) if is an object, call once for each property -* with the arguments: -* the property's value, it's key, -* Examples: -* _.each(["a","b","c"], function(e,i,a){ console.log(e)}); -* -> should log "a" "b" "c" to the console -*/ - + * Arguments: + * 1) A collection + * 2) A function + * Objectives: + * 1) if is an array, call once for each element + * with the arguments: + * the element, it's index, + * 2) if is an object, call once for each property + * with the arguments: + * the property's value, it's key, + * Examples: + * _.each(["a","b","c"], function(e,i,a){ console.log(e)}); + * -> should log "a" "b" "c" to the console + * + * _.each({ a: 'one', b: 'two'}, function(v, k, o){ console.log(v)}); + * -> should log "one" "two" to the console + */ /** _.unique -* Arguments: -* 1) An array -* Objectives: -* 1) Return a new array of all elements from with duplicates removed -* 2) Use _.indexOf() from above -* Examples: -* _.unique([1,2,2,4,5,6,5,2]) -> [1,2,4,5,6] -*/ - + * Arguments: + * 1) An array + * Objectives: + * 1) Return a new array of all elements from with duplicates removed + * 2) Use _.indexOf() from above + * Examples: + * _.unique([1,2,2,4,5,6,5,2]) -> [1,2,4,5,6] + */ /** _.filter -* Arguments: -* 1) An array -* 2) A function -* Objectives: -* 1) call for each element in passing the arguments: -* the element, it's index, -* 2) return a new array of elements for which calling returned true -* Edge Cases: -* 1) What if returns something other than true or false? -* Examples: -* _.filter([1,2,3,4,5], function(x){return x%2 === 0}) -> [2,4] -* Extra Credit: -* use _.each in your implementation -*/ - + * Arguments: + * 1) An array + * 2) A function + * Objectives: + * 1) call for each element in passing the arguments: + * the element, it's index, + * 2) return a new array of elements for which calling returned true + * Edge Cases: + * 1) What if returns something other than true or false? + * Examples: + * _.filter([1,2,3,4,5], function(x){return x%2 === 0}) -> [2,4] + * Extra Credit: + * use _.each in your implementation + */ /** _.reject -* Arguments: -* 1) An array -* 2) A function -* Objectives: -* 1) call for each element in passing the arguments: -* the element, it's index, -* 2) return a new array of elements for which calling returned false -* 3) This is the logical inverse if _.filter() -* Examples: -* _.reject([1,2,3,4,5], function(e){return e%2 === 0}) -> [1,3,5] -*/ - + * Arguments: + * 1) An array + * 2) A function + * Objectives: + * 1) call for each element in passing the arguments: + * the element, it's index, + * 2) return a new array of elements for which calling returned false + * 3) This is the logical inverse if _.filter() + * Examples: + * _.reject([1,2,3,4,5], function(e){return e%2 === 0}) -> [1,3,5] + */ /** _.partition * Arguments: @@ -192,121 +202,117 @@ var _ = {}; } */ - /** _.map -* Arguments: -* 1) A collection -* 2) a function -* Objectives: -* 1) call for each element in passing the arguments: -* if is an array: -* the element, it's index, -* if is an object: -* the value, it's key, -* 2) save the return value of each call in a new array -* 3) return the new array -* Examples: -* _.map([1,2,3,4], function(e){return e * 2}) -> [2,4,6,8] -*/ - + * Arguments: + * 1) A collection + * 2) a function + * Objectives: + * 1) call for each element in passing the arguments: + * if is an array: + * the element, it's index, + * if is an object: + * the value, it's key, + * 2) save the return value of each call in a new array + * 3) return the new array + * Examples: + * _.map([1,2,3,4], function(e){return e * 2}) -> [2,4,6,8] + */ /** _.pluck -* Arguments: -* 1) An array of objects -* 2) A property -* Objectives: -* 1) Return an array containing the value of for every element in -* 2) You must use _.map() in your implementation. -* Examples: -* _.pluck([{a: "one"}, {a: "two"}], "a") -> ["one", "two"] -*/ - + * Arguments: + * 1) An array of objects + * 2) A property + * Objectives: + * 1) Return an array containing the value of for every element in + * 2) You must use _.map() in your implementation. + * Examples: + * _.pluck([{a: "one"}, {a: "two"}], "a") -> ["one", "two"] + */ /** _.every -* Arguments: -* 1) A collection -* 2) A function -* Objectives: -* 1) Call for every element of with the paramaters: -* if is an array: -* current element, it's index, -* if is an object: -* current value, current key, -* 2) If the return value of calling for every element is true, return true -* 3) If even one of them returns false, return false -* 4) If is not provided, return true if every element is truthy, otherwise return false -* Edge Cases: -* 1) what if doesn't return a boolean -* 2) What if is not given? -* Examples: -* _.every([2,4,6], function(e){return e % 2 === 0}) -> true -* _.every([1,2,3], function(e){return e % 2 === 0}) -> false -*/ - + * Arguments: + * 1) A collection + * 2) A function + * Objectives: + * 1) Call for every element of with the paramaters: + * if is an array: + * current element, it's index, + * if is an object: + * current value, current key, + * 2) If the return value of calling for every element is true, return true + * 3) If even one of them returns false, return false + * 4) If is not provided, return true if every element is truthy, otherwise return false + * Edge Cases: + * 1) what if doesn't return a boolean + * 2) What if is not given? + * Examples: + * _.every([2,4,6], function(e){return e % 2 === 0}) -> true + * _.every([1,2,3], function(e){return e % 2 === 0}) -> false + */ /** _.some -* Arguments: -* 1) A collection -* 2) A function -* Objectives: -* 1) Call for every element of with the paramaters: -* if is an array: -* current element, it's index, -* if is an object: -* current value, current key, -* 2) If the return value of calling is true for at least one element, return true -* 3) If it is false for all elements, return false -* 4) If is not provided return true if at least one element is truthy, otherwise return false -* Edge Cases: -* 1) what if doesn't return a boolean -* 2) What if is not given? -* Examples: -* _.some([1,3,5], function(e){return e % 2 === 0}) -> false -* _.some([1,2,3], function(e){return e % 2 === 0}) -> true -*/ - + * Arguments: + * 1) A collection + * 2) A function + * Objectives: + * 1) Call for every element of with the paramaters: + * if is an array: + * current element, it's index, + * if is an object: + * current value, current key, + * 2) If the return value of calling is true for at least one element, return true + * 3) If it is false for all elements, return false + * 4) If is not provided return true if at least one element is truthy, otherwise return false + * Edge Cases: + * 1) what if doesn't return a boolean + * 2) What if is not given? + * Examples: + * _.some([1,3,5], function(e){return e % 2 === 0}) -> false + * _.some([1,2,3], function(e){return e % 2 === 0}) -> true + */ /** _.reduce -* Arguments: -* 1) An array -* 2) A function -* 3) A seed -* Objectives: -* 1) Call for every element in passing the arguments: -* previous result, element, index -* 2) Use the return value of as the "previous result" -* for the next iteration -* 3) On the very first iteration, use as the "previous result" -* 4) If no was given, use the first element/value of as and continue to the next element -* 5) After the last iteration, return the return value of the final call -* Edge Cases: -* 1) What if is not given? -* Examples: -* _.reduce([1,2,3], function(previousSum, currentValue, currentIndex){ return previousSum + currentValue }, 0) -> 6 -*/ - + * Arguments: + * 1) An array + * 2) A function + * 3) A seed + * Objectives: + * 1) Call for every element in passing the arguments: + * previous result, element, index + * 2) Use the return value of as the "previous result" + * for the next iteration + * 3) On the very first iteration, use as the "previous result" + * 4) If no was given, use the first element/value of as and continue to the next element + * 5) After the last iteration, return the return value of the final call + * Edge Cases: + * 1) What if is not given? + * Examples: + * _.reduce([1,2,3], function(previousSum, currentValue, currentIndex){ return previousSum + currentValue }, 0) -> 6 + */ /** _.extend -* Arguments: -* 1) An Object -* 2) An Object -* ...Possibly more objects -* Objectives: -* 1) Copy properties from to -* 2) If more objects are passed in, copy their properties to as well, in the order they are passed in. -* 3) Return the update -* Examples: -* var data = {a:"one"}; -* _.extend(data, {b:"two"}); -> data now equals {a:"one",b:"two"} -* _.extend(data, {a:"two"}); -> data now equals {a:"two"} -*/ - -////////////////////////////////////////////////////////////////////// -// DON'T REMOVE THIS CODE //////////////////////////////////////////// -////////////////////////////////////////////////////////////////////// - -if((typeof process !== 'undefined') && - (typeof process.versions.node !== 'undefined')) { - // here, export any references you need for tests // - module.exports = _; + * Arguments: + * 1) An Object + * 2) An Object + * ...Possibly more objects + * Objectives: + * 1) Copy properties from to + * 2) If more objects are passed in, copy their properties to as well, in the order they are passed in. + * 3) Return the update + * Examples: + * var data = {a:"one"}; + * _.extend(data, {b:"two"}); -> data now equals {a:"one",b:"two"} + * _.extend(data, {a:"two"}); -> data now equals {a:"two"} + */ + +//////////////////////////// +// DON'T REMOVE THIS CODE // +//////////////////////////// + +if ( + typeof process !== 'undefined' && + typeof process.versions.node !== 'undefined' +) { + // here, export any references you need for tests // + module.exports = _; }