├── .github └── workflows │ ├── master.yml │ └── pr.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── TEMPLATE.md ├── configuring.md ├── docs ├── .nojekyll ├── README.md └── index.html ├── index.js ├── lib └── rules │ ├── all.js │ └── rules.json ├── package-lock.json ├── package.json └── tests ├── index.js ├── lib └── rules │ └── all.js └── unit └── all.js /.github/workflows/master.yml: -------------------------------------------------------------------------------- 1 | name: Master 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | # Sets the GITHUB_TOKEN permissions to allow deployment to GitHub Pages 11 | permissions: 12 | contents: write 13 | pages: write 14 | id-token: write 15 | 16 | jobs: 17 | test: 18 | name: Test 19 | runs-on: ubuntu-latest 20 | environment: testing 21 | steps: 22 | - name: 👌🏻 Checkout 23 | uses: actions/checkout@v3 24 | - name: Install dependencies 25 | run: npm ci 26 | - name: 🔎 Run tests 27 | run: npm run test 28 | - name: Run unit tests 29 | run: npm run test:unit 30 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: PR 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | # Allows you to run this workflow manually from the Actions tab 11 | workflow_dispatch: 12 | 13 | # Sets the GITHUB_TOKEN permissions to allow deployment to GitHub Pages 14 | permissions: 15 | contents: write 16 | pages: write 17 | id-token: write 18 | pull-requests: write 19 | 20 | jobs: 21 | test: 22 | name: Test 23 | runs-on: ubuntu-latest 24 | environment: testing 25 | steps: 26 | - name: 👌🏻 Checkout 27 | uses: actions/checkout@v3 28 | - name: Install Dependencies 29 | run: npm ci 30 | - name: 🔎 Run tests 31 | run: npm run test 32 | - name: Run unit tests 33 | run: npm run test:unit 34 | 35 | set-automerge: 36 | name: Approve and automerge (only dependanbot PRs) 37 | runs-on: ubuntu-latest 38 | environment: testing 39 | needs: [test] 40 | if: ${{ github.actor == 'dependabot[bot]' }} 41 | steps: 42 | - name: Checkout 43 | uses: actions/checkout@v3 44 | - name: Dependabot metadata 45 | id: dependabot-metadata 46 | uses: dependabot/fetch-metadata@v1 47 | with: 48 | github-token: "${{ secrets.GITHUB_TOKEN }}" 49 | - name: Approve a PR 50 | run: gh pr review --approve "$PR_URL" 51 | env: 52 | PR_URL: ${{github.event.pull_request.html_url}} 53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 54 | - name: Enable auto-merge for Dependabot PRs 55 | if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }} 56 | run: gh pr merge --auto --squash "$PR_URL" 57 | env: 58 | PR_URL: ${{ github.event.pull_request.html_url }} 59 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | npm-debug.log 4 | /.vscode 5 | yarn.lock -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 10 5 | - 12 6 | - 14 7 | 8 | script: 9 | - yarn test 10 | - yarn test:unit 11 | 12 | after_success: 13 | - yarn coveralls 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 You-Dont-Need 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [You don't (may not) need Lodash/Underscore](https://you-dont-need.github.io/You-Dont-Need-Lodash-Underscore/#/) 2 | 3 | [![Join the community on Spectrum](https://withspectrum.github.io/badge/badge.svg)](https://spectrum.chat/you-dont-need/lodash-underscore) 4 | [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/cht8687/You-Dont-Need-Lodash-Underscore) 5 | 6 | Lodash and Underscore are great modern JavaScript utility libraries, and they are widely used by Front-end developers. However, when you are targeting modern browsers, you may find out that there are many methods which are already supported natively thanks to ECMAScript5 [ES5] and ECMAScript2015 [ES6]. If you want your project to require fewer dependencies, and you know your target browser clearly, then you may not need Lodash/Underscore. 7 | 8 | You are welcome to contribute with more items provided below. 9 | 10 | * If you are targeting legacy JavaScript engine with those ES5 methods, you can use [es5-shim](https://github.com/es-shims/es5-shim) 11 | 12 | * Please note that, the examples used below are just showing you the native alternative of performing certain tasks. For some functions, Lodash provides you more options than native built-ins. This list is not a 1:1 comparison. 13 | 14 | * Please send a PR if you want to add or modify the code. No need to open an issue unless it's something big and you want to discuss. 15 | 16 | 17 | ## Voice of Developers 18 | 19 | > [Make use of native JavaScript object and array utilities before going big.](https://twitter.com/codylindley/status/692356631007342593) 20 | 21 | > —Cody Lindley, Author of [jQuery Cookbook](http://shop.oreilly.com/product/9780596159788.do) and [JavaScript Enlightenment](http://shop.oreilly.com/product/0636920027713.do) 22 | 23 | 24 | > [You probably don't need Lodash. Nice List of JavaScript methods which you can use natively.](https://twitter.com/daniellmb/status/692200768556916740) 25 | 26 | > —Daniel Lamb, Computer Scientist, Technical Reviewer of [Secrets of the JavaScript Ninja](https://www.manning.com/books/secrets-of-the-javascript-ninja-second-edition) and [Functional Programming in JavaScript](https://www.manning.com/books/functional-programming-in-javascript) 27 | 28 | 29 | > [I guess not, but I want it.](https://twitter.com/teropa/status/692280179666898944) 30 | 31 | > —Tero Parviainen, Author of [build-your-own-angular](http://teropa.info/build-your-own-angular) 32 | 33 | 34 | > [I'll admit, I've been guilty of overusing #lodash. Excellent resource.](https://twitter.com/therebelrobot/status/692907269512642561) 35 | 36 | > —@therebelrobot, Maker of web things, Facilitator for Node.js/io.js 37 | 38 | 39 | ## ESLint Plugin 40 | 41 |
42 | 43 | [![NPM Version](https://img.shields.io/npm/v/eslint-plugin-you-dont-need-lodash-underscore.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore) 44 | [![Downloads](http://img.shields.io/npm/dm/eslint-plugin-you-dont-need-lodash-underscore.svg?style=flat-square)](https://www.npmjs.org/package/eslint-plugin-you-dont-need-lodash-underscore) 45 | [![Build Status](https://img.shields.io/travis/you-dont-need/You-Dont-Need-Lodash-Underscore/master.svg?style=flat-square)](https://travis-ci.org/you-dont-need/You-Dont-Need-Lodash-Underscore) 46 | [![Coverage Status](https://img.shields.io/coveralls/you-dont-need/You-Dont-Need-Lodash-Underscore/master.svg?style=flat-square)](https://coveralls.io/github/you-dont-need/You-Dont-Need-Lodash-Underscore?branch=master) 47 | 48 |
49 | 50 | If you're using [ESLint](http://eslint.org/), you can install a 51 | [plugin](http://eslint.org/docs/user-guide/configuring#using-the-configuration-from-a-plugin) that 52 | will help you identify places in your codebase where you don't (may not) need Lodash/Underscore. 53 | 54 | Install the plugin ... 55 | 56 | ```sh 57 | npm install --save-dev eslint-plugin-you-dont-need-lodash-underscore 58 | ``` 59 | 60 | ... then update your config 61 | 62 | ```json 63 | "extends" : ["plugin:you-dont-need-lodash-underscore/compatible"], 64 | ``` 65 | 66 | For more information, see [Configuring the ESLint Plugin](configuring.md) 67 | 68 | > [!IMPORTANT] 69 | > Note that, while many Lodash methods are null safe (e.g. _.keys, _.entries), 70 | > this is not necessarily the case for their Native equivalent. If null safety is critical for your application, we 71 | > suggest that you take extra precautions [e.g. consider using the native `Object.keys` as `Object.keys(value || {})`]. 72 | 73 | ## Quick Links 74 | 75 | **[Array](#array)** 76 | 77 | 1. [_.chunk](#_chunk) 78 | 1. [_.compact](#_compact) 79 | 1. [_.concat](#_concat) 80 | 1. [_.difference](#_difference) 81 | 1. [_.drop](#_drop) 82 | 1. [_.dropRight](#_dropRight) 83 | 1. [_.fill](#_fill) 84 | 1. [_.find](#_find) 85 | 1. [_.findIndex](#_findindex) 86 | 1. [_.first](#_first) 87 | 1. [_.flatten](#_flatten) 88 | 1. [_.flattenDeep](#_flattendeep) 89 | 1. [_.fromPairs](#_frompairs) 90 | 1. [_.head and _.tail](#_head-and-_tail) 91 | 1. [_.indexOf](#_indexof) 92 | 1. [_.intersection](#_intersection) 93 | 1. [_.isArray](#_isarray) 94 | 1. [_.isArrayBuffer](#_isarraybuffer) 95 | 1. [_.join](#_join) 96 | 1. [_.last](#_last) 97 | 1. [_.lastIndexOf](#_lastindexof) 98 | 1. [_.reverse](#_reverse) 99 | 1. [_.slice](#_slice) 100 | 1. [_.without](#_without) 101 | 1. [_.initial](#_initial) 102 | 1. [_.pull](#_pull) 103 | 1. [_.unionBy](#_unionBy) 104 | 105 | **[Collection*](#collection)** 106 | 107 | > [!IMPORTANT] 108 | > Note that most native equivalents are array methods, 109 | > and will not work with objects. If this functionality is needed and no object method is provided, 110 | > then Lodash/Underscore might be the better option. Bear in mind however, that all iterable 111 | > objects can easily be converted to an array by use of the 112 | > [spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax). 113 | 114 | 1. [_.each](#_each) 115 | 1. [_.every](#_every) 116 | 1. [_.filter](#_filter) 117 | 1. [_.groupBy](#_groupby) 118 | 1. [_.includes](#_includes) 119 | 1. [_.keyBy](#_keyBy) 120 | 1. [_.map](#_map) 121 | 1. [_.minBy and _.maxBy](#_minby-and-_maxby) 122 | 1. [_.orderBy](#_sortby-and-_orderby) 123 | 1. [_.pluck](#_pluck) 124 | 1. [_.range](#_range) 125 | 1. [_.reduce](#_reduce) 126 | 1. [_.reduceRight](#_reduceright) 127 | 1. [_.reject](#_reject) 128 | 1. [_.size](#_size) 129 | 1. [_.some](#_some) 130 | 1. [_.sortBy](#_sortby-and-_orderby) 131 | 1. [_.uniq](#_uniq) 132 | 1. [_.uniqWith](#_uniqWith) 133 | 134 | **[Function](#function)** 135 | 136 | 1. [_.after](#_after) 137 | 1. [_.bind](#_bind) 138 | 1. [_.debounce](#_debounce) 139 | 1. [_.isFunction](#_isFunction) 140 | 1. [_.partial](#_partial) 141 | 1. [_.throttle](#_throttle) 142 | 143 | **[Lang](#lang)** 144 | 145 | 1. [_.castArray](#_castarray) 146 | 1. [.cloneDeep](#_clonedeep) 147 | 1. [_.gt](#_gt) 148 | 1. [_.gte](#_gte) 149 | 1. [_.isDate](#_isdate) 150 | 1. [_.isEmpty](#_isempty) 151 | 1. [_.isFinite](#_isfinite) 152 | 1. [_.isInteger](#_isInteger) 153 | 1. [_.isNaN](#_isnan) 154 | 1. [_.isNil](#_isnil) 155 | 1. [_.isNull](#_isnull) 156 | 1. [_.isUndefined](#_isundefined) 157 | 158 | **[Object](#object)** 159 | 160 | 1. [_.assign](#_assign) 161 | 1. [_.defaults](#_defaults) 162 | 1. [_.extend](#_extend) 163 | 1. [_.has](#_has) 164 | 1. [_.get](#_get) 165 | 1. [_.invert](#_invert) 166 | 1. [_.isPlainObject](#_isplainobject) 167 | 1. [_.keys](#_keys) 168 | 1. [_.mapKeys](#_mapKeys) 169 | 1. [_.omit](#_omit) 170 | 1. [_.pick](#_pick) 171 | 1. [_.pickBy](#_pickby) 172 | 1. [_.toPairs](#_topairs) 173 | 1. [_.values](#_values) 174 | 175 | **[String](#string)** 176 | 177 | 1. [_.capitalize](#_capitalize) 178 | 1. [_.endsWith](#_endsWith) 179 | 1. [_.isString](#_isString) 180 | 1. [_.lowerFirst](#_lowerFirst) 181 | 1. [_.padStart and _.padEnd](#_padstart-and-_padend) 182 | 1. [_.repeat](#_repeat) 183 | 1. [_.replace](#_replace) 184 | 1. [_.split](#_split) 185 | 1. [_.startsWith](#_startsWith) 186 | 1. [_.template](#_template) 187 | 1. [_.toLower](#_tolower) 188 | 1. [_.toUpper](#_toupper) 189 | 1. [_.trim](#_trim) 190 | 1. [_.upperFirst](#_upperFirst) 191 | 192 | **[Util](#util)** 193 | 194 | 1. [_.times](#_times) 195 | 196 | **[Number](#number)** 197 | 198 | 1. [_.clamp](#_clamp) 199 | 2. [_.inRange](#_inRange) 200 | 3. [_.random](#_random) 201 | 202 | ## Array 203 | 204 | ### _.chunk 205 | 206 | Creates an array of elements split into groups the length of size. 207 | ```js 208 | // Underscore/Lodash 209 | _.chunk(['a', 'b', 'c', 'd'], 2); 210 | // => [['a', 'b'], ['c', 'd']] 211 | 212 | _.chunk(['a', 'b', 'c', 'd'], 3); 213 | // => [['a', 'b', 'c'], ['d']] 214 | 215 | 216 | // Native 217 | 218 | const chunk = (input, size) => { 219 | return input.reduce((arr, item, idx) => { 220 | return idx % size === 0 221 | ? [...arr, [item]] 222 | : [...arr.slice(0, -1), [...arr.slice(-1)[0], item]]; 223 | }, []); 224 | }; 225 | 226 | chunk(['a', 'b', 'c', 'd'], 2); 227 | // => [['a', 'b'], ['c', 'd']] 228 | 229 | chunk(['a', 'b', 'c', 'd'], 3); 230 | // => [['a', 'b', 'c'], ['d']] 231 | ``` 232 | 233 | #### Browser Support for Spread in array literals 234 | 235 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 236 | :-: | :-: | :-: | :-: | :-: | :-: | 237 | 46.0 ✔ | 12.0 ✔ | 16.0 ✔ | ✖ | 37.0 ✔ | 8.0 ✔ | 238 | 239 | **[⬆ back to top](#quick-links)** 240 | 241 | ### _.compact 242 | 243 | Creates an array with all falsy values removed. 244 | 245 | ```js 246 | // Underscore/Lodash 247 | _.compact([0, 1, false, 2, '', 3]); 248 | // output: [1, 2, 3] 249 | 250 | // Native 251 | [0, 1, false, 2, '', 3].filter(Boolean) 252 | // output: [1, 2, 3] 253 | ``` 254 | 255 | #### Browser Support for `array.prototype.filter` 256 | 257 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 258 | :-: | :-: | :-: | :-: | :-: | :-: | 259 | ✔ | ✔ | 1.5 ✔ | 9.0 ✔ | ✔ | ✔ | 260 | 261 | **[⬆ back to top](#quick-links)** 262 | 263 | ### _.concat 264 | 265 | Creates a new array concatenating array with any additional arrays and/or values. 266 | 267 | ```js 268 | // Underscore/Lodash 269 | var array = [1] 270 | var other = _.concat(array, 2, [3], [[4]]) 271 | 272 | console.log(other) 273 | // output: [1, 2, 3, [4]] 274 | 275 | // Native 276 | var array = [1] 277 | var other = array.concat(2, [3], [[4]]) 278 | 279 | console.log(other) 280 | // output: [1, 2, 3, [4]] 281 | ``` 282 | 283 | #### Browser Support for `Array.prototype.concat()` 284 | 285 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 286 | :-: | :-: | :-: | :-: | :-: | :-: | 287 | 1.0 ✔ | ✔ | 1.0 ✔ | 5.5 ✔ | ✔ | ✔ | 288 | 289 | **[⬆ back to top](#quick-links)** 290 | 291 | ### _.difference 292 | 293 | Similar to [without](#_without), but returns the values from array that are not present in the other arrays. 294 | 295 | ```js 296 | // Underscore/Lodash 297 | console.log(_.difference([1, 2, 3, 4, 5], [5, 2, 10])) 298 | // output: [1, 3, 4] 299 | 300 | // Native 301 | var arrays = [[1, 2, 3, 4, 5], [5, 2, 10]]; 302 | console.log(arrays.reduce(function(a, b) { 303 | return a.filter(function(value) { 304 | return !b.includes(value); 305 | }); 306 | })); 307 | // output: [1, 3, 4] 308 | 309 | // ES6 310 | let arrays = [[1, 2, 3, 4, 5], [5, 2, 10]]; 311 | console.log(arrays.reduce((a, b) => a.filter(c => !b.includes(c)))); 312 | // output: [1, 3, 4] 313 | ``` 314 | 315 | #### Browser Support for `Array.prototype.reduce()` 316 | 317 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 318 | :-: | :-: | :-: | :-: | :-: | :-: | 319 | ✔ | ✔ | 3.0 ✔ | 9.0 ✔ | 10.5 ✔ | 4.0 ✔ | 320 | 321 | **[⬆ back to top](#quick-links)** 322 | 323 | ### _.drop 324 | 325 | Creates a slice of array with n elements dropped from the beginning. 326 | 327 | ```js 328 | // Underscore/Lodash 329 | _.drop([1, 2, 3]); 330 | // => [2, 3] 331 | 332 | _.drop([1, 2, 3], 2); 333 | // => [3] 334 | 335 | // Native 336 | [1, 2, 3].slice(1); 337 | // => [2, 3] 338 | 339 | [1, 2, 3].slice(2); 340 | // => [3] 341 | ``` 342 | 343 | #### Browser Support for `Array.prototype.slice()` 344 | 345 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 346 | :-: | :-: | :-: | :-: | :-: | :-: | 347 | 1.0 ✔ | ✔ | 1.0 ✔ | ✔ | ✔ | ✔ | 348 | 349 | **[⬆ back to top](#quick-links)** 350 | 351 | ### _.dropRight 352 | 353 | Creates a slice of array with n elements dropped at the end. 354 | 355 | ```js 356 | // Underscore/Lodash 357 | _.dropRight([1, 2, 3]); 358 | // => [1, 2] 359 | 360 | _.dropRight([1, 2, 3], 2); 361 | // => [1] 362 | 363 | // Native 364 | [1, 2, 3].slice(0, -1); 365 | // => [1, 2] 366 | 367 | [1, 2, 3].slice(0, -2); 368 | // => [1] 369 | ``` 370 | 371 | #### Browser Support for `Array.prototype.slice()` 372 | 373 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 374 | :-: | :-: | :-: | :-: | :-: | :-: | 375 | 1.0 ✔ | ✔ | 1.0 ✔ | ✔ | ✔ | ✔ | 376 | 377 | **[⬆ back to top](#quick-links)** 378 | 379 | ### _.fill 380 | 381 | Fills elements of array with value from start up to, but not including, end. 382 | 383 | > [!NOTE] 384 | > `fill` is a mutable method in both native and Lodash/Underscore. 385 | 386 | ```js 387 | // Underscore/Lodash 388 | var array = [1, 2, 3] 389 | 390 | _.fill(array, 'a') 391 | 392 | console.log(array) 393 | // output: ['a', 'a', 'a'] 394 | 395 | _.fill(Array(3), 2) 396 | // output: [2, 2, 2] 397 | 398 | _.fill([4, 6, 8, 10], '*', 1, 3) 399 | // output: [4, '*', '*', 10] 400 | 401 | // Native 402 | var array = [1, 2, 3] 403 | 404 | array.fill('a') 405 | 406 | console.log(array) 407 | // output: ['a', 'a', 'a'] 408 | 409 | Array(3).fill(2) 410 | // output: [2, 2, 2] 411 | 412 | [4, 6, 8, 10].fill('*', 1, 3) 413 | // output: [4, '*', '*', 10] 414 | ``` 415 | 416 | #### Browser Support for `Array.prototype.fill()` 417 | 418 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 419 | :-: | :-: | :-: | :-: | :-: | :-: | 420 | 45.0 ✔ | ✔ | 31.0 ✔ | ✖ | 32.0 ✔ | 8 ✔ | 421 | 422 | **[⬆ back to top](#quick-links)** 423 | 424 | ### _.find 425 | 426 | Returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined is returned. 427 | 428 | ```js 429 | // Underscore/Lodash 430 | var users = [ 431 | { 'user': 'barney', 'age': 36, 'active': true }, 432 | { 'user': 'fred', 'age': 40, 'active': false }, 433 | { 'user': 'pebbles', 'age': 1, 'active': true } 434 | ] 435 | 436 | _.find(users, function (o) { return o.age < 40; }) 437 | // output: object for 'barney' 438 | 439 | // Native 440 | var users = [ 441 | { 'user': 'barney', 'age': 36, 'active': true }, 442 | { 'user': 'fred', 'age': 40, 'active': false }, 443 | { 'user': 'pebbles', 'age': 1, 'active': true } 444 | ] 445 | 446 | users.find(function (o) { return o.age < 40; }) 447 | // output: object for 'barney' 448 | ``` 449 | 450 | #### Browser Support for `Array.prototype.find()` 451 | 452 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 453 | :-: | :-: | :-: | :-: | :-: | :-: | 454 | 45.0 ✔ | ✔ | 25.0 ✔ | ✖ | 32.0 ✔ | 7.1 ✔ | 455 | 456 | **[⬆ back to top](#quick-links)** 457 | 458 | ### _.findIndex 459 | 460 | Returns the index of the first element in the array that satisfies the provided testing function. Otherwise -1 is returned. 461 | 462 | ```js 463 | // Underscore/Lodash 464 | var users = [ 465 | { 'user': 'barney', 'age': 36, 'active': true }, 466 | { 'user': 'fred', 'age': 40, 'active': false }, 467 | { 'user': 'pebbles', 'age': 1, 'active': true } 468 | ] 469 | 470 | var index = _.findIndex(users, function (o) { return o.age >= 40; }) 471 | console.log(index) 472 | // output: 1 473 | 474 | // Native 475 | var users = [ 476 | { 'user': 'barney', 'age': 36, 'active': true }, 477 | { 'user': 'fred', 'age': 40, 'active': false }, 478 | { 'user': 'pebbles', 'age': 1, 'active': true } 479 | ] 480 | 481 | var index = users.findIndex(function (o) { return o.age >= 40; }) 482 | console.log(index) 483 | // output: 1 484 | ``` 485 | 486 | #### Browser Support for `Array.prototype.findIndex()` 487 | 488 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 489 | :-: | :-: | :-: | :-: | :-: | :-: | 490 | 45.0 ✔ | ✔ | 25.0 ✔ | ✖ | 32.0 ✔ | 7.1 ✔ | 491 | 492 | **[⬆ back to top](#quick-links)** 493 | 494 | ### _.first 495 | 496 | Returns the first element of an array. Passing n will return the first n elements of the array. 497 | 498 | ```js 499 | // Underscore/Lodash 500 | _.first([1, 2, 3, 4, 5]); 501 | // => 1 502 | 503 | // Underscore 504 | _.first([1, 2, 3, 4, 5], 2); 505 | // => [1, 2] 506 | 507 | // Native 508 | [1, 2, 3, 4, 5][0]; 509 | // => 1 510 | //or 511 | [].concat(1, 2, 3, 4, 5).shift() 512 | // => 1 513 | //or 514 | [].concat([1, 2, 3, 4, 5]).shift() 515 | // => 1 516 | 517 | // Native (works even with potentially undefined/null, like _.first) 518 | [].concat(undefined).shift() 519 | // => undefined 520 | 521 | [1, 2, 3, 4, 5].slice(0, 2); 522 | // => [1, 2] 523 | 524 | // Native with ES13 525 | [1, 2, 3, 4, 5].at(0) 526 | // => 1 527 | //or 528 | [].at(0) 529 | // => undefined 530 | ``` 531 | 532 | #### Browser Support for `Array.prototype.slice()` 533 | 534 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 535 | :-: | :-: | :-: | :-: | :-: | :-: | 536 | 1.0 ✔ | ✔ | 1.0 ✔ | ✔ | ✔ | ✔ | 537 | 538 | #### Browser Support for `Array.prototype.at()` 539 | 540 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 541 | :-: | :-: | :-: | :-: | :-: | :-: | 542 | 92 ✔ | 92 ✔ | 90 ✔ | ✖ | 78 ✔ | 15.4 ✔ | 543 | 544 | **[⬆ back to top](#quick-links)** 545 | 546 | ### _.flatten 547 | 548 | Flattens array a single level deep. 549 | 550 | ```js 551 | // Underscore/Lodash 552 | _.flatten([1, [2, [3, [4]], 5]]); 553 | // => [1, 2, [3, [4]], 5] 554 | 555 | // Native 556 | const flatten = [1, [2, [3, [4]], 5]].reduce( (a, b) => a.concat(b), []) 557 | // => [1, 2, [3, [4]], 5] 558 | 559 | const flatten = [].concat(...[1, [2, [3, [4]], 5]]) 560 | // => [1, 2, [3, [4]], 5] 561 | 562 | // Native(ES2019) 563 | const flatten = [1, [2, [3, [4]], 5]].flat() 564 | // => [1, 2, [3, [4]], 5] 565 | 566 | const flatten = [1, [2, [3, [4]], 5]].flatMap(number => number) 567 | // => [1, 2, [3, [4]], 5] 568 | ``` 569 | 570 | #### Browser Support for `Array.prototype.reduce()` 571 | 572 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 573 | :-: | :-: | :-: | :-: | :-: | :-: | 574 | 46.0 ✔ | ✔ | 3.0 ✔ | 9.0 ✔ | 10.5 ✔ | 4 ✔ | 575 | 576 | #### Browser Support for `Array.prototype.flat()` 577 | 578 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] | 579 | :-: | :-: | :-: | :-: | :-: | :-: | 580 | 69 ✔ | 79 ✔ | 62 ✔ | ✖ | 56 ✔ | 12 ✔ | 581 | 582 | #### Browser Support for `Array.prototype.flatMap()` 583 | 584 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] | 585 | :-: | :-: | :-: | :-: | :-: | :-: | 586 | 69 ✔ | 79 ✔ | 62 ✔ | ✖ | 56 ✔ | 12 ✔ | 587 | 588 | **[⬆ back to top](#quick-links)** 589 | 590 | ### _.flattenDeep 591 | 592 | Recursively flattens array. 593 | 594 | ```js 595 | // Underscore/Lodash 596 | _.flattenDeep([1, [2, [3, [4]], 5]]); 597 | // => [1, 2, 3, 4, 5] 598 | 599 | // Native 600 | const flattenDeep = (arr) => Array.isArray(arr) 601 | ? arr.reduce( (a, b) => a.concat(flattenDeep(b)) , []) 602 | : [arr] 603 | 604 | flattenDeep([1, [[2], [3, [4]], 5]]) 605 | // => [1, 2, 3, 4, 5] 606 | 607 | // Native(ES2019) 608 | [1, [2, [3, [4]], 5]].flat(Infinity) 609 | // => [1, 2, 3, 4, 5] 610 | 611 | const flattenDeep = (arr) => arr.flatMap((subArray, index) => Array.isArray(subArray) ? flattenDeep(subArray) : subArray) 612 | 613 | flattenDeep([1, [[2], [3, [4]], 5]]) 614 | // => [1, 2, 3, 4, 5] 615 | ``` 616 | 617 | #### Browser Support 618 | 619 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 620 | :-: | :-: | :-: | :-: | :-: | :-: | 621 | 46.0 ✔ | ✔ | 16.0 ✔ | ✖ | 37.0 ✔ | 7.1 ✔ | 622 | 623 | 624 | #### Browser Support for `Array.prototype.flat()` 625 | 626 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] | 627 | :-: | :-: | :-: | :-: | :-: | :-: | 628 | 69 ✔ | 79 ✔ | 62 ✔ | ✖ | 56 ✔ | 12 ✔ | 629 | 630 | 631 | #### Browser Support for `Array.prototype.flatMap()` 632 | 633 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] | 634 | :-: | :-: | :-: | :-: | :-: | :-: | 635 | 69 ✔ | 79 ✔ | 62 ✔ | ✖ | 56 ✔ | 12 ✔ | 636 | 637 | 638 | **[⬆ back to top](#quick-links)** 639 | 640 | ### _.fromPairs 641 | 642 | Returns an object composed from key-value pairs. 643 | 644 | ```js 645 | // Underscore/Lodash 646 | _.fromPairs([['a', 1], ['b', 2]]); 647 | // => { 'a': 1, 'b': 2 } 648 | 649 | // Native 650 | const fromPairs = function(arr) { 651 | return arr.reduce(function(accumulator, value) { 652 | accumulator[value[0]] = value[1]; 653 | return accumulator; 654 | }, {}) 655 | } 656 | 657 | // Compact form 658 | const fromPairs = (arr) => arr.reduce((acc, val) => (acc[val[0]] = val[1], acc), {}) 659 | 660 | fromPairs([['a', 1], ['b', 2]]); 661 | // => { 'a': 1, 'b': 2 } 662 | 663 | // Native(ES2019) 664 | Object.fromEntries([['a', 1], ['b', 2]]) 665 | // => { 'a': 1, 'b': 2 } 666 | ``` 667 | 668 | #### Browser Support for `Array.prototype.reduce()` 669 | 670 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 671 | :-: | :-: | :-: | :-: | :-: | :-: | 672 | ✔ | ✔ | 3.0 ✔ | 9.0 ✔ | 10.5 ✔ | 4.0 ✔ | 673 | 674 | #### Browser Support for `Object.fromEntries()` 675 | 676 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 677 | :-: | :-: | :-: | :-: | :-: | :-: | 678 | 73.0 ✔ | 79.0 ✔ | 63.0 ✔ | ✖ | 60 ✔ | 12.1 ✔ | 679 | 680 | **[⬆ back to top](#quick-links)** 681 | 682 | ### _.head and _.tail 683 | 684 | Gets the first element or all but the first element. 685 | 686 | ```js 687 | const array = [1, 2, 3] 688 | 689 | // Underscore: _.first, _.head, _.take 690 | // Lodash: _.first, _.head 691 | _.head(array) 692 | // output: 1 693 | 694 | // Underscore: _.rest, _.tail, _.drop 695 | // Lodash: _.tail 696 | _.tail(array) 697 | // output: [2, 3] 698 | 699 | 700 | // Native 701 | const [ head, ...tail ] = array 702 | console.log(head) 703 | // output: 1 704 | console.log(tail) 705 | // output [2, 3] 706 | 707 | // Native replacement for _.head in ES13 708 | array.at(0) 709 | // output: 1 710 | ``` 711 | 712 | #### Browser Support for Spread in array literals 713 | 714 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 715 | :-: | :-: | :-: | :-: | :-: | :-: | 716 | 46.0 ✔ | 12.0 ✔ | 16.0 ✔ | ✖ | 37.0 ✔ | 8.0 ✔ | 717 | 718 | #### Browser Support for `Array.prototype.at()` 719 | 720 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 721 | :-: | :-: | :-: | :-: | :-: | :-: | 722 | 92 ✔ | 92 ✔ | 90 ✔ | ✖ | 78 ✔ | 15.4 ✔ | 723 | 724 | **[⬆ back to top](#quick-links)** 725 | 726 | ### _.indexOf 727 | 728 | Returns the first index at which a given element can be found in the array, or -1 if it is not present. 729 | 730 | ```js 731 | // Underscore/Lodash 732 | var array = [2, 9, 9] 733 | var result = _.indexOf(array, 2) 734 | console.log(result) 735 | // output: 0 736 | 737 | // Native 738 | var array = [2, 9, 9] 739 | var result = array.indexOf(2) 740 | console.log(result) 741 | // output: 0 742 | ``` 743 | 744 | #### Browser Support for `Array.prototype.indexOf()` 745 | 746 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 747 | :-: | :-: | :-: | :-: | :-: | :-: | 748 | ✔ | ✔ | 1.5 ✔ | 9.0 ✔ | ✔ | ✔ | 749 | 750 | **[⬆ back to top](#quick-links)** 751 | 752 | ### _.intersection 753 | 754 | Returns an array that is the intersection of all the arrays. Each value in the result is present in each of the arrays. 755 | 756 | ```js 757 | // Underscore/Lodash 758 | console.log(_.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1])) 759 | // output: [1, 2] 760 | 761 | // Native 762 | var arrays = [[1, 2, 3], [101, 2, 1, 10], [2, 1]]; 763 | console.log(arrays.reduce(function(a, b) { 764 | return a.filter(function(value) { 765 | return b.includes(value); 766 | }); 767 | })); 768 | // output: [1, 2] 769 | 770 | // ES6 771 | let arrays = [[1, 2, 3], [101, 2, 1, 10], [2, 1]]; 772 | console.log(arrays.reduce((a, b) => a.filter(c => b.includes(c)))); 773 | // output: [1, 2] 774 | ``` 775 | 776 | #### Browser Support for `Array.prototype.reduce()` 777 | 778 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 779 | :-: | :-: | :-: | :-: | :-: | :-: | 780 | ✔ | ✔ | 3.0 ✔ | 9.0 ✔ | 10.5 ✔ | 4.0 ✔ | 781 | 782 | **[⬆ back to top](#quick-links)** 783 | 784 | ### _.takeRight 785 | 786 | Creates a slice of array with n elements taken from the end. 787 | 788 | > [!IMPORTANT] 789 | > Native slice does not behave entirely the same as the `Lodash` method. See example below to understand why. 790 | 791 | ```js 792 | // Underscore/Lodash 793 | _.takeRight([1, 2, 3]); 794 | // => [3] 795 | 796 | _.takeRight([1, 2, 3], 2); 797 | // => [2, 3] 798 | 799 | _.takeRight([1, 2, 3], 5); 800 | // => [1, 2, 3] 801 | 802 | // Native 803 | [1, 2, 3].slice(-1); 804 | // => [3] 805 | 806 | [1, 2, 3].slice(-2); 807 | // => [2, 3] 808 | 809 | [1, 2, 3].slice(-5); 810 | // => [1, 2, 3] 811 | 812 | // Difference in compatibility 813 | 814 | // Lodash 815 | _.takeRight([1, 2, 3], 0); 816 | // => [] 817 | 818 | // Native 819 | [1, 2, 3].slice(0); 820 | // => [1, 2, 3] 821 | ``` 822 | 823 | #### Browser Support for `Array.prototype.slice()` 824 | 825 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 826 | :-: | :-: | :-: | :-: | :-: | :-: | 827 | 1.0 ✔ | ✔ | 1.0 ✔ | ✔ | ✔ | ✔ | 828 | 829 | **[⬆ back to top](#quick-links)** 830 | 831 | ### _.isArray 832 | 833 | Returns true if given value is an array. 834 | 835 | ```js 836 | // Lodash 837 | var array = [] 838 | console.log(_.isArray(array)) 839 | // output: true 840 | 841 | // Native 842 | var array = [] 843 | console.log(Array.isArray(array)); 844 | // output: true 845 | ``` 846 | 847 | #### Browser Support for `Array.isArray()` 848 | 849 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 850 | :-: | :-: | :-: | :-: | :-: | :-: | 851 | 5.0 ✔ | ✔ | 4.0 ✔ | 9.0 ✔ | 10.5 ✔ | 5.0 ✔ | 852 | 853 | **[⬆ back to top](#quick-links)** 854 | 855 | ### _.isArrayBuffer 856 | 857 | Checks if value is classified as an ArrayBuffer object. 858 | 859 | ```js 860 | // Lodash 861 | _.isArrayBuffer(new ArrayBuffer(2)); 862 | // output: true 863 | 864 | // Native 865 | console.log(new ArrayBuffer(2) instanceof ArrayBuffer); 866 | // output: true 867 | ``` 868 | 869 | > [!WARNING] 870 | > You will get the wrong result if you get `ArrayBuffer` from another realm. 871 | > [See details](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof#instanceof_and_multiple_context_e.g._frames_or_windows). 872 | 873 | #### Browser Support for `instanceof` 874 | 875 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 876 | :-: | :-: | :-: | :-: | :-: | :-: | 877 | ✔ | ✔ | 1.0 ✔ | ✔ | ✔ | ✔ | 878 | 879 | **[⬆ back to top](#quick-links)** 880 | 881 | ### _.join 882 | 883 | > [!IMPORTANT] 884 | > Not in Underscore.js 885 | 886 | Joins a list of elements in an array with a given separator. 887 | 888 | ```js 889 | // Lodash 890 | var result = _.join(['one', 'two', 'three'], '--') 891 | console.log(result) 892 | // output: 'one--two--three' 893 | 894 | // Native 895 | var result = ['one', 'two', 'three'].join('--') 896 | console.log(result) 897 | // output: 'one--two--three' 898 | ``` 899 | 900 | #### Browser Support for `Array.prototype.join()` 901 | 902 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 903 | :-: | :-: | :-: | :-: | :-: | :-: | 904 | 1.0 ✔ | ✔ | 1.0 ✔ | 5.5 ✔ | ✔ | ✔ | 905 | 906 | **[⬆ back to top](#quick-links)** 907 | 908 | ### _.last 909 | 910 | Returns the last element of an array. Passing n will return the last n elements of the array. 911 | 912 | ```js 913 | // Underscore/Lodash 914 | const numbers = [1, 2, 3, 4, 5]; 915 | _.last(numbers); 916 | // => 5 917 | 918 | // Underscore 919 | _.last(numbers, 2); 920 | // => [4, 5] 921 | 922 | // Native 923 | const numbers = [1, 2, 3, 4, 5]; 924 | numbers[numbers.length - 1]; 925 | // => 5 926 | //or 927 | numbers.slice(-1)[0]; 928 | // => 5 929 | //or 930 | [].concat(numbers).pop() 931 | // => 5 932 | //or 933 | numbers.at(-1); 934 | // => 5 935 | 936 | // Native (works even with potentially undefined/null) 937 | [].concat(undefined).pop() 938 | // => undefined 939 | 940 | numbers.slice(-2); 941 | // => [4, 5] 942 | ``` 943 | 944 | #### Browser Support for `Array.prototype.concat()` 945 | 946 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 947 | :-: | :-: | :-: | :-: | :-: | :-: | 948 | 1.0 ✔ | ✔ | 1.0 ✔ | 5.5 ✔ | ✔ | ✔ | 949 | 950 | #### Browser Support for `Array.prototype.at()` 951 | 952 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 953 | :-: | :-: | :-: | :-: | :-: | :-: | 954 | 92 ✔ | 92 ✔ | 90 ✔ | ✖ | 78 ✔ | 15.4 ✔ | 955 | 956 | **[⬆ back to top](#quick-links)** 957 | 958 | ### _.lastIndexOf 959 | 960 | Returns the index of the last occurrence of value in the array, or -1 if value is not present. 961 | 962 | ```js 963 | // Underscore/Lodash 964 | var array = [2, 9, 9, 4, 3, 6] 965 | var result = _.lastIndexOf(array, 9) 966 | console.log(result) 967 | // output: 2 968 | 969 | // Native 970 | var array = [2, 9, 9, 4, 3, 6] 971 | var result = array.lastIndexOf(9) 972 | console.log(result) 973 | // output: 2 974 | ``` 975 | 976 | #### Browser Support for `Array.prototype.lastIndexOf()` 977 | 978 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 979 | :-: | :-: | :-: | :-: | :-: | :-: | 980 | ✔ | ✔ | 1.5 ✔ | 9.0 ✔ | ✔ | ✔ | 981 | 982 | **[⬆ back to top](#quick-links)** 983 | 984 | ### _.reverse 985 | 986 | > [!IMPORTANT] 987 | > Not in Underscore.js 988 | 989 | Reverses array so that the first element becomes the last, the second element becomes the second to last, and so on. 990 | 991 | ```js 992 | // Lodash 993 | var array = [1, 2, 3] 994 | console.log(_.reverse(array)) 995 | // output: [3, 2, 1] 996 | 997 | // Native 998 | var array = [1, 2, 3] 999 | console.log(array.reverse()) 1000 | // output: [3, 2, 1] 1001 | ``` 1002 | 1003 | Voice from the Lodash author: 1004 | 1005 | > Lodash's `_.reverse` just calls `Array#reverse` and enables composition like `_.map(arrays, _.reverse).` 1006 | > It's exposed on _ because previously, like `Underscore`, it was only exposed in the chaining syntax. 1007 | > --- [jdalton](https://github.com/cht8687/You-Dont-Need-Lodash-Underscore/commit/22c4bcf2be48dd415d2b073759805562e520b615#) 1008 | 1009 | #### Browser Support for `Array.prototype.reverse()` 1010 | 1011 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1012 | :-: | :-: | :-: | :-: | :-: | :-: | 1013 | ✔ | ✔ | 1.5 ✔ | 9 ✔ | ✔ | ✔ | 1014 | 1015 | **[⬆ back to top](#quick-links)** 1016 | 1017 | ### _.slice 1018 | 1019 | Returns a shallow copy of a portion of an array into a new array object selected from `begin` to `end` (`end` not included) 1020 | 1021 | ```js 1022 | // Lodash 1023 | var array = [1, 2, 3, 4] 1024 | console.log(_.slice(array, 1, 3)) 1025 | // output: [2, 3] 1026 | 1027 | // Native 1028 | var array = [1, 2, 3, 4] 1029 | console.log(array.slice(1, 3)); 1030 | // output: [2, 3] 1031 | ``` 1032 | 1033 | #### Browser Support for `Array.prototype.slice()` 1034 | 1035 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1036 | :-: | :-: | :-: | :-: | :-: | :-: | 1037 | 1.0 ✔ | ✔ | 1.0 ✔ | ✔ | ✔ | ✔ | 1038 | 1039 | **[⬆ back to top](#quick-links)** 1040 | 1041 | ### _.without 1042 | 1043 | > [!IMPORTANT] 1044 | > Not in Underscore.js 1045 | 1046 | Returns an array where matching items are filtered. 1047 | 1048 | ```js 1049 | // Lodash 1050 | var array = [1, 2, 3] 1051 | console.log(_.without(array, 2)) 1052 | // output: [1, 3] 1053 | 1054 | // Native 1055 | var array = [1, 2, 3] 1056 | console.log(array.filter(function(value) { 1057 | return value !== 2; 1058 | })); 1059 | // output: [1, 3] 1060 | ``` 1061 | 1062 | #### Browser Support for `Array.prototype.filter()` 1063 | 1064 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1065 | :-: | :-: | :-: | :-: | :-: | :-: | 1066 | 1.0 ✔ | ✔ | 1.5 ✔ | 9 ✔ | ✔ | ✔ | 1067 | 1068 | **[⬆ back to top](#quick-links)** 1069 | 1070 | ### _.initial 1071 | 1072 | Returns everything but the last entry of the array. Pass n to exclude the last n elements from the result. 1073 | 1074 | ```js 1075 | // Underscore 1076 | var array = [5, 4, 3, 2, 1] 1077 | console.log(_.initial(array, 2)) 1078 | // output: [5, 4, 3] 1079 | 1080 | // Native 1081 | var array = [5, 4, 3, 2, 1] 1082 | console.log(array.slice(0, -2)); 1083 | // output: [5, 4, 3] 1084 | ``` 1085 | 1086 | #### Browser Support for `Array.prototype.slice()` 1087 | 1088 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1089 | :-: | :-: | :-: | :-: | :-: | :-: | 1090 | 1.0 ✔ | ✔ | 1.0 ✔ | ✔ | ✔ | ✔ | 1091 | 1092 | **[⬆ back to top](#quick-links)** 1093 | 1094 | ### _.pull 1095 | 1096 | Removes all provided values from the given array using strict equality for comparisons, i.e. ===. 1097 | 1098 | ```js 1099 | // Lodash 1100 | const array = [1, 2, 3, 1, 2, 3]; 1101 | _.pull(array, 2, 3); 1102 | console.log(array); // output: [1, 1] 1103 | ``` 1104 | 1105 | ```js 1106 | // Native JS 1107 | const array = [1, 2, 3, 1, 2, 3]; 1108 | function pull(arr, ...removeList){ 1109 | var removeSet = new Set(removeList) 1110 | return arr.filter(function(el){ 1111 | return !removeSet.has(el) 1112 | }) 1113 | } 1114 | console.log(pull(array, 2, 3)); // output: [1, 1] 1115 | console.log(array); // still [1, 2, 3, 1, 2, 3]. This is not in place, unlike lodash! 1116 | ``` 1117 | 1118 | ```ts 1119 | // TypeScript 1120 | const array = [1, 2, 3, 1, 2, 3]; 1121 | const pull = (sourceArray: T[], ...removeList: T[]): T[] => { 1122 | const removeSet = new Set(removeList); 1123 | return sourceArray.filter(el => !removeSet.has(el)); 1124 | }; 1125 | console.log(pull(array, 2, 3)); // output: [1, 1] 1126 | console.log(array); // still [1, 2, 3, 1, 2, 3]. This is not in place, unlike lodash! 1127 | ``` 1128 | 1129 | #### Browser Support for `Array.prototype.filter()` 1130 | 1131 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1132 | :-: | :-: | :-: | :-: | :-: | :-: | 1133 | 1.0 ✔ | ✔ | 1.5 ✔ | 9 ✔ | ✔ | ✔ | 1134 | 1135 | #### Browser Support for `Set.prototype.has()` 1136 | 1137 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] | 1138 | :-: | :-: | :-: | :-: | :-: | :-: | 1139 | 38 ✔ | 12 ✔ | 13 ✔ | 11 ✔ | 25 ✔ | 8 ✔ | 1140 | 1141 | **[⬆ back to top](#quick-links)** 1142 | 1143 | ### _.unionBy 1144 | 1145 | Creates an array of unique values, taking an `iteratee` to compute uniqueness with 1146 | (note that to iterate by a key in an object you must use `x => x.key` instead of `key` for the `iteratee`) 1147 | 1148 | ```js 1149 | // Lodash 1150 | var array1 = [2.1]; 1151 | var array2 = [1.2, 2.3]; 1152 | var result = _.unionBy(array1, array2, Math.floor) 1153 | console.log(result) 1154 | // output: [2.1, 1.2] 1155 | 1156 | // Native 1157 | var array1 = [2.1]; 1158 | var array2 = [1.2, 2.3]; 1159 | function unionBy(...arrays) { 1160 | const iteratee = (arrays).pop(); 1161 | 1162 | if (Array.isArray(iteratee)) { 1163 | return []; // return empty if iteratee is missing 1164 | } 1165 | 1166 | return [...arrays].flat().filter( 1167 | (set => (o) => set.has(iteratee(o)) ? false : set.add(iteratee(o)))(new Set()), 1168 | ); 1169 | }; 1170 | console.log(unionBy(array1, array2, Math.floor)) 1171 | // output: [2.1, 1.2] 1172 | ``` 1173 | 1174 | #### Browser Support for `Array.prototype.flat()` 1175 | 1176 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] | 1177 | :-: | :-: | :-: | :-: | :-: | :-: | 1178 | 69 ✔ | 79 ✔ | 62 ✔ | ✖ | 56 ✔ | 12 ✔ | 1179 | 1180 | #### Browser Support for `Array.isArray()` 1181 | 1182 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1183 | :-: | :-: | :-: | :-: | :-: | :-: | 1184 | 5.0 ✔ | ✔ | 4.0 ✔ | 9.0 ✔ | 10.5 ✔ | 5.0 ✔ | 1185 | 1186 | #### Browser Support for `Set.prototype.has()` 1187 | 1188 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] | 1189 | :-: | :-: | :-: | :-: | :-: | :-: | 1190 | 38 ✔ | 12 ✔ | 13 ✔ | 11 ✔ | 25 ✔ | 8 ✔ | 1191 | 1192 | **[⬆ back to top](#quick-links)** 1193 | 1194 | ## Collection* 1195 | 1196 | > [!IMPORTANT] 1197 | > Most native equivalents are array methods, 1198 | > and will not work with objects. If this functionality is needed and no object method is provided, 1199 | > then Lodash/Underscore is the better option. 1200 | 1201 | ### _.each 1202 | 1203 | Iterates over a list of elements, yielding each in turn to an iteratee function. 1204 | 1205 | ```js 1206 | // Underscore/Lodash 1207 | //For arrays 1208 | _.each([1, 2, 3], function (value, index) { 1209 | console.log(value) 1210 | }) 1211 | // output: 1 2 3 1212 | 1213 | //For objects 1214 | _.each({'one':1, 'two':2, 'three':3}, function(value) { 1215 | console.log(value) 1216 | }) 1217 | // output: 1 2 3 1218 | 1219 | // Native 1220 | //For arrays 1221 | [1, 2, 3].forEach(function (value, index) { 1222 | console.log(value) 1223 | }) 1224 | // output: 1 2 3 1225 | 1226 | //For objects 1227 | Object.entries({'one':1, 'two':2, 'three':3}).forEach(function([key,value],index) { 1228 | console.log(value) 1229 | }) 1230 | //output: 1 2 3 1231 | ``` 1232 | 1233 | #### Browser Support for `Array.prototype.forEach()` 1234 | 1235 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1236 | :-: | :-: | :-: | :-: | :-: | :-: | 1237 | ✔ | ✔ | 1.5 ✔ | 9.0 ✔ | ✔ | ✔ | 1238 | 1239 | #### Browser Support for `Object.entries().forEach()` 1240 | 1241 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1242 | :-: | :-: | :-: | :-: | :-: | :-: | 1243 | 54 ✔ | 14 ✔ | 47 ✔ | ✖ | 41 ✔ | 10.1✔ | 1244 | 1245 | **[⬆ back to top](#quick-links)** 1246 | 1247 | ### _.every 1248 | 1249 | Tests whether all elements in the array pass the test implemented by the provided function. 1250 | 1251 | ```js 1252 | // Underscore/Lodash 1253 | function isLargerThanTen (element, index, array) { 1254 | return element >= 10 1255 | } 1256 | var array = [10, 20, 30] 1257 | var result = _.every(array, isLargerThanTen) 1258 | console.log(result) 1259 | // output: true 1260 | 1261 | // Native 1262 | function isLargerThanTen (element, index, array) { 1263 | return element >= 10 1264 | } 1265 | 1266 | var array = [10, 20, 30] 1267 | var result = array.every(isLargerThanTen) 1268 | console.log(result) 1269 | // output: true 1270 | ``` 1271 | 1272 | #### Browser Support for `Array.prototype.every()` 1273 | 1274 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1275 | :-: | :-: | :-: | :-: | :-: | :-: | 1276 | ✔ | ✔ | 1.5 ✔ | 9.0 ✔ | ✔ | ✔ | 1277 | 1278 | **[⬆ back to top](#quick-links)** 1279 | 1280 | ### _.filter 1281 | 1282 | Creates a new array with all elements that pass the test implemented by the provided function. 1283 | 1284 | ```js 1285 | // Underscore/Lodash 1286 | function isBigEnough (value) { 1287 | return value >= 10 1288 | } 1289 | var array = [12, 5, 8, 130, 44] 1290 | var filtered = _.filter(array, isBigEnough) 1291 | console.log(filtered) 1292 | // output: [12, 130, 44] 1293 | 1294 | // Native 1295 | function isBigEnough (value) { 1296 | return value >= 10 1297 | } 1298 | var array = [12, 5, 8, 130, 44] 1299 | var filtered = array.filter(isBigEnough) 1300 | console.log(filtered) 1301 | // output: [12, 130, 44] 1302 | ``` 1303 | 1304 | #### Browser Support for `Array.prototype.filter()` 1305 | 1306 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1307 | :-: | :-: | :-: | :-: | :-: | :-: | 1308 | ✔ | ✔ | 1.5 ✔ | 9 ✔ | ✔ | ✔ | 1309 | 1310 | **[⬆ back to top](#quick-links)** 1311 | 1312 | ### _.groupBy 1313 | 1314 | Group items by key. 1315 | 1316 | ```js 1317 | // Underscore/Lodash 1318 | var grouped = _.groupBy(['one', 'two', 'three'], 'length') 1319 | console.log(grouped) 1320 | // output: {3: ["one", "two"], 5: ["three"]} 1321 | 1322 | // Native 1323 | var grouped = ['one', 'two', 'three'].reduce((r, v, i, a, k = v.length) => ((r[k] || (r[k] = [])).push(v), r), {}) 1324 | console.log(grouped) 1325 | // output: {3: ["one", "two"], 5: ["three"]} 1326 | 1327 | // Native 1328 | Object.groupBy(['one', 'two', 'three'], ({length}) => length) 1329 | // output: {3: ["one", "two"], 5: ["three"]} 1330 | ``` 1331 | 1332 | ```js 1333 | // Underscore/Lodash 1334 | var grouped = _.groupBy([1.3, 2.1, 2.4], num => Math.floor(num)) 1335 | console.log(grouped) 1336 | // output: {1: [1.3], 2: [2.1, 2.4]} 1337 | 1338 | // Native 1339 | var grouped = [1.3, 2.1, 2.4].reduce((r, v, i, a, k = Math.floor(v)) => ((r[k] || (r[k] = [])).push(v), r), {}) 1340 | console.log(grouped) 1341 | // output: {1: [1.3], 2: [2.1, 2.4]} 1342 | 1343 | // Native 1344 | Object.groupBy([1.3, 2.1, 2.4], num => Math.floor(num)) 1345 | // output: {1: [1.3], 2: [2.1, 2.4]} 1346 | ``` 1347 | 1348 | #### Browser Support for `Array.prototype.reduce()` 1349 | 1350 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1351 | :-: | :-: | :-: | :-: | :-: | :-: | 1352 | ✔ | ✔ | 3.0 ✔ | 9.0 ✔ | 10.5 ✔ | 4.0 ✔ | 1353 | 1354 | #### Browser Support for `Object.groupBy()` 1355 | 1356 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1357 | :-: | :-: | :-: | :-: | :-: | :-: | 1358 | 117.0 ✔ | 117.0 ✔ | 119.0 ✔ | ✖ | 103.0 ✔ | 16.4 ✔ | 1359 | 1360 | **[⬆ back to top](#quick-links)** 1361 | 1362 | ### _.includes 1363 | 1364 | Checks if a value is in collection. 1365 | 1366 | ```js 1367 | var array = [1, 2, 3] 1368 | // Underscore/Lodash - also called _.contains 1369 | _.includes(array, 1) 1370 | // output: true 1371 | 1372 | // Native 1373 | var array = [1, 2, 3] 1374 | array.includes(1) 1375 | // output: true 1376 | 1377 | // Native (does not use same value zero) 1378 | var array = [1, 2, 3] 1379 | array.indexOf(1) > -1 1380 | // output: true 1381 | ``` 1382 | 1383 | #### Browser Support for `Array.prototype.includes` 1384 | 1385 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1386 | :-: | :-: | :-: | :-: | :-: | :-: | 1387 | 47.0 ✔ | 14.0 ✔ | 43.0 ✔ | ✖ | 34.0 ✔ | 9.0 ✔ | 1388 | 1389 | #### Browser Support for `Array.prototype.indexOf` 1390 | 1391 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1392 | :-: | :-: | :-: | :-: | :-: | :-: | 1393 | ✔ | ✔ | 1.5 ✔ | 9.0 ✔ | ✔ | ✔ | 1394 | 1395 | **[⬆ back to top](#quick-links)** 1396 | 1397 | ### _.keyBy 1398 | 1399 | > [!WARNING] 1400 | > Not in Underscore.js 1401 | 1402 | Creates an object composed of keys generated from the results of running each element of collection through iteratee. 1403 | 1404 | ```js 1405 | // Lodash 1406 | console.log(_.keyBy(['a', 'b', 'c'])) 1407 | // output: { a: 'a', b: 'b', c: 'c' } 1408 | console.log(_.keyBy([{ id: 'a1', title: 'abc' }, { id: 'b2', title: 'def' }], 'id')) 1409 | // output: { a1: { id: 'a1', title: 'abc' }, b2: { id: 'b2', title: 'def' } } 1410 | console.log(_.keyBy({ data: { id: 'a1', title: 'abc' }}, 'id')) 1411 | // output: { a1: { id: 'a1', title: 'abc' }} 1412 | 1413 | // keyBy for array only 1414 | const keyBy = (array, key) => (array || []).reduce((r, x) => ({ ...r, [key ? x[key] : x]: x }), {}); 1415 | 1416 | // Native 1417 | console.log(keyBy(['a', 'b', 'c'])) 1418 | // output: { a: 'a', b: 'b', c: 'c' } 1419 | console.log(keyBy([{ id: 'a1', title: 'abc' }, { id: 'b2', title: 'def' }], 'id')) 1420 | // output: { a1: { id: 'a1', title: 'abc' }, b2: { id: 'b2', title: 'def' } } 1421 | console.log(keyBy(Object.values({ data: { id: 'a1', title: 'abc' }}), 'id')) 1422 | // output: { a1: { id: 'a1', title: 'abc' }} 1423 | 1424 | // keyBy for array and object 1425 | const collectionKeyBy = (collection, key) => { 1426 | const c = collection || {}; 1427 | return c.isArray() ? keyBy(c, key) : keyBy(Object.values(c), key); 1428 | } 1429 | ``` 1430 | 1431 | #### Browser Support for `Array.prototype.reduce()` 1432 | 1433 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1434 | :-: | :-: | :-: | :-: | :-: | :-: | 1435 | ✔ | 12.0 ✔ | 3.0 ✔ | 9.0 ✔ | 10.5 ✔ | 4.0 ✔ | 1436 | 1437 | **[⬆ back to top](#quick-links)** 1438 | 1439 | ### _.map 1440 | 1441 | Translates all items in an array or object to new array of items. 1442 | 1443 | ```js 1444 | // Underscore/Lodash 1445 | var array1 = [1, 2, 3] 1446 | var array2 = _.map(array1, function (value, index) { 1447 | return value * 2 1448 | }) 1449 | console.log(array2) 1450 | // output: [2, 4, 6] 1451 | 1452 | // Native 1453 | var array1 = [1, 2, 3] 1454 | var array2 = array1.map(function (value, index) { 1455 | return value * 2 1456 | }) 1457 | console.log(array2) 1458 | // output: [2, 4, 6] 1459 | ``` 1460 | 1461 | ```js 1462 | // Underscore/Lodash 1463 | var object1 = { 'a': 1, 'b': 2, 'c': 3 } 1464 | var object2 = _.map(object1, function (value, index) { 1465 | return value * 2 1466 | }) 1467 | console.log(object2) 1468 | // output: [2, 4, 6] 1469 | 1470 | // Native 1471 | var object1 = { 'a': 1, 'b': 2, 'c': 3 } 1472 | var object2 = Object.entries(object1).map(function ([key, value], index) { 1473 | return value * 2 1474 | }) 1475 | console.log(object2) 1476 | // output: [2, 4, 6] 1477 | ``` 1478 | 1479 | #### Browser Support for `Object.entries()` and destructuring 1480 | 1481 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1482 | :-: | :-: | :-: | :-: | :-: | :-: | 1483 | ✔ | ✔ | 1.5 ✔ | ✖ | ✔ | ✔ | 1484 | 1485 | #### Browser Support for `Array.prototype.map()` 1486 | 1487 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1488 | :-: | :-: | :-: | :-: | :-: | :-: | 1489 | ✔ | ✔ | 1.5 ✔ | 9.0 ✔ | ✔ | ✔ | 1490 | 1491 | **[⬆ back to top](#quick-links)** 1492 | 1493 | ### _.minBy and _.maxBy 1494 | 1495 | Use `Array.prototype.reduce()` for find the maximum or minimum collection item 1496 | 1497 | ```js 1498 | // Underscore/Lodash 1499 | var data = [{ value: 6 }, { value: 2 }, { value: 4 }] 1500 | var minItem = _.minBy(data, 'value') 1501 | var maxItem = _.maxBy(data, 'value') 1502 | console.log(minItem, maxItem) 1503 | // output: { value: 2 } { value: 6 } 1504 | 1505 | // Native 1506 | var data = [{ value: 6 }, { value: 2 }, { value: 4 }] 1507 | var minItem = data.reduce(function(a, b) { return a.value <= b.value ? a : b }, {}) 1508 | var maxItem = data.reduce(function(a, b) { return a.value >= b.value ? a : b }, {}) 1509 | console.log(minItem, maxItem) 1510 | // output: { value: 2 }, { value: 6 } 1511 | ``` 1512 | 1513 | Extract a functor and use es2015 for better code 1514 | 1515 | ```js 1516 | // utils 1517 | const makeSelect = (comparator) => (a, b) => comparator(a, b) ? a : b 1518 | const minByValue = makeSelect((a, b) => a.value <= b.value) 1519 | const maxByValue = makeSelect((a, b) => a.value >= b.value) 1520 | 1521 | // main logic 1522 | const data = [{ value: 6 }, { value: 2 }, { value: 4 }] 1523 | const minItem = data.reduce(minByValue, {}) 1524 | const maxItem = data.reduce(maxByValue, {}) 1525 | 1526 | console.log(minItem, maxItem) 1527 | // output: { value: 2 }, { value: 6 } 1528 | 1529 | // or also more universal and little slower variant of minBy 1530 | const minBy = (collection, key) => { 1531 | // slower because need to create a lambda function for each call... 1532 | const select = (a, b) => a[key] <= b[key] ? a : b 1533 | return collection.reduce(select, {}) 1534 | } 1535 | 1536 | console.log(minBy(data, 'value')) 1537 | // output: { value: 2 } 1538 | ``` 1539 | 1540 | #### Browser Support for `Array.prototype.reduce()` 1541 | 1542 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1543 | :-: | :-: | :-: | :-: | :-: | :-: | 1544 | ✔ | ✔ | 3.0 ✔ | 9.0 ✔ | 10.5 ✔ | 4.0 ✔ | 1545 | 1546 | **[⬆ back to top](#quick-links)** 1547 | 1548 | ### _.pluck 1549 | 1550 | `array.map` or `_.map` can also be used to replace `_.pluck`. 1551 | Lodash v4.0 removed `_.pluck` in favor of `_.map` with iteratee shorthand. 1552 | Details can be found in [Changelog](https://github.com/lodash/lodash/wiki/Changelog) 1553 | 1554 | ```js 1555 | // Underscore/Lodash 1556 | var array1 = [{name: "Alice"}, {name: "Bob"}, {name: "Jeremy"}] 1557 | var names = _.pluck(array1, "name") 1558 | console.log(names) 1559 | // output: ["Alice", "Bob", "Jeremy"] 1560 | 1561 | // Native 1562 | var array1 = [{name: "Alice"}, {name: "Bob"}, {name: "Jeremy"}] 1563 | var names = array1.map(function(x){ 1564 | return x.name 1565 | }) 1566 | console.log(names) 1567 | // output: ["Alice", "Bob", "Jeremy"] 1568 | ``` 1569 | 1570 | #### Browser Support for `Array.prototype.map()` 1571 | 1572 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1573 | :-: | :-: | :-: | :-: | :-: | :-: | 1574 | ✔ | ✔ | 1.5 ✔ | 9.0 ✔ | ✔ | ✔ | 1575 | 1576 | **[⬆ back to top](#quick-links)** 1577 | 1578 | ### _.reduce 1579 | 1580 | Applies a function against an accumulator and each value of the array (from left-to-right) to reduce it to a single value. 1581 | 1582 | ```js 1583 | // Underscore/Lodash 1584 | var array = [0, 1, 2, 3, 4] 1585 | var result = _.reduce(array, function (previousValue, currentValue, currentIndex, array) { 1586 | return previousValue + currentValue 1587 | }) 1588 | console.log(result) 1589 | // output: 10 1590 | 1591 | // Native 1592 | var array = [0, 1, 2, 3, 4] 1593 | var result = array.reduce(function (previousValue, currentValue, currentIndex, array) { 1594 | return previousValue + currentValue 1595 | }) 1596 | console.log(result) 1597 | // output: 10 1598 | ``` 1599 | 1600 | #### Browser Support for `Array.prototype.reduce()` 1601 | 1602 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1603 | :-: | :-: | :-: | :-: | :-: | :-: | 1604 | ✔ | ✔ | 3.0 ✔ | 9.0 ✔ | 10.5 ✔ | 4.0 ✔ | 1605 | 1606 | **[⬆ back to top](#quick-links)** 1607 | 1608 | ### _.range 1609 | 1610 | Creates an array of numbers progressing from start up to. 1611 | 1612 | ```js 1613 | // Underscore/Lodash 1614 | _.range(4) // output: [0, 1, 2, 3] 1615 | _.range(-4) // output: [0, -1, -2, -3] 1616 | _.range(1, 5) // output: [1, 2, 3, 4] 1617 | _.range(0, 20, 5) // output: [0, 5, 10, 15] 1618 | 1619 | // Native ( solution with Array.from ) 1620 | Array.from({length: 4}, (_, i) => i) // output: [0, 1, 2, 3] 1621 | Array.from({length: 4}, (_, i) => -i) // output: [-0, -1, -2, -3] 1622 | Array.from({length: 4}, (_, i) => i + 1) // output: [1, 2, 3, 4] 1623 | Array.from({length: 4}, (_, i) => i * 5) // output: [0, 5, 10, 15] 1624 | 1625 | // Native ( solution with keys() and spread ) 1626 | [...Array(4).keys()] // output: [0, 1, 2, 3] 1627 | [...Array(4).keys()].map(k => -k) // output: [-0, -1, -2, -3] 1628 | [...Array(4).keys()].map(k => k + 1) // output: [1, 2, 3, 4] 1629 | [...Array(4).keys()].map(k => k * 5) // output: [0, 5, 10, 15] 1630 | ``` 1631 | 1632 | 1633 | #### Browser Support for `Array.from()` 1634 | 1635 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1636 | :-: | :-: | :-: | :-: | :-: | :-: | 1637 | 45.0 ✔ | ✔ | 32.0 ✔ | ✖ | ✔ | 9.0 ✔ | 1638 | 1639 | #### Browser Support for keys and spread in Array literals 1640 | 1641 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1642 | :-: | :-: | :-: | :-: | :-: | :-: | 1643 | 46.0 ✔ | 12.0 ✔ | 16.0 ✔ | ✖ | 37.0 ✔ | 7.1 ✔ | 1644 | 1645 | **[⬆ back to top](#quick-links)** 1646 | 1647 | ### _.reduceRight 1648 | 1649 | This method is like _.reduce except that it iterates over elements of collection from right to left. 1650 | 1651 | ```js 1652 | // Underscore/Lodash 1653 | var array = [0, 1, 2, 3, 4] 1654 | var result = _.reduceRight(array, function (previousValue, currentValue, currentIndex, array) { 1655 | return previousValue - currentValue 1656 | }) 1657 | console.log(result) 1658 | // output: -2 1659 | 1660 | // Native 1661 | var array = [0, 1, 2, 3, 4] 1662 | var result = array.reduceRight(function (previousValue, currentValue, currentIndex, array) { 1663 | return previousValue - currentValue 1664 | }) 1665 | console.log(result) 1666 | // output: -2 1667 | ``` 1668 | 1669 | #### Browser Support for `Array.prototype.reduceRight()` 1670 | 1671 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1672 | :-: | :-: | :-: | :-: | :-: | :-: | 1673 | ✔ | ✔ | 3.0 ✔ | 9.0 ✔ | 10.5 ✔ | 4.0 ✔ | 1674 | 1675 | **[⬆ back to top](#quick-links)** 1676 | 1677 | ### _.reject 1678 | 1679 | The opposite of _.filter; this method returns the elements of collection that predicate does not return truthy for. 1680 | 1681 | ```js 1682 | // Underscore/Lodash 1683 | var array = [1, 2, 3, 4, 5]; 1684 | var result = _.reject(array, function (x) { 1685 | return x % 2 === 0; 1686 | }); 1687 | // output: [1, 3, 5] 1688 | 1689 | // Native 1690 | var array = [1, 2, 3, 4, 5]; 1691 | 1692 | var reject = function (arr, predicate) { 1693 | var complement = function (f) { 1694 | return function (x) { 1695 | return !f(x); 1696 | } 1697 | }; 1698 | 1699 | return arr.filter(complement(predicate)); 1700 | }; 1701 | // output: [1, 3, 5] 1702 | ``` 1703 | 1704 | #### Browser Support for `Array.prototype.filter()` 1705 | 1706 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1707 | :-: | :-: | :-: | :-: | :-: | :-: | 1708 | ✔ | 12 ✔ | 1.5 ✔ | 9.0 ✔ | 9.5 ✔ | 3.0 ✔ | 1709 | 1710 | **[⬆ back to top](#quick-links)** 1711 | 1712 | ### _.sample 1713 | 1714 | Gets a random element from `array`. 1715 | 1716 | ```js 1717 | // Underscore/Lodash 1718 | const array = [0, 1, 2, 3, 4] 1719 | const result = _.sample(array) 1720 | console.log(result) 1721 | // output: 2 1722 | 1723 | // Native 1724 | const array = [0, 1, 2, 3, 4] 1725 | const sample = arr => { 1726 | const len = arr == null ? 0 : arr.length 1727 | return len ? arr[Math.floor(Math.random() * len)] : undefined 1728 | } 1729 | 1730 | const result = sample(array) 1731 | console.log(result) 1732 | // output: 2 1733 | ``` 1734 | 1735 | #### Browser Support for `Array.prototype.length()` and `Math.random()` 1736 | 1737 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1738 | :-: | :-: | :-: | :-: | :-: | :-: | 1739 | ✔ | ✔ | 1.0 ✔ | ✔ | ✔ | ✔ | 1740 | 1741 | **[⬆ back to top](#quick-links)** 1742 | 1743 | ### _.size 1744 | 1745 | Returns the number of values in the collection. 1746 | 1747 | ```js 1748 | // Underscore/Lodash 1749 | var result = _.size({one: 1, two: 2, three: 3}) 1750 | console.log(result) 1751 | // output: 3 1752 | 1753 | // Native 1754 | var result2 = Object.keys({one: 1, two: 2, three: 3}).length 1755 | console.log(result2) 1756 | // output: 3 1757 | ``` 1758 | 1759 | #### Browser Support for `Object.keys()` 1760 | 1761 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1762 | :-: | :-: | :-: | :-: | :-: | :-: | 1763 | 5.0 ✔ | ✔ | 4.0 ✔ | 9.0 ✔ | 12.0 ✔ | 5.0 ✔ | 1764 | 1765 | **[⬆ back to top](#quick-links)** 1766 | 1767 | ### _.some 1768 | 1769 | Tests whether any of the elements in the array pass the test implemented by the provided function. 1770 | 1771 | ```js 1772 | // Underscore/Lodash 1773 | function isLargerThanTen (element, index, array) { 1774 | return element >= 10 1775 | } 1776 | var array = [10, 9, 8] 1777 | var result = _.some(array, isLargerThanTen) 1778 | console.log(result) 1779 | // output: true 1780 | 1781 | // Native 1782 | function isLargerThanTen (element, index, array) { 1783 | return element >= 10 1784 | } 1785 | 1786 | var array = [10, 9, 8] 1787 | var result = array.some(isLargerThanTen) 1788 | console.log(result) 1789 | // output: true 1790 | ``` 1791 | 1792 | #### Browser Support for `Array.prototype.some()` 1793 | 1794 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1795 | :-: | :-: | :-: | :-: | :-: | :-: | 1796 | ✔ | ✔ | 1.5 ✔ | ✔ 9.0 | ✔ | ✔ | 1797 | 1798 | **[⬆ back to top](#quick-links)** 1799 | 1800 | ### _.sortBy and _.orderBy 1801 | 1802 | Sorts an array of object based on an object key provided by a parameter (note this is more limited than Underscore/Lodash). 1803 | 1804 | ```js 1805 | const fruits = [ 1806 | {name:"banana", amount: 2}, 1807 | {name:"apple", amount: 4}, 1808 | {name:"pineapple", amount: 2}, 1809 | {name:"mango", amount: 1} 1810 | ]; 1811 | 1812 | // Underscore 1813 | _.sortBy(fruits, 'name'); 1814 | // => [{name:"apple", amount: 4}, {name:"banana", amount: 2}, {name:"mango", amount: 1}, {name:"pineapple", amount: 2}] 1815 | 1816 | // Lodash 1817 | _.orderBy(fruits, ['name'],['asc']); 1818 | // => [{name:"apple", amount: 4}, {name:"banana", amount: 2}, {name:"mango", amount: 1}, {name:"pineapple", amount: 2}] 1819 | 1820 | // Native 1821 | const sortBy = (key) => { 1822 | return (a, b) => (a[key] > b[key]) ? 1 : ((b[key] > a[key]) ? -1 : 0); 1823 | }; 1824 | 1825 | // The native sort modifies the array in place. `_.orderBy` and `_.sortBy` do not, so we use `.concat()` to 1826 | // copy the array, then sort. 1827 | fruits.concat().sort(sortBy("name")); 1828 | // => [{name:"apple", amount: 4}, {name:"banana", amount: 2}, {name:"mango", amount: 1}, {name:"pineapple", amount: 2}] 1829 | ``` 1830 | 1831 | #### Browser Support for `Array.prototype.concat()` and `Array.prototype.sort()` 1832 | 1833 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1834 | :-: | :-: | :-: | :-: | :-: | :-: | 1835 | 1.0 ✔ | ✔ | 1.0 ✔ | 5.5 ✔ | ✔ | ✔ | 1836 | 1837 | **[⬆ back to top](#quick-links)** 1838 | 1839 | ### _.uniq 1840 | 1841 | Produces a duplicate-free version of the array, using === to test object equality. 1842 | 1843 | ```js 1844 | // Underscore/Lodash 1845 | var array = [1, 2, 1, 4, 1, 3] 1846 | var result = _.uniq(array) 1847 | console.log(result) 1848 | // output: [1, 2, 4, 3] 1849 | 1850 | // Native 1851 | var array = [1, 2, 1, 4, 1, 3]; 1852 | var result = [...new Set(array)]; 1853 | console.log(result) 1854 | // output: [1, 2, 4, 3] 1855 | ``` 1856 | 1857 | #### Browser Support for Spread in array literals 1858 | 1859 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1860 | :-: | :-: | :-: | :-: | :-: | :-: | 1861 | 46.0 ✔ | 12.0 ✔ | 16.0 ✔ | ✖ | 37.0 ✔ | 8.0 ✔ | 1862 | 1863 | **[⬆ back to top](#quick-links)** 1864 | 1865 | ## Function 1866 | 1867 | ### _.after 1868 | 1869 | > [!WARNING] 1870 | > This is an alternative implementation 1871 | 1872 | Creates a version of the function that will only be run after first being called count times. Useful for grouping asynchronous responses, where you want to be sure that all the async calls have finished, before proceeding. 1873 | 1874 | ```js 1875 | var notes = ['profile', 'settings'] 1876 | // Underscore/Lodash 1877 | var renderNotes = _.after(notes.length, render) 1878 | notes.forEach(function (note) { 1879 | console.log(note) 1880 | renderNotes() 1881 | }) 1882 | 1883 | // Native 1884 | notes.forEach(function (note, index) { 1885 | console.log(note) 1886 | if (notes.length === (index + 1)) { 1887 | render() 1888 | } 1889 | }) 1890 | ``` 1891 | 1892 | #### Browser Support for `Array.prototype.forEach()` 1893 | 1894 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1895 | :-: | :-: | :-: | :-: | :-: | :-: | 1896 | ✔ | ✔ | 1.5 ✔ | 9.0 ✔ | ✔ | ✔ | 1897 | 1898 | **[⬆ back to top](#quick-links)** 1899 | 1900 | ### _.bind 1901 | 1902 | Create a new function that calls _func_ with _thisArg_ and _args_. 1903 | 1904 | ```js 1905 | var objA = { 1906 | x: 66, 1907 | offsetX: function(offset) { 1908 | return this.x + offset; 1909 | } 1910 | } 1911 | 1912 | var objB = { 1913 | x: 67 1914 | }; 1915 | 1916 | // Underscore/Lodash 1917 | var boundOffsetX = _.bind(objA.offsetX, objB, 0); 1918 | 1919 | // Native 1920 | var boundOffsetX = objA.offsetX.bind(objB, 0); 1921 | ``` 1922 | 1923 | #### Browser Support for `Function.prototype.bind()` 1924 | 1925 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1926 | :-: | :-: | :-: | :-: | :-: | :-: | 1927 | 7.0 ✔ | ✔ | 4.0 ✔ | 9.0 ✔ | 11.6 ✔ | 5.1 ✔ | 1928 | 1929 | **[⬆ back to top](#quick-links)** 1930 | 1931 | ### _.isFunction 1932 | 1933 | Checks if value is classified as a _Function_ object. 1934 | 1935 | ```js 1936 | // Lodash 1937 | _.isFunction(console.log); 1938 | // => true 1939 | 1940 | _.isFunction(/abc/); 1941 | // => false 1942 | 1943 | // Native 1944 | function isFunction(func) { 1945 | return typeof func === "function"; 1946 | } 1947 | 1948 | isFunction(setTimeout); 1949 | // => true 1950 | 1951 | isFunction(123); 1952 | // => false 1953 | ``` 1954 | 1955 | #### Browser Support 1956 | 1957 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1958 | :-: | :-: | :-: | :-: | :-: | :-: | 1959 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | 1960 | 1961 | **[⬆ back to top](#quick-links)** 1962 | 1963 | ### _.debounce 1964 | 1965 | Create a new function that calls `func` with `thisArg` and `args`. 1966 | 1967 | ```js 1968 | function debounce(func, wait, immediate) { 1969 | var timeout; 1970 | return function() { 1971 | var context = this, args = arguments; 1972 | clearTimeout(timeout); 1973 | if (immediate && !timeout) func.apply(context, args); 1974 | timeout = setTimeout(function() { 1975 | timeout = null; 1976 | if (!immediate) func.apply(context, args); 1977 | }, wait); 1978 | }; 1979 | } 1980 | 1981 | // Avoid costly calculations while the window size is in flux. 1982 | jQuery(window).on('resize', debounce(calculateLayout, 150)); 1983 | ``` 1984 | 1985 | #### Browser Support for `debounce` 1986 | 1987 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 1988 | :-: | :-: | :-: | :-: | :-: | :-: | 1989 | 7.0 ✔ | ✔ | 4.0 ✔ | 9.0 ✔ | 11.6 ✔ | 5.1 ✔ | 1990 | 1991 | **[⬆ back to top](#quick-links)** 1992 | 1993 | 1994 | ### _.partial 1995 | 1996 | Create a new function that calls `func` with `args`. 1997 | 1998 | ```js 1999 | // Lodash 2000 | function greet(greeting, name) { 2001 | return greeting + ' ' + name; 2002 | } 2003 | var sayHelloTo = _.partial(greet, 'Hello'); 2004 | var result = sayHelloTo('Jose') 2005 | console.log(result) 2006 | // output: 'Hello Jose' 2007 | 2008 | // Native 2009 | function greet(greeting, name) { 2010 | return greeting + ' ' + name; 2011 | } 2012 | var sayHelloTo = (...args) => greet('Hello', ...args) 2013 | var result = sayHelloTo('Jose') 2014 | console.log(result) 2015 | // output: 'Hello Jose' 2016 | 2017 | // Native 2018 | const partial = (func, ...boundArgs) => (...remainingArgs) => func(...boundArgs, ...remainingArgs) 2019 | var sayHelloTo = partial(greet, 'Hello'); 2020 | var result = sayHelloTo('Jose') 2021 | console.log(result) 2022 | // output: 'Hello Jose' 2023 | ``` 2024 | 2025 | #### Browser Support for Spread 2026 | 2027 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2028 | :-: | :-: | :-: | :-: | :-: | :-: | 2029 | 46.0 ✔ | 12.0 ✔ | 16.0 ✔ | ✖ | 37.0 ✔ | 8.0 ✔ | 2030 | 2031 | **[⬆ back to top](#quick-links)** 2032 | 2033 | ### _.throttle 2034 | 2035 | Create a new function that limits calls to `func` to once every given timeframe. 2036 | 2037 | ```js 2038 | function throttle(func, timeFrame) { 2039 | var lastTime = 0; 2040 | return function (...args) { 2041 | var now = new Date(); 2042 | if (now - lastTime >= timeFrame) { 2043 | func(...args); 2044 | lastTime = now; 2045 | } 2046 | }; 2047 | } 2048 | 2049 | // Avoid running the same function twice within the specified timeframe. 2050 | jQuery(window).on('resize', throttle(calculateLayout, 150)); 2051 | ``` 2052 | 2053 | #### Browser Support for `throttle` 2054 | 2055 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2056 | :-: | :-: | :-: | :-: | :-: | :-: | 2057 | 5.0 ✔ | 12.0 ✔ | 3.0 ✔ | 9.0 ✔ | 10.5 ✔ | 4.0 ✔ | 2058 | 2059 | **[⬆ back to top](#quick-links)** 2060 | 2061 | 2062 | ## Lang 2063 | 2064 | ### _.castArray 2065 | 2066 | Puts the value into an array of length one if it is not already an array. 2067 | 2068 | ```js 2069 | // Underscore 2070 | console.log(_.castArray(5)) 2071 | // output: [5] 2072 | console.log(_.castArray([2])) 2073 | // output: [2] 2074 | 2075 | // Native 2076 | function castArray(arr) { 2077 | return Array.isArray(arr) ? arr : [arr] 2078 | } 2079 | // output: true 2080 | console.log(castArray(5)); 2081 | // output: [5] 2082 | console.log(castArray([2])); 2083 | // output: [2] 2084 | ``` 2085 | 2086 | #### Browser Support for `Array.isArray()` 2087 | 2088 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2089 | :-: | :-: | :-: | :-: | :-: | :-: | 2090 | 5.0 ✔ | ✔ | 4.0 ✔ | 9.0 ✔ | 10.5 ✔ | 5.0 ✔ | 2091 | 2092 | **[⬆ back to top](#quick-links)** 2093 | 2094 | ### _.cloneDeep 2095 | Creates a deep copy by recursively cloning the value. 2096 | 2097 | ```js 2098 | // Lodash 2099 | var objects = [{ 'a': 1 }, { 'b': 2 }]; 2100 | 2101 | var clone = _.cloneDeep(objects); 2102 | console.log(clone[0] === objects[0]); 2103 | // output: false 2104 | 2105 | // Native 2106 | var objects = [{ 'a': 1 }, { 'b': 2 }]; 2107 | 2108 | var clone = structuredClone(objects); 2109 | console.log(clone[0] === objects[0]); 2110 | // output: false 2111 | ``` 2112 | 2113 | #### Browser Support for `structuredClone()` 2114 | 2115 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2116 | :-: | :-: | :-: | :-: | :-: | :-: | 2117 | 98.0 ✔ | 98.0 ✔ | 94.0 ✔ | ✖ | 84.0 ✔ | 15.4 ✔ | 2118 | 2119 | **[⬆ back to top](#quick-links)** 2120 | 2121 | ### _.isDate 2122 | 2123 | Checks if value is classified as a Date object. 2124 | 2125 | ```js 2126 | // Lodash 2127 | console.log(_.isDate(new Date)); 2128 | // output: true 2129 | 2130 | // Native 2131 | console.log(Object.prototype.toString.call(new Date) === "[object Date]"); 2132 | // output: true 2133 | ``` 2134 | 2135 | #### Browser Support for `String.prototype.toString.call()` 2136 | 2137 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2138 | :-: | :-: | :-: | :-: | :-: | :-: | 2139 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | 2140 | 2141 | **[⬆ back to top](#quick-links)** 2142 | 2143 | ### _.gt 2144 | 2145 | Checks if value is greater than other. 2146 | 2147 | ```js 2148 | // Lodash 2149 | console.log(_.gt(3, 1)) 2150 | // output: true 2151 | 2152 | // Native 2153 | console.log(3 > 1); 2154 | // output: true 2155 | ``` 2156 | 2157 | #### Browser Support 2158 | 2159 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2160 | :-: | :-: | :-: | :-: | :-: | :-: | 2161 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | 2162 | 2163 | **[⬆ back to top](#quick-links)** 2164 | 2165 | ### _.gte 2166 | 2167 | Checks if value is greater than or equal to other. 2168 | 2169 | ```js 2170 | // Lodash 2171 | console.log(_.gte(3, 1)) 2172 | // output: true 2173 | 2174 | // Native 2175 | console.log(3 >= 1); 2176 | // output: true 2177 | ``` 2178 | 2179 | #### Browser Support 2180 | 2181 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2182 | :-: | :-: | :-: | :-: | :-: | :-: | 2183 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | 2184 | 2185 | **[⬆ back to top](#quick-links)** 2186 | 2187 | ### _.isEmpty 2188 | 2189 | Checks if value is an empty object or collection. 2190 | 2191 | > [!WARNING] 2192 | > The Native version does not support evaluating a Set or a Map 2193 | 2194 | ```js 2195 | // Lodash 2196 | console.log(_.isEmpty(null)) 2197 | // output: true 2198 | console.log(_.isEmpty('')) 2199 | // output: true 2200 | console.log(_.isEmpty({})) 2201 | // output: true 2202 | console.log(_.isEmpty([])) 2203 | // output: true 2204 | console.log(_.isEmpty({a: '1'})) 2205 | // output: false 2206 | 2207 | // Native 2208 | const isEmpty = obj => [Object, Array].includes((obj || {}).constructor) && !Object.entries((obj || {})).length; 2209 | 2210 | console.log(isEmpty(null)) 2211 | // output: true 2212 | console.log(isEmpty('')) 2213 | // output: true 2214 | console.log(isEmpty({})) 2215 | // output: true 2216 | console.log(isEmpty([])) 2217 | // output: true 2218 | console.log(isEmpty({a: '1'})) 2219 | // output: false 2220 | ``` 2221 | 2222 | #### Browser Support for `Array.prototype.includes()` 2223 | 2224 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2225 | :-: | :-: | :-: | :-: | :-: | :-: | 2226 | 47.0 ✔ | 14.0 ✔ | 43.0 ✔ | ✖ | 34.0 ✔ | 9.0 ✔ | 2227 | 2228 | **[⬆ back to top](#quick-links)** 2229 | 2230 | ### _.isFinite 2231 | 2232 | Checks if value is a finite primitive number. 2233 | 2234 | ```js 2235 | // Lodash 2236 | console.log(_.isFinite('3')) 2237 | // output: false 2238 | console.log(_.isFinite(3)) 2239 | // output: true 2240 | 2241 | // Native 2242 | console.log(Number.isFinite('3')) 2243 | // output: false 2244 | console.log(Number.isFinite(3)) 2245 | // output: true 2246 | ``` 2247 | 2248 | #### Browser Support for `Number.isFinite()` 2249 | 2250 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2251 | :-: | :-: | :-: | :-: | :-: | :-: | 2252 | 19.0 ✔ | ✔ | 16.0 ✔ | ✖ | 15.0 ✔ | 9.0 ✔ | 2253 | 2254 | **[⬆ back to top](#quick-links)** 2255 | 2256 | ### _.isInteger 2257 | 2258 | Checks if value is an integer. 2259 | 2260 | ```js 2261 | // Lodash 2262 | console.log(_.isInteger(3)) 2263 | // output: true 2264 | console.log(_.isInteger('3')) 2265 | // output: false 2266 | 2267 | // Native 2268 | console.log(Number.isInteger(3)) 2269 | // output: true 2270 | console.log(Number.isInteger('3')) 2271 | // output: false 2272 | ``` 2273 | 2274 | #### Browser Support for `Number.isInteger()` 2275 | 2276 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2277 | :-: | :-: | :-: | :-: | :-: | :-: | 2278 | ✔ | 12 ✔ | 16.0 ✔ | ✖ | ✔ | ✔ | 2279 | 2280 | **[⬆ back to top](#quick-links)** 2281 | 2282 | ### _.isPlainObject 2283 | 2284 | Checks if value is a plain object, that is, an object created by the Object constructor or one with a [[Prototype]] of null. 2285 | 2286 | ```js 2287 | var object = { 'a': 1, 'b': 2, 'c': 1 }; 2288 | 2289 | // Underscore/Lodash 2290 | var result = _.isPlainObject(object); 2291 | console.log(result) 2292 | // output: true 2293 | 2294 | function isPlainObject(value) { 2295 | if (typeof value !== 'object' || value === null) return false 2296 | 2297 | if (Object.prototype.toString.call(value) !== '[object Object]') return false 2298 | 2299 | const proto = Object.getPrototypeOf(value); 2300 | if (proto === null) return true 2301 | 2302 | const Ctor = Object.prototype.hasOwnProperty.call(proto, 'constructor') && proto.constructor; 2303 | return ( 2304 | typeof Ctor === 'function' && 2305 | Ctor instanceof Ctor && Function.prototype.call(Ctor) === Function.prototype.call(value) 2306 | ); 2307 | } 2308 | ``` 2309 | 2310 | #### Browser Support for `Object.getPrototypeOf()` 2311 | 2312 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2313 | :-: | :-: | :-: | :-: | :-: | :-: | 2314 | 5.0 ✔ | 12.0 ✔ | 3.5 ✔ | ✖ | 12.1 ✔ | 5.0 ✔ | 2315 | 2316 | **[⬆ back to top](#quick-links)** 2317 | 2318 | ### _.isNaN 2319 | 2320 | Checks if a value is NaN. 2321 | 2322 | ```js 2323 | // Underscore/Lodash 2324 | console.log(_.isNaN(NaN)) 2325 | // output: true 2326 | 2327 | // Native 2328 | console.log(isNaN(NaN)) 2329 | // output: true 2330 | 2331 | // ES6 2332 | console.log(Number.isNaN(NaN)) 2333 | // output: true 2334 | ``` 2335 | 2336 | MDN: 2337 | > In comparison to the global `isNaN()` function, `Number.isNaN()` doesn't suffer the problem of forcefully converting the parameter to a number. This means it is now safe to pass values that would normally convert to `NaN`, but aren't actually the same value as `NaN`. This also means that only values of the type number, that are also `NaN`, return true. [Number.isNaN()](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN) 2338 | 2339 | Voice from the Lodash author: 2340 | 2341 | > Lodash's `_.isNaN` is equiv to ES6 `Number.isNaN` which is different than the global `isNaN`. 2342 | > --- [jdalton](https://github.com/cht8687/You-Dont-Need-Lodash-Underscore/commit/b8559a603dccaaa2449b5a68a2d8325cf1fb29cd#) 2343 | 2344 | #### Browser Support for `isNaN` 2345 | 2346 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2347 | :-: | :-: | :-: | :-: | :-: | :-: | 2348 | ✔ | ✔ | 1.0 ✔ | ✔ | ✔ | ✔ | 2349 | 2350 | #### Browser Support for `Number.isNaN` 2351 | 2352 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2353 | :-: | :-: | :-: | :-: | :-: | :-: | 2354 | 25.0 ✔ | ✔ | 15.0 ✔ | ✖ | ✔ | 9.0 ✔ | 2355 | 2356 | **[⬆ back to top](#quick-links)** 2357 | 2358 | ### _.isNil 2359 | 2360 | > [!WARNING] 2361 | > Not in Underscore.js 2362 | 2363 | Checks if value is null or undefined. 2364 | 2365 | ```js 2366 | // Lodash 2367 | console.log(_.isNil(null)) 2368 | // output: true 2369 | console.log(_.isNil(NaN)) 2370 | // output: false 2371 | console.log(_.isNil(undefined)) 2372 | // output: true 2373 | 2374 | // Native 2375 | console.log(null == null); 2376 | // output: true 2377 | console.log(NaN == null); 2378 | // output: false 2379 | console.log(undefined == null) 2380 | // output: true 2381 | ``` 2382 | 2383 | #### Browser Support 2384 | 2385 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2386 | :-: | :-: | :-: | :-: | :-: | :-: | 2387 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | 2388 | 2389 | **[⬆ back to top](#quick-links)** 2390 | 2391 | ### _.isNull 2392 | 2393 | Checks if value is null. 2394 | 2395 | ```js 2396 | // Underscore/Lodash 2397 | console.log(_.isNull(null)) 2398 | // output: true 2399 | console.log(_.isNull(void 0)) 2400 | // output: false 2401 | 2402 | // Native 2403 | console.log(null === null); 2404 | // output: true 2405 | console.log(void 0 === null); 2406 | // output: false 2407 | ``` 2408 | 2409 | #### Browser Support 2410 | 2411 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2412 | :-: | :-: | :-: | :-: | :-: | :-: | 2413 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | 2414 | 2415 | **[⬆ back to top](#quick-links)** 2416 | 2417 | ### _.isUndefined 2418 | 2419 | Checks if value is undefined. 2420 | 2421 | ```js 2422 | // Underscore/Lodash 2423 | console.log(_.isUndefined(a)) 2424 | // output: true 2425 | 2426 | // Native 2427 | console.log(typeof a === 'undefined'); 2428 | // output: true 2429 | console.log(a === undefined); 2430 | // output: true 2431 | ``` 2432 | 2433 | #### Browser Support 2434 | 2435 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2436 | :-: | :-: | :-: | :-: | :-: | :-: | 2437 | ✔ | ✔ | 1 ✔ | ✔ | ✔ | ✔ | 2438 | 2439 | **[⬆ back to top](#quick-links)** 2440 | 2441 | ## Object 2442 | 2443 | ### _.assign 2444 | 2445 | The method is used to copy the values of all enumerable own properties from one or more source objects to a target object. 2446 | 2447 | ```js 2448 | // Underscore: _.extendOwn 2449 | // Lodash 2450 | function Foo() { 2451 | this.c = 3; 2452 | } 2453 | function Bar() { 2454 | this.e = 5; 2455 | } 2456 | Foo.prototype.d = 4; 2457 | Bar.prototype.f = 6; 2458 | var result = _.assign(new Foo, new Bar); 2459 | console.log(result); 2460 | // output: { 'c': 3, 'e': 5 } 2461 | 2462 | // Native 2463 | function Foo() { 2464 | this.c = 3; 2465 | } 2466 | function Bar() { 2467 | this.e = 5; 2468 | } 2469 | Foo.prototype.d = 4; 2470 | Bar.prototype.f = 6; 2471 | var result = Object.assign({}, new Foo, new Bar); 2472 | console.log(result); 2473 | // output: { 'c': 3, 'e': 5 } 2474 | ``` 2475 | 2476 | #### Browser Support for `Object.assign()` 2477 | 2478 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2479 | :-: | :-: | :-: | :-: | :-: | :-: | 2480 | 45.0 ✔ | ✔ | 34.0 ✔ | ✖ | 32.0 ✔ | 9.0 ✔ | 2481 | 2482 | **[⬆ back to top](#quick-links)** 2483 | 2484 | ### _.defaults 2485 | 2486 | The method is used to apply new values over an object with default values for keys. 2487 | 2488 | ```js 2489 | // Underscore: _.defaults 2490 | // Lodash 2491 | const newValues = {a: 3}; 2492 | const defaultValues = {a: 1, b: 2} 2493 | const appliedValues = _.defaults(newValues, defaultValues); 2494 | console.log(appliedValues) 2495 | // output { a: 3, b: 2 } 2496 | 2497 | // Native 2498 | const newValues = {a: 3}; 2499 | const defaultValues = {a: 1, b: 2} 2500 | const appliedValues = Object.assign({}, defaultValues, newValues); 2501 | // output { a: 3, b: 2 } 2502 | ``` 2503 | 2504 | #### Browser Support for `Object.assign()` 2505 | 2506 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2507 | :-: | :-: | :-: | :-: | :-: | :-: | 2508 | 45.0 ✔ | ✔ | 34.0 ✔ | ✖ | 32.0 ✔ | 9.0 ✔ | 2509 | 2510 | **[⬆ back to top](#quick-links)** 2511 | 2512 | ### _.extend 2513 | 2514 | The method is used to copy the values of all enumerable own and inherited properties from one or more source objects to a target object. 2515 | 2516 | ```js 2517 | // Underscore 2518 | // Lodash: _.assignIn 2519 | function Foo() { 2520 | this.c = 3; 2521 | } 2522 | function Bar() { 2523 | this.e = 5; 2524 | } 2525 | Foo.prototype.d = 4; 2526 | Bar.prototype.f = 6; 2527 | var result = _.extend({}, new Foo, new Bar); 2528 | console.log(result); 2529 | // output: { 'c': 3, 'd': 4, 'e': 5, 'f': 6 } 2530 | 2531 | // Native 2532 | function Foo() { 2533 | this.c = 3; 2534 | } 2535 | function Bar() { 2536 | this.e = 5; 2537 | } 2538 | Foo.prototype.d = 4; 2539 | Bar.prototype.f = 6; 2540 | var result = Object.assign({}, new Foo, Foo.prototype, new Bar, Bar.prototype); 2541 | console.log(result); 2542 | // output: { 'c': 3, 'd': 4, 'e': 5, 'f': 6 } 2543 | 2544 | //Or using a function 2545 | const extend = (target, ...sources) => { 2546 | const length = sources.length; 2547 | 2548 | if (length < 1 || target == null) return target; 2549 | for (let i = 0; i < length; i++) { 2550 | const source = sources[i]; 2551 | 2552 | for (const key in source) { 2553 | target[key] = source[key]; 2554 | } 2555 | } 2556 | return target; 2557 | }; 2558 | console.log(extend({}, new Foo, new Bar)); 2559 | // output: { 'c': 3, 'd': 4, 'e': 5, 'f': 6 } 2560 | ``` 2561 | 2562 | #### Browser Support for `Object.assign()` 2563 | 2564 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2565 | :-: | :-: | :-: | :-: | :-: | :-: | 2566 | 45.0 ✔ | ✔ | 34.0 ✔ | ✖ | 32.0 ✔ | 9.0 ✔ | 2567 | 2568 | **[⬆ back to top](#quick-links)** 2569 | 2570 | ### _.has 2571 | 2572 | Checks if `key` is a direct property of `object`. Key may be a path of a value separated by `.` 2573 | 2574 | ```js 2575 | // Lodash 2576 | var object = { a: 1, b: 'settings', c: { d: 'test' } }; 2577 | 2578 | var hasA = _.has(object, 'a'); 2579 | var hasCWhichHasD = _.has(object, 'c.d') 2580 | 2581 | console.log(hasA); 2582 | // output: true 2583 | console.log(hasCWhichHasD); 2584 | // output: true 2585 | 2586 | // Native 2587 | const has = function (obj, key) { 2588 | var keyParts = key.split('.'); 2589 | 2590 | return !!obj && ( 2591 | keyParts.length > 1 2592 | ? has(obj[key.split('.')[0]], keyParts.slice(1).join('.')) 2593 | : hasOwnProperty.call(obj, key) 2594 | ); 2595 | }; 2596 | 2597 | var object = { a: 1, b: 'settings' }; 2598 | var result = has(object, 'a'); 2599 | // output: true 2600 | ``` 2601 | 2602 | #### Browser Support for Object .hasOwnProperty 2603 | 2604 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2605 | :-: | :-: | :-: | :-: | :-: | :-: | 2606 | ✔ | 12 ✔ | ✔ | 5.5 ✔ | 5 ✔ | 3 ✔ | 2607 | 2608 | **[⬆ back to top](#quick-links)** 2609 | 2610 | ### _.get 2611 | 2612 | Gets the value at path of object. 2613 | 2614 | > [!NOTE] 2615 | > If provided path does not exist inside the object js will generate error. 2616 | 2617 | ```js 2618 | // Lodash 2619 | var object = { a: [{ b: { c: 3 } }] }; 2620 | var result = _.get(object, 'a[0].b.c', 1); 2621 | console.log(result); 2622 | // output: 3 2623 | 2624 | // Native (ES6 - IE not supported) 2625 | var object = { a: [{ b: { c: 3 } }] }; 2626 | var { a: [{ b: { c: result = 1 } = {} } = {}] = [] } = object; 2627 | console.log(result); 2628 | // output: 3 2629 | 2630 | // Native (ES11) 2631 | var object = { a: [{ b: { c: 3 } }] }; 2632 | var result = object?.a?.[0]?.b?.c ?? 1; 2633 | console.log(result); 2634 | // output: 3 2635 | 2636 | // Native 2637 | const get = (obj, path, defaultValue = undefined) => { 2638 | const travel = regexp => 2639 | String.prototype.split 2640 | .call(path, regexp) 2641 | .filter(Boolean) 2642 | .reduce((res, key) => (res !== null && res !== undefined ? res[key] : res), obj); 2643 | const result = travel(/[,[\]]+?/) || travel(/[,[\].]+?/); 2644 | return result === undefined || result === obj ? defaultValue : result; 2645 | }; 2646 | 2647 | var object = { a: [{ b: { c: 3 } }] }; 2648 | var result = get(object, 'a[0].b.c', 1); 2649 | // output: 3 2650 | ``` 2651 | 2652 | #### Browser Support for Object destructing 2653 | 2654 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2655 | :-: | :-: | :-: | :-: | :-: | :-: | 2656 | 49.0 ✔ | 14.0 ✔ | 41.0 ✔ | ✖ | 41.0 ✔ | 8.0 ✔ | 2657 | 2658 | #### Browser Support for optional chaining `?.` 2659 | 2660 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2661 | :-: | :-: | :-: | :-: | :-: | :-: | 2662 | 80.0 ✔ | 80.0 ✔ | 74.0 ✔ | ✖ | 67.0 ✔ | 13.1 ✔ | 2663 | 2664 | #### Browser Support for nullish coalescing operator `??` 2665 | 2666 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2667 | :-: | :-: | :-: | :-: | :-: | :-: | 2668 | 80.0 ✔ | 80.0 ✔ | 72.0 ✔ | ✖ | ✖ | 13.1 ✔ | 2669 | 2670 | **[⬆ back to top](#quick-links)** 2671 | 2672 | ### _.invert 2673 | 2674 | Creates an object composed of the inverted keys and values of object. If object contains duplicate values, subsequent values overwrite property assignments of previous values. 2675 | 2676 | ```js 2677 | var object = { 'a': 1, 'b': 2, 'c': 1 }; 2678 | 2679 | // Underscore/Lodash 2680 | var result = _.invert(object); 2681 | console.log(result) 2682 | // output: { '1': 'c', '2': 'b' } 2683 | 2684 | // Native (IE6) 2685 | function invert(object) { 2686 | var obj = {}; 2687 | for (var key in object) { 2688 | if (object.hasOwnProperty(key)) { 2689 | obj[object[key]] = key; 2690 | } 2691 | } 2692 | return obj; 2693 | } 2694 | var result = invert(object); 2695 | console.log(result) 2696 | // output: { '1': 'c', '2': 'b' } 2697 | 2698 | // Native (IE not supported) 2699 | const invert = object => Object.entries(object) 2700 | .reduce((acc, current) => { 2701 | acc[current[1]] = current[0]; 2702 | return acc; 2703 | }, {} 2704 | ); 2705 | ``` 2706 | #### Browser Support 2707 | 2708 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2709 | :-: | :-: | :-: | :-: | :-: | :-: | 2710 | 4.0 ✔ | ✔ | 2.0 ✔ | 6.0 ✔ | 10.0 ✔ | 3.1 ✔ | 2711 | 2712 | #### Browser Support for `Object.entries()` 2713 | 2714 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2715 | :-: | :-: | :-: | :-: | :-: | :-: | 2716 | 54.0 ✔ | 14.0 ✔ | 47.0 ✔ | ✖ | 41.0 ✔ | 10.1 ✔ | 2717 | 2718 | **[⬆ back to top](#quick-links)** 2719 | 2720 | ### _.keys 2721 | 2722 | Retrieves all the names of the object's own enumerable properties. 2723 | 2724 | ```js 2725 | // Underscore/Lodash 2726 | var result = _.keys({one: 1, two: 2, three: 3}) 2727 | console.log(result) 2728 | // output: ["one", "two", "three"] 2729 | 2730 | // Native 2731 | var result2 = Object.keys({one: 1, two: 2, three: 3}) 2732 | console.log(result2) 2733 | // output: ["one", "two", "three"] 2734 | ``` 2735 | 2736 | #### Browser Support for `Object.keys()` 2737 | 2738 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2739 | :-: | :-: | :-: | :-: | :-: | :-: | 2740 | 5.0 ✔ | ✔ | 4.0 ✔ | 9.0 ✔ | 12.0 ✔ | 5.0 ✔ | 2741 | 2742 | **[⬆ back to top](#quick-links)** 2743 | 2744 | ### _.mapKeys 2745 | 2746 | The opposite of _.mapValues; this method creates an object with the same values as object and keys generated by running each own enumerable string keyed property of object thru iteratee. The iteratee is invoked with three arguments: (value, key, object). 2747 | 2748 | ```js 2749 | // Lodash 2750 | var result = _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { 2751 | return key + value; 2752 | }); 2753 | console.log(result) 2754 | // output: { 'a1': 1, 'b2': 2 } 2755 | 2756 | // Native (IE6) 2757 | function mapKeys(object, cb) { 2758 | var obj = {}; 2759 | for (var key in object) { 2760 | if (object.hasOwnProperty(key)) { 2761 | var newKey = cb(object[key], key, object); 2762 | obj[newKey] = object[key]; 2763 | } 2764 | } 2765 | return obj; 2766 | } 2767 | var result = mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { 2768 | return key + value; 2769 | }); 2770 | console.log(result) 2771 | // output: { 'a1': 1, 'b2': 2 } 2772 | 2773 | // Native (IE not supported) 2774 | const mapKeys = (object, cb) => Object.entries(object) 2775 | .reduce((acc, current) => { 2776 | const newKey = cb(current[1], current[0], object); 2777 | acc[newKey] = current[1]; 2778 | return acc; 2779 | }, {} 2780 | ); 2781 | const result2 = mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { 2782 | return key + value; 2783 | }); 2784 | console.log(result2) 2785 | // output: { 'a1': 1, 'b2': 2 } 2786 | ``` 2787 | #### Browser Support 2788 | 2789 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2790 | :-: | :-: | :-: | :-: | :-: | :-: | 2791 | 4.0 ✔ | ✔ | 2.0 ✔ | 6.0 ✔ | 10.0 ✔ | 3.1 ✔ | 2792 | 2793 | #### Browser Support for `Object.entries()` 2794 | 2795 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2796 | :-: | :-: | :-: | :-: | :-: | :-: | 2797 | 54.0 ✔ | 14.0 ✔ | 47.0 ✔ | ✖ | 41.0 ✔ | 10.1 ✔ | 2798 | 2799 | **[⬆ back to top](#quick-links)** 2800 | 2801 | ### _.omit 2802 | 2803 | Returns a copy of the object, filtered to omit the keys specified. 2804 | 2805 | ```js 2806 | var object = { 'a': 1, 'b': '2', 'c': 3 }; 2807 | 2808 | // Underscore/Lodash 2809 | var result = _.omit(object, ['a', 'c']); 2810 | console.log(result) 2811 | // output: { 'b': '2' } 2812 | 2813 | // Native 2814 | var { a, c, ...result2 } = object; 2815 | console.log(result2) 2816 | // output: { 'b': '2' } 2817 | ``` 2818 | 2819 | #### Browser Support for Spread in object literals 2820 | 2821 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2822 | :-: | :-: | :-: | :-: | :-: | :-: | 2823 | 60.0 ✔ | 79 ✔ | 55.0 ✔ | ✖ | 37.0 ✔ | ✖ | 2824 | 2825 | **[⬆ back to top](#quick-links)** 2826 | 2827 | ### _.pick 2828 | 2829 | Creates an object composed of the object properties predicate returns truthy for. 2830 | 2831 | ```js 2832 | var object = { 'a': 1, 'b': '2', 'c': 3 }; 2833 | 2834 | // Underscore/Lodash 2835 | var result = _.pick(object, ['a', 'c', 'x']); 2836 | console.log(result) 2837 | // output: {a: 1, c: 3} 2838 | 2839 | // Native 2840 | function pick(object, keys) { 2841 | return keys.reduce((obj, key) => { 2842 | if (object && object.hasOwnProperty(key)) { 2843 | obj[key] = object[key]; 2844 | } 2845 | return obj; 2846 | }, {}); 2847 | } 2848 | var result = pick(object, ['a', 'c', 'x']); 2849 | console.log(result) 2850 | // output: {a: 1, c: 3} 2851 | ``` 2852 | 2853 | #### Browser Support 2854 | 2855 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2856 | :-: | :-: | :-: | :-: | :-: | :-: | 2857 | 38.0 ✔ | ✔ | 13.0 ✔ | 12.0 ✔ | 25.0 ✔ | 7.1 ✔ | 2858 | 2859 | **[⬆ back to top](#quick-links)** 2860 | 2861 | ### _.pickBy 2862 | 2863 | Creates an object composed of the object properties predicate returns truthy for. 2864 | 2865 | ```js 2866 | var object = { 'a': 1, 'b': null, 'c': 3, 'd': false, 'e': undefined }; 2867 | 2868 | // Underscore/Lodash 2869 | var result = _.pickBy(object); 2870 | console.log(result) 2871 | // output: {a: 1, c: 3} 2872 | 2873 | // Native 2874 | function pickBy(object) { 2875 | const obj = {}; 2876 | for (const key in object) { 2877 | if (object[key]) { 2878 | obj[key] = object[key]; 2879 | } 2880 | } 2881 | return obj; 2882 | } 2883 | var result = pickBy(object); 2884 | console.log(result) 2885 | // output: {a: 1, c: 3} 2886 | ``` 2887 | 2888 | #### Browser Support 2889 | 2890 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2891 | :-: | :-: | :-: | :-: | :-: | :-: | 2892 | ✔ | ✔ | ✔ | 6.0 ✔ | ✔ | ✔ | 2893 | 2894 | **[⬆ back to top](#quick-links)** 2895 | 2896 | ### _.toPairs 2897 | 2898 | Retrieves all the given object's own enumerable property `[ key, value ]` pairs. 2899 | 2900 | ```js 2901 | // Underscore - also called _.pairs 2902 | // Lodash - also called _.entries 2903 | var result = _.toPairs({one: 1, two: 2, three: 3}) 2904 | console.log(result) 2905 | // output: [["one", 1], ["two", 2], ["three", 3]] 2906 | 2907 | // Native 2908 | var result2 = Object.entries({one: 1, two: 2, three: 3}) 2909 | console.log(result2) 2910 | // output: [["one", 1], ["two", 2], ["three", 3]] 2911 | ``` 2912 | 2913 | #### Browser Support for `Object.entries()` 2914 | 2915 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2916 | :-: | :-: | :-: | :-: | :-: | :-: | 2917 | 54.0 ✔ | 14.0 ✔ | 47.0 ✔ | ✖ | 41.0 ✔ | 10.1 ✔ | 2918 | 2919 | **[⬆ back to top](#quick-links)** 2920 | 2921 | ### _.values 2922 | 2923 | Retrieves all the given object's own enumerable property values. 2924 | 2925 | ```js 2926 | // Underscore/Lodash 2927 | var result = _.values({one: 1, two: 2, three: 3}) 2928 | console.log(result) 2929 | // output: [1, 2, 3] 2930 | 2931 | // Native 2932 | var result2 = Object.values({one: 1, two: 2, three: 3}) 2933 | console.log(result2) 2934 | // output: [1, 2, 3] 2935 | ``` 2936 | 2937 | #### Browser Support for `Object.values()` 2938 | 2939 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2940 | :-: | :-: | :-: | :-: | :-: | :-: | 2941 | 54.0 ✔ | 14.0 ✔ | 47.0 ✔ | ✖ | 41.0 ✔ | 10.1 ✔ | 2942 | 2943 | **[⬆ back to top](#quick-links)** 2944 | 2945 | ## String 2946 | 2947 | ### _.capitalize 2948 | 2949 | > [!WARNING] 2950 | > Not in Underscore.js 2951 | 2952 | Converts the first character of string to upper case and the remaining to lower case. 2953 | 2954 | ```js 2955 | // Lodash 2956 | var result = _.capitalize('FRED'); 2957 | console.log(result); 2958 | // => 'Fred' 2959 | 2960 | // Native 2961 | const capitalize = (string) => { 2962 | return string ? string.charAt(0).toUpperCase() + string.slice(1).toLowerCase() : ''; 2963 | }; 2964 | 2965 | var result = capitalize('FRED'); 2966 | console.log(result); 2967 | // => 'Fred' 2968 | ``` 2969 | 2970 | #### Browser Support 2971 | 2972 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 2973 | :-: | :-: | :-: | :-: | :-: | :-: | 2974 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | 2975 | 2976 | **[⬆ back to top](#quick-links)** 2977 | 2978 | ### _.endsWith 2979 | 2980 | > [!WARNING] 2981 | > Not in Underscore.js 2982 | 2983 | Checks if string ends with the given target string. 2984 | 2985 | ```js 2986 | // Lodash 2987 | _.endsWith('abc', 'c'); 2988 | // => true 2989 | 2990 | _.endsWith('abc', 'b'); 2991 | // => false 2992 | 2993 | _.endsWith('abc', 'b', 2); 2994 | // => true 2995 | 2996 | // Native 2997 | 'abc'.endsWith('c'); 2998 | // => true 2999 | 3000 | 'abc'.endsWith('b'); 3001 | // => false 3002 | 3003 | 'abc'.endsWith('b', 2); 3004 | // => true 3005 | ``` 3006 | 3007 | #### Browser Support for `String.prototype.endsWith()` 3008 | 3009 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3010 | :-: | :-: | :-: | :-: | :-: | :-: | 3011 | 41.0 ✔ | ✔ | 17.0 ✔ | ✖ | 28.0 ✔ | 9.0 ✔ | 3012 | 3013 | **[⬆ back to top](#quick-links)** 3014 | 3015 | ### _.isString 3016 | 3017 | Checks if value is classified as a String primitive or object. 3018 | 3019 | ```js 3020 | // Lodash 3021 | _.isString('abc'); 3022 | // => true 3023 | 3024 | _.isString(123); 3025 | // => false 3026 | 3027 | // Native 3028 | function isString(str){ 3029 | if (str != null && typeof str.valueOf() === "string") { 3030 | return true 3031 | } 3032 | return false 3033 | } 3034 | 3035 | isString('abc'); 3036 | // => true 3037 | 3038 | isString(123); 3039 | // => false 3040 | ``` 3041 | 3042 | #### Browser Support 3043 | 3044 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3045 | :-: | :-: | :-: | :-: | :-: | :-: | 3046 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | 3047 | 3048 | **[⬆ back to top](#quick-links)** 3049 | 3050 | ### _.lowerFirst 3051 | 3052 | > [!WARNING] 3053 | > Not in Underscore.js 3054 | 3055 | Converts the first character of string to lower case. 3056 | 3057 | ```js 3058 | // Lodash 3059 | var result = _.lowerFirst('Fred') 3060 | console.log(result) 3061 | // output: 'fred' 3062 | 3063 | // Native 3064 | const lowerFirst = (string) => { 3065 | return string ? string.charAt(0).toLowerCase() + string.slice(1) : '' 3066 | } 3067 | 3068 | var result = lowerFirst('Fred') 3069 | console.log(result) 3070 | // output: 'fred' 3071 | ``` 3072 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3073 | :-: | :-: | :-: | :-: | :-: | :-: | 3074 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | 3075 | 3076 | **[⬆ back to top](#quick-links)** 3077 | 3078 | ### _.padStart and _.padEnd 3079 | 3080 | > [!WARNING] 3081 | > Not in Underscore.js 3082 | 3083 | Pads the current string with another string (multiple times, if needed) until the resulting string reaches the given length. 3084 | 3085 | ```js 3086 | // Lodash 3087 | console.log(_.padStart('123', 5, '0')) 3088 | // output: '00123' 3089 | 3090 | console.log(_.padEnd('123', 5, '0')) 3091 | // output: '12300' 3092 | 3093 | // Native 3094 | console.log('123'.padStart(5, '0')) 3095 | // output: '00123' 3096 | 3097 | console.log('123'.padEnd(5, '0')) 3098 | // output: '12300' 3099 | ``` 3100 | 3101 | #### Browser Support for `String.prototype.padStart()` and `String.prototype.padEnd()` 3102 | 3103 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3104 | :-: | :-: | :-: | :-: | :-: | :-: | 3105 | 57.0 ✔ | 15.0 ✔ | 48.0 ✔ | ✖ | 44.0 ✔ | 10.0 ✔ | 3106 | 3107 | **[⬆ back to top](#quick-links)** 3108 | 3109 | ### _.repeat 3110 | 3111 | > [!WARNING] 3112 | > Not in Underscore.js 3113 | 3114 | Repeats the given string n times. 3115 | 3116 | ```js 3117 | // Lodash 3118 | var result = _.repeat('abc', 2) 3119 | console.log(result) 3120 | // output: 'abcabc' 3121 | 3122 | // Native 3123 | var result = 'abc'.repeat(2) 3124 | console.log(result) 3125 | // output: 'abcabc' 3126 | ``` 3127 | 3128 | #### Browser Support for `String.prototype.repeat()` 3129 | 3130 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3131 | :-: | :-: | :-: | :-: | :-: | :-: | 3132 | 41.0 ✔ | ✔ | 24.0 ✔ | ✖ | 28.0 ✔ | 9.0 ✔ | 3133 | 3134 | **[⬆ back to top](#quick-links)** 3135 | 3136 | ### _.replace 3137 | 3138 | Returns a new string with some or all matches of a `pattern` replaced by a `replacement`. 3139 | 3140 | ```js 3141 | // Lodash 3142 | var re = /apples/gi; 3143 | var str = 'Apples are round, and apples are juicy.'; 3144 | var newstr = _.replace(str, re, 'oranges'); 3145 | console.log(newstr); 3146 | // output: 'oranges are round, and oranges are juicy.' 3147 | 3148 | // Native 3149 | var re = /apples/gi; 3150 | var str = 'Apples are round, and apples are juicy.'; 3151 | var result = str.replace(re, 'oranges'); 3152 | console.log(result); 3153 | // output: 'oranges are round, and oranges are juicy.' 3154 | ``` 3155 | 3156 | #### Browser Support for `String.prototype.replace()` 3157 | 3158 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3159 | :-: | :-: | :-: | :-: | :-: | :-: | 3160 | ✔ | ✔ | 1.0 ✔ | ✔ | ✔ | ✔ | 3161 | 3162 | **[⬆ back to top](#quick-links)** 3163 | 3164 | ### _.split 3165 | 3166 | > [!WARNING] 3167 | > Not in Underscore.js 3168 | 3169 | Splits string by separator. 3170 | 3171 | ```js 3172 | // Lodash 3173 | var result = _.split('a-b-c', '-', 2) 3174 | console.log(result) 3175 | // output: ['a','b'] 3176 | 3177 | // Native 3178 | var result = 'a-b-c'.split('-', 2) 3179 | console.log(result) 3180 | // output: ['a','b'] 3181 | ``` 3182 | 3183 | #### Browser Support for `String.prototype.split()` 3184 | 3185 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3186 | :-: | :-: | :-: | :-: | :-: | :-: | 3187 | ✔ | ✔ | 1.0 ✔ | ✔ | ✔ | ✔ | 3188 | 3189 | **[⬆ back to top](#quick-links)** 3190 | 3191 | ### _.startsWith 3192 | 3193 | > [!WARNING] 3194 | > Not in Underscore.js 3195 | 3196 | Checks if string starts with the given target string. 3197 | 3198 | ```js 3199 | // Lodash 3200 | var result = _.startsWith('abc', 'b', 1) 3201 | console.log(result) 3202 | // output: true 3203 | 3204 | // Native 3205 | var result = 'abc'.startsWith('b', 1) 3206 | console.log(result) 3207 | // output: true 3208 | ``` 3209 | 3210 | #### Browser Support for `String.prototype.startsWith()` 3211 | 3212 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3213 | :-: | :-: | :-: | :-: | :-: | :-: | 3214 | 41.0 ✔ | ✔ | 17.0 ✔ | ✖ | 28.0 ✔ | 9.0 ✔ | 3215 | 3216 | **[⬆ back to top](#quick-links)** 3217 | 3218 | ### _.template 3219 | 3220 | > [!NOTE] 3221 | > This is an alternative implementation. Native template literals not escape html. 3222 | 3223 | Create a template function. 3224 | 3225 | ```js 3226 | // Lodash/Underscore 3227 | const compiled = _.template('hello <%= user %>!'); 3228 | var result = compiled({ 'user': 'fred' }); 3229 | console.log(result); 3230 | // output: 'hello fred' 3231 | 3232 | // Native 3233 | const templateLiteral = (value) => `hello ${value.user}`; 3234 | var result = templateLiteral({ 'user': 'fred' }); 3235 | console.log(result); 3236 | // output: 'hello fred' 3237 | ``` 3238 | 3239 | #### Browser Support for String (template) literals 3240 | 3241 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3242 | :-: | :-: | :-: | :-: | :-: | :-: | 3243 | 41.0 ✔ | 12.0 ✔ | 34.0 ✔ | ✖ | 28.0 ✔ | 9.0 ✔ | 3244 | 3245 | **[⬆ back to top](#quick-links)** 3246 | 3247 | ### _.toLower 3248 | 3249 | > [!WARNING] 3250 | > Not in Underscore.js 3251 | 3252 | Lowercases a given string. 3253 | 3254 | ```js 3255 | // Lodash 3256 | var result = _.toLower('FOOBAR') 3257 | console.log(result) 3258 | // output: 'foobar' 3259 | 3260 | // Native 3261 | var result = 'FOOBAR'.toLowerCase() 3262 | console.log(result) 3263 | // output: 'foobar' 3264 | ``` 3265 | 3266 | #### Browser Support for `String.prototype.toLowerCase()` 3267 | 3268 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3269 | :-: | :-: | :-: | :-: | :-: | :-: | 3270 | ✔ | ✔ | 1.0 ✔ | ✔ | ✔ | ✔ | 3271 | 3272 | **[⬆ back to top](#quick-links)** 3273 | 3274 | ### _.toUpper 3275 | 3276 | > [!WARNING] 3277 | > Not in Underscore.js 3278 | 3279 | Uppercases a given string. 3280 | 3281 | ```js 3282 | // Lodash 3283 | var result = _.toUpper('foobar') 3284 | console.log(result) 3285 | // output: 'FOOBAR' 3286 | 3287 | // Native 3288 | var result = 'foobar'.toUpperCase() 3289 | console.log(result) 3290 | // output: 'FOOBAR' 3291 | ``` 3292 | 3293 | #### Browser Support for `String.prototype.toUpperCase()` 3294 | 3295 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3296 | :-: | :-: | :-: | :-: | :-: | :-: | 3297 | ✔ | ✔ | 1.0 ✔ | ✔ | ✔ | ✔ | 3298 | 3299 | **[⬆ back to top](#quick-links)** 3300 | 3301 | ### _.trim 3302 | 3303 | > [!WARNING] 3304 | > Not in Underscore.js 3305 | 3306 | Removes the leading and trailing whitespace characters from a string. 3307 | 3308 | ```js 3309 | // Lodash 3310 | var result = _.trim(' abc ') 3311 | console.log(result) 3312 | // output: 'abc' 3313 | 3314 | // Native 3315 | var result = ' abc '.trim() 3316 | console.log(result) 3317 | // output: 'abc' 3318 | ``` 3319 | 3320 | #### Browser Support for `String.prototype.trim()` 3321 | 3322 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3323 | :-: | :-: | :-: | :-: | :-: | :-: | 3324 | 5.0 ✔ | ✔ | 3.5 ✔ | 9.0 ✔ | 10.5 ✔ | 5.0 ✔ | 3325 | 3326 | **[⬆ back to top](#quick-links)** 3327 | 3328 | ### _.upperFirst 3329 | 3330 | > [!WARNING] 3331 | > Not in Underscore.js 3332 | 3333 | Uppercases the first letter of a given string. 3334 | 3335 | ```js 3336 | // Lodash 3337 | var result = _.upperFirst('george') 3338 | console.log(result) 3339 | // output: 'George' 3340 | 3341 | // Native 3342 | const upperFirst = (string) => { 3343 | return string ? string.charAt(0).toUpperCase() + string.slice(1) : '' 3344 | } 3345 | 3346 | var result = upperFirst('george') 3347 | console.log(result) 3348 | // output: 'George' 3349 | ``` 3350 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3351 | :-: | :-: | :-: | :-: | :-: | :-: | 3352 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | 3353 | 3354 | **[⬆ back to top](#quick-links)** 3355 | 3356 | ## Reference 3357 | 3358 | * [Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference) 3359 | * [Underscore.js](http://underscorejs.org) 3360 | * [Lodash.js](https://lodash.com/docs) 3361 | 3362 | **[⬆ back to top](#quick-links)** 3363 | 3364 | ### _.uniqWith 3365 | 3366 | Similar to `_.uniq` except that it accepts comparator which is invoked to compare elements of array. The order of result values is determined by the order they occur in the array. 3367 | 3368 | ```js 3369 | // Lodash 3370 | const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }]; 3371 | const result = _.uniqWith(objects, _.isEqual); 3372 | console.log(result); 3373 | // output: [{ x: 1, y: 2 }, { x: 2, y: 1 }] 3374 | 3375 | // Native 3376 | const uniqWith = (arr, fn) => arr.filter((element, index) => arr.findIndex((step) => fn(element, step)) === index); 3377 | 3378 | const array = [1, 2, 2, 3, 4, 5, 2]; 3379 | const result = uniqWith(array, (a, b) => a === b); 3380 | console.log(result); 3381 | // output: [1, 2, 3, 4, 5] 3382 | 3383 | const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }]; 3384 | const result = uniqWith(objects, (a, b) => JSON.stringify(a) === JSON.stringify(b)); 3385 | console.log(result); 3386 | // output: [{ x: 1, y: 2 }, { x: 2, y: 1 }] 3387 | ``` 3388 | 3389 | ### Browser Support for `Array.prototype.filter()` and `Array.prototype.findIndex()` 3390 | 3391 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3392 | :-: | :-: | :-: | :-: | :-: | :-: | 3393 | 45.0 ✔ | 12.0 ✔ | 25.0 ✔ | ✖ | 32.0 ✔ | 8.0 ✔ | 3394 | 3395 | **[⬆ back to top](#quick-links)** 3396 | 3397 | ## Util 3398 | 3399 | ### _.times 3400 | 3401 | Invokes the iteratee n times, returning an array of the results of each invocation. 3402 | 3403 | ```js 3404 | // Lodash 3405 | var result = _.times(10) 3406 | console.log(result) 3407 | // output: '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]' 3408 | 3409 | // Native 3410 | var result = Array.from({length: 10}, (_,x) => x) 3411 | console.log(result) 3412 | // output: '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]' 3413 | 3414 | // Native 3415 | var result = [...Array(10).keys()] 3416 | console.log(result) 3417 | // output: '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]' 3418 | ``` 3419 | 3420 | #### Browser Support for `Array.from()` 3421 | 3422 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3423 | :-: | :-: | :-: | :-: | :-: | :-: | 3424 | 45.0 ✔ | ✔ | 32.0 ✔ | ✖ | ✔ | 9.0 ✔ | 3425 | 3426 | **[⬆ back to top](#quick-links)** 3427 | 3428 | ## Number 3429 | 3430 | ### _.clamp 3431 | 3432 | Clamps number within the inclusive lower and upper bounds. 3433 | 3434 | ```js 3435 | // Lodash 3436 | _.clamp(-10, -5, 5); 3437 | // => -5 3438 | 3439 | _.clamp(10, -5, 5); 3440 | // => 5 3441 | 3442 | _.clamp(10, -5); 3443 | // => -5 3444 | 3445 | _.clamp(10, 99); 3446 | // => 10 3447 | 3448 | // Native 3449 | const clamp = (number, boundOne, boundTwo) => { 3450 | if (!boundTwo) { 3451 | return Math.max(number, boundOne) === boundOne ? number : boundOne; 3452 | } else if (Math.min(number, boundOne) === number) { 3453 | return boundOne; 3454 | } else if (Math.max(number, boundTwo) === number) { 3455 | return boundTwo; 3456 | } 3457 | return number; 3458 | }; 3459 | 3460 | clamp(-10, -5, 5); 3461 | // => -5 3462 | 3463 | clamp(10, -5, 5); 3464 | // => 5 3465 | 3466 | clamp(10, -5); 3467 | // => -5 3468 | 3469 | clamp(10, 99); 3470 | // => 10 3471 | ``` 3472 | 3473 | #### Browser Support for `Math.min() and Math.max()` 3474 | 3475 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3476 | :-: | :-: | :-: | :-: | :-: | :-: | 3477 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | 3478 | 3479 | **[⬆ back to top](#quick-links)** 3480 | 3481 | ### _.inRange 3482 | 3483 | Checks if n is between start and up to, but not including, end. If end is not specified, it's set to start with start then set to 0. If start is greater than end the params are swapped to support negative ranges. 3484 | 3485 | ```js 3486 | // Lodash 3487 | _.inRange(3, 2, 4); 3488 | // output: true 3489 | _.inRange(-3, -2, -6); 3490 | // output: true 3491 | 3492 | //Native 3493 | const inRange = (num, init, final) => { 3494 | if(final === undefined){ 3495 | final = init; 3496 | init = 0; 3497 | } 3498 | return (num >= Math.min(init, final) && num < Math.max(init, final)); 3499 | } 3500 | 3501 | //Native 3502 | const inRange = (num, a, b=0) => (Math.min(a,b) <= num && num < Math.max(a,b)); 3503 | 3504 | inRange(3, 2, 4); 3505 | // output: true 3506 | inRange(-3, -2, -6); 3507 | // output: true 3508 | ``` 3509 | 3510 | #### Browser Support for `Math.min() and Math.max()` 3511 | 3512 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3513 | :-: | :-: | :-: | :-: | :-: | :-: | 3514 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | 3515 | 3516 | **[⬆ back to top](#quick-links)** 3517 | 3518 | ### _.random 3519 | 3520 | Produces a random number between the inclusive lower and upper bounds. If only one argument is provided a number between 0 and the given number is returned. If floating is true, or either lower or upper are floats, a floating-point number is returned instead of an integer. 3521 | 3522 | ```js 3523 | // Lodash 3524 | _.random(0, 5); 3525 | // => an integer between 0 and 5 3526 | 3527 | _.random(5); 3528 | // => also an integer between 0 and 5 3529 | 3530 | _.random(5, true); 3531 | // => a floating-point number between 0 and 5 3532 | 3533 | _.random(1.2, 5.2); 3534 | // => a floating-point number between 1.2 and 5.2 3535 | 3536 | //Native ES6 3537 | const random = (a = 1, b = 0) => { 3538 | const lower = Math.min(a, b); 3539 | const upper = Math.max(a, b); 3540 | return lower + Math.random() * (upper - lower); 3541 | }; 3542 | 3543 | const randomInt = (a = 1, b = 0) => { 3544 | const lower = Math.ceil(Math.min(a, b)); 3545 | const upper = Math.floor(Math.max(a, b)); 3546 | return Math.floor(lower + Math.random() * (upper - lower + 1)) 3547 | }; 3548 | 3549 | random(); 3550 | // => a floating-point number between 0 and 1 3551 | 3552 | random(5); 3553 | // => a floating-point number between 0 and 5 3554 | 3555 | random(0, 5); 3556 | // => also a floating-point number between 0 and 5 3557 | 3558 | random(1.2, 5.2); 3559 | // => a floating-point number between 1.2 and 5.2 3560 | 3561 | randomInt(); 3562 | // => just 0 or 1 3563 | 3564 | randomInt(5); 3565 | // => an integer between 0 and 5 3566 | 3567 | randomInt(0, 5); 3568 | // => also an integer between 0 and 5 3569 | 3570 | randomInt(1.2, 5.2); 3571 | // => an integer between 2 and 5 3572 | 3573 | ``` 3574 | 3575 | #### Browser Support for `Math.random()` 3576 | 3577 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 3578 | :-: | :-: | :-: | :-: | :-: | :-: | 3579 | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | 3580 | 3581 | **[⬆ back to top](#quick-links)** 3582 | 3583 | 3584 | ## Inspired by: 3585 | 3586 | * [You-Dont-Need-jQuery](https://github.com/oneuijs/You-Dont-Need-jQuery) 3587 | * [Rui's blog](http://ktei.github.io/2016/01/07/some-general-js-tips-1.html) 3588 | 3589 | 3590 | ## License 3591 | 3592 | MIT 3593 | 3594 | [chrome-image]: https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png 3595 | [firefox-image]: https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png 3596 | [ie-image]: https://raw.githubusercontent.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png 3597 | [opera-image]: https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png 3598 | [safari-image]: https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png 3599 | [edge-image]: https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png 3600 | -------------------------------------------------------------------------------- /TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Quick Links 2 | 3 | **[Section](#section)** 4 | 5 | 1. [_.sortYourMethodAlphabetically](#_sortYourMethodAlphabetically) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ### _.sortYourMethodAlphabetically 16 | Explains or copy/paste lodash/underscore description. 17 | 18 | ```js 19 | // Lodash 20 | var result = _.sortYourMethodAlphabetically('abc', ...) 21 | console.log(result) 22 | // output: true 23 | 24 | // Native 25 | var result = 'abc'.sortYourMethodAlphabetically(...) 26 | console.log(result) 27 | // output: true 28 | ``` 29 | 30 | #### Browser Support for `Array.prototype.sortYourMethodAlphabetically()` 31 | 32 | ![Chrome][chrome-image] | ![Edge][edge-image] | ![Firefox][firefox-image] | ![IE][ie-image] | ![Opera][opera-image] | ![Safari][safari-image] 33 | :-: | :-: | :-: | :-: | :-: | :-: | 34 | 41.0 ✔ | 15.0 ✔ | 17.0 ✔ | ✖ | 10.5 ✔ | 9.0 ✔ | 35 | 36 | **[⬆ back to top](#quick-links)** 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | > !------ DO NOT COPY THESE LINES BELOW -------------! 46 | > For Browser support, visit [MDN web docs](https://developer.mozilla.org/en-US/), search for a term there and see the bottom section of the page. 47 | 48 | [chrome-image]: https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png 49 | [firefox-image]: https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png 50 | [ie-image]: https://raw.githubusercontent.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png 51 | [opera-image]: https://raw.githubusercontent.com/alrra/browser-logos/master/src/opera/opera_48x48.png 52 | [safari-image]: https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png 53 | [edge-image]: 54 | https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png 55 | -------------------------------------------------------------------------------- /configuring.md: -------------------------------------------------------------------------------- 1 | ## Configuring the ESLint Plugin 2 | 3 | 4 | ### If you haven't already, add ESLint to your project 5 | 6 | ```sh 7 | npm install --save-dev eslint 8 | ``` 9 | 10 | ### Add the plugin 11 | 12 | ```sh 13 | npm install --save-dev eslint-plugin-you-dont-need-lodash-underscore 14 | ``` 15 | 16 | ### Add the plugin to your .eslintrc.js file 17 | 18 | ```js 19 | "plugins": ["you-dont-need-lodash-underscore"], 20 | ``` 21 | 22 | If you already have plugins installed, just add to the array. 23 | 24 | ```js 25 | "plugins": ["react", "you-dont-need-lodash-underscore"], 26 | ``` 27 | 28 | ### Now configure your plugin. 29 | 30 | You can enable or disable individual rules. 31 | 32 | ```js 33 | "rules": { 34 | "you-dont-need-lodash-underscore/for-each": "error", 35 | "you-dont-need-lodash-underscore/concat": "warn", 36 | "you-dont-need-lodash-underscore/map": "off", 37 | ... 38 | } 39 | ``` 40 | 41 | To save the trouble of configuring each rule individually, you can start by extending one of the 42 | default configurations, and then override individual rules as desired. 43 | 44 | ```js 45 | "extends" : ["plugin:you-dont-need-lodash-underscore/compatible"], 46 | ``` 47 | 48 | The following options are available: 49 | 50 | - you-dont-need-lodash-underscore:all-warn (all rules set to warn) 51 | - you-dont-need-lodash-underscore:all (all rules set to error) 52 | - you-dont-need-lodash-underscore:compatible-warn (rules in which the native implementation is perfectly compatible with the _ one are set to warn, the rest are disabled) 53 | - you-dont-need-lodash-underscore:compatible (rules in which the native implementation is perfectly compatible with _ one are set to error, the rest are set to warn) 54 | 55 | 56 | For more information, see the [ESLint documentation](http://eslint.org/docs/user-guide/configuring). 57 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/you-dont-need/You-Dont-Need-Lodash-Underscore/5ea02d3a0b151ea3e49ab14233c739cba657acaa/docs/.nojekyll -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | [remoteMarkdownUrl](https://raw.githubusercontent.com/you-dont-need/You-Dont-Need-Lodash-Underscore/master/README.md) -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | You don’t (may not) need Lodash/Underscore 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const kebabCase = require('kebab-case'); 3 | const rules = require('./lib/rules/rules.json') 4 | 5 | module.exports.rules = require('./lib/rules/all'); 6 | 7 | const all = Object.keys(rules); 8 | const compatible = Object.keys(rules).filter(rule => rules[rule].compatible); 9 | const incompatible = Object.keys(rules).filter(rule => !rules[rule].compatible); 10 | 11 | const WARN = 1; 12 | const ERROR = 2; 13 | 14 | const configure = (list, level) => ( 15 | list.reduce((ret, rule) => (Object.assign({}, ret, 16 | { ['you-dont-need-lodash-underscore/' + (rules[rule].ruleName || kebabCase(rule))]: level })), {}) 17 | ) 18 | 19 | module.exports.configs = { 20 | 'all-warn': { 21 | plugins: [ 22 | 'you-dont-need-lodash-underscore' 23 | ], 24 | rules: configure(all, WARN) 25 | }, 26 | 27 | 'all': { 28 | plugins: [ 29 | 'you-dont-need-lodash-underscore' 30 | ], 31 | rules: configure(all, ERROR) 32 | }, 33 | 34 | 'compatible-warn': { 35 | plugins: [ 36 | 'you-dont-need-lodash-underscore' 37 | ], 38 | rules: configure(compatible, WARN) 39 | }, 40 | 41 | 'compatible': { 42 | plugins: [ 43 | 'you-dont-need-lodash-underscore' 44 | ], 45 | rules: Object.assign( 46 | configure(compatible, ERROR), 47 | configure(incompatible, WARN) 48 | ) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/rules/all.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const kebabCase = require('kebab-case'); 3 | const rules = require('./rules'); 4 | 5 | const forbiddenLibs = ['lodash', 'lodash/fp', 'lodash-es']; 6 | 7 | function getAssignmentLeftHandSide(node) { 8 | // For VariableDeclarator nodes, the left hand side is called `id` 9 | // The `x` on `var x = 3; 10 | if (node.type === 'VariableDeclarator') { 11 | return node.id; 12 | } 13 | // For AssignmentExpression nodes, the left hand side is called `left` 14 | // The `x` on `x = 3; 15 | if (node.type === 'AssignmentExpression') { 16 | return node.left; 17 | } 18 | return null; 19 | } 20 | 21 | for (const rule in rules) { 22 | const alternative = rules[rule].alternative; 23 | const ruleName = rules[rule].ruleName || kebabCase(rule); 24 | const forbiddenImports = { 25 | [`lodash/${rule}`]: 1, 26 | [`lodash/fp/${rule}`]: 1, 27 | [`lodash-es/${rule}`]: 1, 28 | [`lodash.${rule.toLowerCase()}`]: 1, 29 | }; 30 | 31 | module.exports[ruleName] = { 32 | create(context) { 33 | return { 34 | CallExpression(node) { 35 | const callee = node.callee; 36 | const objectName = callee.name || (callee.object && callee.object.name) || (callee.object && callee.object.callee && callee.object.callee.name); 37 | 38 | if (objectName === 'require' && node.arguments.length === 1) { 39 | const requiredModuleName = node.arguments[0].value; 40 | const { parent } = node; 41 | if (forbiddenLibs.includes(requiredModuleName)) { 42 | const leftHandSide = getAssignmentLeftHandSide(parent); 43 | // ex: const { indexOf } = require('lodash'); 44 | // ex: ({ indexOf } = require('lodash')); 45 | if (leftHandSide && leftHandSide.type === 'ObjectPattern') { 46 | leftHandSide.properties.forEach(property => { 47 | if (property.key.name === rule) { 48 | context.report({ 49 | node, 50 | message: `{ ${rule} } = require('${requiredModuleName}') detected. Consider using the native ${alternative}` 51 | }); 52 | } 53 | }); 54 | } 55 | } else if (forbiddenImports.hasOwnProperty(requiredModuleName)) { 56 | // ex: const indexOf = require('lodash.indexof'); 57 | // ex: const indexOf = require('lodash/indexOf'); 58 | context.report({ 59 | node, 60 | message: `require('${requiredModuleName}') detected. Consider using the native ${alternative}` 61 | }); 62 | } 63 | } else if ((objectName === '_' || objectName === 'lodash' || objectName === 'underscore') && callee.property && callee.property.name === rule) { 64 | context.report({ 65 | node, 66 | message: `Consider using the native ${alternative}` 67 | }); 68 | } 69 | }, 70 | ImportDeclaration(node) { 71 | if (forbiddenLibs.includes(node.source.value)) { 72 | // ex: import { indexOf } from 'lodash'; 73 | // ex: import { indexOf as x } from 'lodash'; 74 | node.specifiers.forEach(specifier => { 75 | if (specifier.type === 'ImportSpecifier' && specifier.imported.name === rule) { 76 | context.report({ 77 | node, 78 | message: `Import { ${rule} } from '${node.source.value}' detected. Consider using the native ${alternative}` 79 | }); 80 | } 81 | }); 82 | } else if (forbiddenImports.hasOwnProperty(node.source.value)) { 83 | // ex: import indexOf from 'lodash/indexOf'; 84 | // ex: import indexOf from 'lodash.indexof'; 85 | context.report({ 86 | node, 87 | message: `Import from '${node.source.value}' detected. Consider using the native ${alternative}` 88 | }); 89 | } 90 | } 91 | }; 92 | } 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /lib/rules/rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "concat": { 3 | "compatible": true, 4 | "alternative": "Array.prototype.concat()" 5 | }, 6 | "drop": { 7 | "compatible": true, 8 | "alternative": "Array.prototype.slice()" 9 | }, 10 | "dropRight": { 11 | "compatible": true, 12 | "alternative": "Array.prototype.slice()" 13 | }, 14 | "indexOf": { 15 | "compatible": true, 16 | "alternative": "Array.prototype.indexOf()" 17 | }, 18 | "join": { 19 | "compatible": true, 20 | "alternative": "Array.prototype.join()" 21 | }, 22 | "last": { 23 | "compatible": true, 24 | "alternative": "Array.prototype.at(-1) or Array.prototype.slice()", 25 | "ES13": true 26 | }, 27 | "lastIndexOf": { 28 | "compatible": true, 29 | "alternative": "Array.prototype.lastIndexOf()" 30 | }, 31 | "reverse": { 32 | "compatible": true, 33 | "alternative": "Array.prototype.reverse()" 34 | }, 35 | "fill": { 36 | "compatible": true, 37 | "alternative": "Array.prototype.fill()", 38 | "ES6": true 39 | }, 40 | "find": { 41 | "compatible": false, 42 | "alternative": "Array.prototype.find()", 43 | "ES6": true 44 | }, 45 | "detect": { 46 | "compatible": true, 47 | "alternative": "Array.prototype.find()", 48 | "ES6": true 49 | }, 50 | "first": { 51 | "compatible": true, 52 | "alternative": "Array.prototype.at(0) or Array.prototype.slice()", 53 | "ES13": true 54 | }, 55 | "findIndex": { 56 | "compatible": false, 57 | "alternative": "Array.prototype.findIndex()", 58 | "ES6": true 59 | }, 60 | "isArray": { 61 | "compatible": true, 62 | "alternative": "Array.isArray()" 63 | }, 64 | "each": { 65 | "compatible": false, 66 | "alternative": "Array.prototype.forEach() or Object.entries().forEach()" 67 | }, 68 | "forEach": { 69 | "compatible": false, 70 | "alternative": "Array.prototype.forEach() or Object.entries().forEach()" 71 | }, 72 | "every": { 73 | "compatible": false, 74 | "alternative": "Array.prototype.every()" 75 | }, 76 | "all": { 77 | "compatible": false, 78 | "alternative": "Array.prototype.every()" 79 | }, 80 | "filter": { 81 | "compatible": false, 82 | "alternative": "Array.prototype.filter()" 83 | }, 84 | "select": { 85 | "compatible": false, 86 | "alternative": "Array.prototype.filter()" 87 | }, 88 | "map": { 89 | "compatible": false, 90 | "alternative": "Array.prototype.map()" 91 | }, 92 | "collect": { 93 | "compatible": false, 94 | "alternative": "Array.prototype.map()" 95 | }, 96 | "reduce": { 97 | "compatible": false, 98 | "alternative": "Array.prototype.reduce()" 99 | }, 100 | "inject": { 101 | "compatible": false, 102 | "alternative": "Array.prototype.reduce()" 103 | }, 104 | "foldl": { 105 | "compatible": false, 106 | "alternative": "Array.prototype.reduce()" 107 | }, 108 | "reduceRight": { 109 | "compatible": false, 110 | "alternative": "Array.prototype.reduceRight()" 111 | }, 112 | "foldr": { 113 | "compatible": false, 114 | "alternative": "Array.prototype.reduceRight()" 115 | }, 116 | "size": { 117 | "compatible": false, 118 | "alternative": "Array.prototype.length" 119 | }, 120 | "some": { 121 | "compatible": false, 122 | "alternative": "Array.prototype.some()" 123 | }, 124 | "any": { 125 | "compatible": false, 126 | "alternative": "Array.prototype.some()" 127 | }, 128 | "includes": { 129 | "compatible": false, 130 | "alternative": "Array.prototype.includes()", 131 | "ES6": true 132 | }, 133 | "contains": { 134 | "compatible": false, 135 | "alternative": "Array.prototype.includes()", 136 | "ES6": true 137 | }, 138 | "slice": { 139 | "compatible": true, 140 | "alternative": "Array.prototype.slice()" 141 | }, 142 | "takeRight": { 143 | "compatible": false, 144 | "alternative": "Array.prototype.slice()" 145 | }, 146 | "bind": { 147 | "ruleName": "bind", 148 | "compatible": true, 149 | "alternative": "Function.prototype.bind()" 150 | }, 151 | "isFinite": { 152 | "compatible": true, 153 | "alternative": "Number.isFinite()" 154 | }, 155 | "isInteger": { 156 | "ruleName": "is-integer", 157 | "compatible": true, 158 | "alternative": "Number.isInteger()", 159 | "ES6": true 160 | }, 161 | "isNaN": { 162 | "ruleName": "is-nan", 163 | "compatible": true, 164 | "alternative": "Number.isNaN()", 165 | "ES6": true 166 | }, 167 | "isNil": { 168 | "ruleName": "is-nil", 169 | "compatible": true, 170 | "alternative": "value === null || value === undefined" 171 | }, 172 | "isNull": { 173 | "ruleName": "is-null", 174 | "compatible": true, 175 | "alternative": "value === null" 176 | }, 177 | "isUndefined": { 178 | "ruleName": "is-undefined", 179 | "compatible": true, 180 | "alternative": "value === undefined" 181 | }, 182 | "keys": { 183 | "compatible": true, 184 | "alternative": "Object.keys()" 185 | }, 186 | "extendOwn": { 187 | "compatible": true, 188 | "alternative": "Object.assign()", 189 | "ES6": true 190 | }, 191 | "assign": { 192 | "compatible": true, 193 | "alternative": "Object.assign()", 194 | "ES6": true 195 | }, 196 | "values": { 197 | "compatible": true, 198 | "alternative": "Object.values()", 199 | "ES6": true 200 | }, 201 | "entries": { 202 | "compatible": true, 203 | "alternative": "Object.entries()", 204 | "ES6": true 205 | }, 206 | "toPairs": { 207 | "compatible": true, 208 | "alternative": "Object.entries()", 209 | "ES6": true 210 | }, 211 | "pairs": { 212 | "compatible": true, 213 | "alternative": "Object.entries()", 214 | "ES6": true 215 | }, 216 | "get": { 217 | "compatible": false, 218 | "alternative": "optional chaining to get nested values and nullish coalescing operator for fallback values", 219 | "ES11": true 220 | }, 221 | "split": { 222 | "compatible": true, 223 | "alternative": "String.prototype.split()" 224 | }, 225 | "startsWith": { 226 | "ruleName": "starts-with", 227 | "compatible": true, 228 | "alternative": "String.prototype.startsWith()" 229 | }, 230 | "endsWith": { 231 | "ruleName": "ends-with", 232 | "compatible": true, 233 | "alternative": "String.prototype.endsWith()" 234 | }, 235 | "toLower": { 236 | "compatible": true, 237 | "alternative": "String.prototype.toLowerCase()" 238 | }, 239 | "toUpper": { 240 | "compatible": true, 241 | "alternative": "String.prototype.toUpperCase()" 242 | }, 243 | "trim": { 244 | "compatible": true, 245 | "alternative": "String.prototype.trim()" 246 | }, 247 | "padStart": { 248 | "compatible": true, 249 | "alternative": "String.prototype.padStart()", 250 | "ES6": true 251 | }, 252 | "padEnd": { 253 | "compatible": true, 254 | "alternative": "String.prototype.padEnd()", 255 | "ES6": true 256 | }, 257 | "repeat": { 258 | "compatible": true, 259 | "alternative": "String.prototype.repeat()", 260 | "ES6": true 261 | }, 262 | "uniq": { 263 | "compatible": true, 264 | "alternative": "[... new Set(arr)]", 265 | "ES6": true 266 | }, 267 | "replace": { 268 | "compatible": true, 269 | "alternative": "String.prototype.replace()" 270 | }, 271 | "omit": { 272 | "compatible": true, 273 | "alternative": "{a, b, c, ...notOmittedValues}", 274 | "ES6": true 275 | }, 276 | "flatten": { 277 | "compatible": true, 278 | "alternative": "Array.prototype.reduce((a,b) => a.concat(b), [])" 279 | }, 280 | "throttle": { 281 | "compatible": true, 282 | "alternative": "Example of native implementation: https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_throttle" 283 | }, 284 | "isString": { 285 | "compatible": true, 286 | "alternative": "str != null && typeof str.valueOf() === \"string\"" 287 | }, 288 | "castArray": { 289 | "compatible": true, 290 | "alternative": "Array.isArray(arr) ? arr : [arr]" 291 | }, 292 | "cloneDeep": { 293 | "compatible": true, 294 | "alternative": "structuredClone()" 295 | }, 296 | "isFunction": { 297 | "compatible": true, 298 | "alternative": "typeof func === \"function\"" 299 | }, 300 | "unionBy": { 301 | "compatible": false, 302 | "alternative": "Example of native implementation: https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_unionBy", 303 | "ES10": true 304 | }, 305 | "capitalize": { 306 | "compatible": true, 307 | "alternative": "string.charAt(0).toUpperCase() + string.slice(1).toLowerCase()" 308 | }, 309 | "isDate": { 310 | "compatible": true, 311 | "alternative": "String.prototype.toString.call()" 312 | }, 313 | "defaults": { 314 | "compatible": true, 315 | "alternative": "Object.assign({}, defaultValues, newValues)", 316 | "ES6": true 317 | }, 318 | "isArrayBuffer": { 319 | "compatible": false, 320 | "alternative": "value instanceof ArrayBuffer" 321 | }, 322 | "head": { 323 | "compatible": true, 324 | "alternative": "Array.prototype.at(0)", 325 | "ES13": true 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-you-dont-need-lodash-underscore", 3 | "version": "6.14.0", 4 | "description": "Check methods you can use natively without lodash/underscore", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore.git" 8 | }, 9 | "keywords": [ 10 | "underscore", 11 | "lodash", 12 | "eslint", 13 | "eslintplugin", 14 | "eslint-plugin", 15 | "native" 16 | ], 17 | "author": "Robert Chang ", 18 | "contributors": [ 19 | "Patrick McElhaney (http://patrickmcelhaney.com)", 20 | "Steve Mao (https://github.com/stevemao)" 21 | ], 22 | "main": "index.js", 23 | "scripts": { 24 | "coveralls": "cat ./coverage/lcov.info | coveralls", 25 | "test": "istanbul cover tests", 26 | "test:unit": "mocha tests/unit" 27 | }, 28 | "dependencies": { 29 | "kebab-case": "^1.0.0" 30 | }, 31 | "devDependencies": { 32 | "coveralls": "^3.1.1", 33 | "eslint": "^8.48.0", 34 | "istanbul": "^0.4.4", 35 | "lodash": "^4.17.4", 36 | "mocha": "^10.2.0" 37 | }, 38 | "engines": { 39 | "node": ">=4.0" 40 | }, 41 | "license": "MIT", 42 | "bugs": { 43 | "url": "https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore/issues" 44 | }, 45 | "homepage": "https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore.git" 46 | } 47 | -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | require('./lib/rules/all.js'); 2 | 3 | const assert = require('assert'); 4 | const plugin = require('../'); 5 | 6 | assert.equal(plugin.configs['all-warn'].rules['you-dont-need-lodash-underscore/contains'], 1); 7 | assert.equal(plugin.configs['all-warn'].rules['you-dont-need-lodash-underscore/trim'], 1); 8 | assert.equal(plugin.configs.all.rules['you-dont-need-lodash-underscore/every'], 2); 9 | assert.equal(plugin.configs.all.rules['you-dont-need-lodash-underscore/keys'], 2); 10 | assert.equal(plugin.configs['compatible-warn'].rules['you-dont-need-lodash-underscore/each'], undefined); 11 | assert.equal(plugin.configs['compatible-warn'].rules['you-dont-need-lodash-underscore/last-index-of'], 1); 12 | assert.equal(plugin.configs.compatible.rules['you-dont-need-lodash-underscore/for-each'], 1); 13 | assert.equal(plugin.configs.compatible.rules['you-dont-need-lodash-underscore/is-nan'], 2); 14 | -------------------------------------------------------------------------------- /tests/lib/rules/all.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const RuleTester = require('eslint').RuleTester; 3 | const assert = require('assert'); 4 | const rules = require('../../../lib/rules/all'); 5 | const allRules = require('../../../lib/rules/rules'); 6 | 7 | const ruleTester = new RuleTester({ 8 | parserOptions: { ecmaVersion: 2018, sourceType: "module" } 9 | }); 10 | 11 | // Only a couple of smoke tests because otherwise it would get very redundant 12 | 13 | ruleTester.run('_.concat', rules.concat, { 14 | valid: [ 15 | 'array.concat(2, [3], [[4]])' 16 | ], 17 | invalid: [{ 18 | code: '_.concat(array, 2, [3], [[4]])', 19 | errors: ['Consider using the native Array.prototype.concat()'] 20 | }] 21 | }); 22 | 23 | ruleTester.run('lodash.keys', rules.keys, { 24 | valid: [ 25 | 'Object.keys({one: 1, two: 2, three: 3})' 26 | ], 27 | invalid: [{ 28 | code: 'lodash.keys({one: 1, two: 2, three: 3})', 29 | errors: ['Consider using the native Object.keys()'] 30 | }] 31 | }); 32 | 33 | ruleTester.run(`Import lodash.isnan`, rules['is-nan'], { 34 | valid: [`{ x: require('lodash') }`], 35 | invalid: [{ 36 | code: `import isNaN from 'lodash/isNaN';`, 37 | errors: [`Import from 'lodash/isNaN' detected. Consider using the native Number.isNaN()`] 38 | }, { 39 | code: `import isNaN from 'lodash.isnan';`, 40 | errors: [`Import from 'lodash.isnan' detected. Consider using the native Number.isNaN()`] 41 | }, { 42 | code: `import { isNaN as x } from 'lodash';`, 43 | errors: [`Import { isNaN } from 'lodash' detected. Consider using the native Number.isNaN()`] 44 | }, { 45 | code: `const { isNaN: x } = require('lodash');`, 46 | errors: [`{ isNaN } = require('lodash') detected. Consider using the native Number.isNaN()`] 47 | }, { 48 | code: `({ isNaN: x } = require('lodash'));`, 49 | errors: [`{ isNaN } = require('lodash') detected. Consider using the native Number.isNaN()`] 50 | }, { 51 | code: `require('lodash/isNaN');`, 52 | errors: [`require('lodash/isNaN') detected. Consider using the native Number.isNaN()`] 53 | }, { 54 | code: `require('lodash.isnan');`, 55 | errors: [`require('lodash.isnan') detected. Consider using the native Number.isNaN()`] 56 | }] 57 | }); 58 | 59 | ruleTester.run(`Import { isNaN } from lodash-es`, rules['is-nan'], { 60 | valid: [`{ x: require('lodash-es') }`], 61 | invalid: [{ 62 | code: `import { isNaN } from 'lodash-es';`, 63 | errors: [`Import { isNaN } from 'lodash-es' detected. Consider using the native Number.isNaN()`] 64 | }, 65 | { 66 | code: `import isNaN from 'lodash-es/isNaN';`, 67 | errors: [`Import from 'lodash-es/isNaN' detected. Consider using the native Number.isNaN()`] 68 | }, { 69 | code: `import { isNaN as x } from 'lodash-es';`, 70 | errors: [`Import { isNaN } from 'lodash-es' detected. Consider using the native Number.isNaN()`] 71 | }, { 72 | code: `const { isNaN: x } = require('lodash-es');`, 73 | errors: [`{ isNaN } = require('lodash-es') detected. Consider using the native Number.isNaN()`] 74 | }, { 75 | code: `({ isNaN: x } = require('lodash-es'));`, 76 | errors: [`{ isNaN } = require('lodash-es') detected. Consider using the native Number.isNaN()`] 77 | }, { 78 | code: `require('lodash-es/isNaN');`, 79 | errors: [`require('lodash-es/isNaN') detected. Consider using the native Number.isNaN()`] 80 | }] 81 | }); 82 | 83 | ruleTester.run('underscore.forEach', rules['for-each'], { 84 | valid: [ 85 | '[0, 1].forEach()', 86 | "Object.entries({'one':1,'two':2}).forEach()" 87 | ], 88 | invalid: [{ 89 | code: 'underscore.forEach()', 90 | errors: ['Consider using the native Array.prototype.forEach() or Object.entries().forEach()'] 91 | }] 92 | }); 93 | 94 | ruleTester.run('underscore.isNaN', rules['is-nan'], { 95 | valid: [ 96 | 'Number.isNaN(NaN);' 97 | ], 98 | invalid: [{ 99 | code: 'underscore.isNaN(NaN)', 100 | errors: ['Consider using the native Number.isNaN()'] 101 | }] 102 | }); 103 | 104 | ruleTester.run('_.first', rules['first'], { 105 | valid: [ 106 | '[0, 1, 3][0]', 107 | '[0, 1, 3].at(0)', 108 | '[0, 1, 3].slice(0, 2)' 109 | ], 110 | invalid: [{ 111 | code: '_.first([0, 1, 3])', 112 | errors: ['Consider using the native Array.prototype.at(0) or Array.prototype.slice()'] 113 | }, { 114 | code: '_.first([0, 1, 3], 2)', 115 | errors: ['Consider using the native Array.prototype.at(0) or Array.prototype.slice()'] 116 | }] 117 | }); 118 | 119 | ruleTester.run('_.last', rules['last'], { 120 | valid: [ 121 | 'var numbers = [0, 1, 3]; numbers[numbers.length - 1]', 122 | '[0, 1, 3].at(-1)', 123 | '[0, 1, 3].slice(-2)' 124 | ], 125 | invalid: [{ 126 | code: '_.last([0, 1, 3])', 127 | errors: ['Consider using the native Array.prototype.at(-1) or Array.prototype.slice()'] 128 | }, { 129 | code: '_.last([0, 1, 3], 2)', 130 | errors: ['Consider using the native Array.prototype.at(-1) or Array.prototype.slice()'] 131 | }] 132 | }); 133 | 134 | ruleTester.run('_', rules.concat, { 135 | valid: [ 136 | '_(2, [3], [[4]])' 137 | ], 138 | invalid: [] 139 | }); 140 | 141 | ruleTester.run('_.isUndefined', rules['is-undefined'], { 142 | valid: [ 143 | '2 === undefined' 144 | ], 145 | invalid: [{ 146 | code: '_.isUndefined(2)', 147 | errors: ['Consider using the native value === undefined'] 148 | },{ 149 | code: '_(2).isUndefined()', 150 | errors: ['Consider using the native value === undefined'] 151 | 152 | }] 153 | }); 154 | 155 | /*This is to make sure that You-Dont-Need-Lodash can handle the 156 | evaluation of nested functions that had caused an error noted in the comments of 157 | Pull Request #219*/ 158 | ruleTester.run('Nested functions', rules['is-undefined'], { 159 | valid: [ 160 | `function myNestedFunction(firstInput) { 161 | return (secondInput) => { 162 | return firstInput + secondInput 163 | } 164 | } 165 | myNestedFunction(2)(2)` 166 | ], 167 | invalid: [] 168 | }); 169 | 170 | /*Test for new flatten rule*/ 171 | ruleTester.run('_.flatten', rules['flatten'], { 172 | valid: [ 173 | `[1,2,[3,4]].reduce((a,b) => a.concat(b), [])`, 174 | `[1,2,[3,4]].flat()`, 175 | `[1,2,[3,4]].flatMap(a => a)` 176 | ], 177 | invalid: [{ 178 | code: `_.flatten([1,2,[3,4]])`, 179 | errors: [`Consider using the native Array.prototype.reduce((a,b) => a.concat(b), [])`] 180 | },{ 181 | code: `_([1,2,[3,4]]).flatten()`, 182 | errors: [`Consider using the native Array.prototype.reduce((a,b) => a.concat(b), [])`] 183 | }] 184 | }); 185 | 186 | ruleTester.run('_.isUndefined', rules['is-undefined'], { 187 | valid: [ 188 | '2 === undefined' 189 | ], 190 | invalid: [{ 191 | code: '_.isUndefined(2)', 192 | errors: ['Consider using the native value === undefined'] 193 | },{ 194 | code: '_(2).isUndefined()', 195 | errors: ['Consider using the native value === undefined'] 196 | 197 | }] 198 | }); 199 | 200 | ruleTester.run('_.startsWith', rules['starts-with'], { 201 | valid: [ 202 | '"abc".startsWith("a")', 203 | '"abc".startsWith("b", 1)' 204 | ], 205 | invalid: [{ 206 | code: '_.startsWith("abc", "a")', 207 | errors: ['Consider using the native String.prototype.startsWith()'] 208 | },{ 209 | code: '_.startsWith("abc", "b", 1)', 210 | errors: ['Consider using the native String.prototype.startsWith()'] 211 | 212 | }] 213 | }); 214 | 215 | ruleTester.run('_.endsWith', rules['ends-with'], { 216 | valid: [ 217 | '"abc".endsWith("c")', 218 | '"abc".endsWith("b", 1)' 219 | ], 220 | invalid: [{ 221 | code: '_.endsWith("abc", "c")', 222 | errors: ['Consider using the native String.prototype.endsWith()'] 223 | },{ 224 | code: '_.endsWith("abc", "b", 1)', 225 | errors: ['Consider using the native String.prototype.endsWith()'] 226 | 227 | }] 228 | }); 229 | 230 | ruleTester.run('_.head', rules['head'], { 231 | valid: [ 232 | '[0, 1, 3].at(0)', 233 | ], 234 | invalid: [{ 235 | code: '_.head([0, 1, 3])', 236 | errors: ['Consider using the native Array.prototype.at(0)'] 237 | }] 238 | }); 239 | 240 | -------------------------------------------------------------------------------- /tests/unit/all.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const _ = require('lodash'); 4 | 5 | describe('code snippet example', () => { 6 | 7 | it('compact', () => { 8 | assert.deepEqual( 9 | _.compact([0, 1, false, 2, '', 3]), 10 | [0, 1, false, 2, '', 3].filter(v => v) 11 | ) 12 | }) 13 | 14 | it('concat', () => { 15 | const lodashArray = [1] 16 | const lodashResult = _.concat(lodashArray, 2, [3], [[4]]) 17 | 18 | const nativeArray = [1] 19 | const nativeResult = nativeArray.concat(2, [3], [[4]]) 20 | 21 | assert.deepEqual(lodashResult, nativeResult) 22 | }) 23 | 24 | it('invert', () => { 25 | var object = { 'a': 1, 'b': '2', 'c': 3 }; 26 | function invert(object) { 27 | var obj = {}; 28 | for (var key in object) { 29 | if (object.hasOwnProperty(key)) { 30 | obj[object[key]] = key; 31 | } 32 | } 33 | return obj; 34 | } 35 | assert.deepEqual( 36 | _.invert(object), 37 | invert(object) 38 | ) 39 | }) 40 | 41 | it('mapKeys', () => { 42 | var object = { 'a': 1, 'b': 2 }; 43 | function mapKeys(object, cb) { 44 | var obj = {}; 45 | for (var key in object) { 46 | if (object.hasOwnProperty(key)) { 47 | var newKey = cb(object[key], key, object); 48 | obj[newKey] = object[key]; 49 | } 50 | } 51 | return obj; 52 | } 53 | assert.deepEqual( 54 | _.mapKeys(object, function (value, key) { 55 | return key + value; 56 | }), 57 | mapKeys(object, function (value, key) { 58 | return key + value; 59 | }) 60 | ) 61 | }) 62 | 63 | it('pick', () => { 64 | var object = { 'a': 1, 'b': '2', 'c': 3 }; 65 | function pick(object, keys) { 66 | return keys.reduce((obj, key) => { 67 | if (object && object.hasOwnProperty(key)) { 68 | obj[key] = object[key]; 69 | } 70 | return obj; 71 | }, {}); 72 | } 73 | assert.deepEqual( 74 | _.pick(object, ['a', 'c', 'x']), 75 | pick(object, ['a', 'c', 'x']) 76 | ) 77 | }) 78 | 79 | it('pickBy', () => { 80 | var object = { 'a': 1, 'b': null, 'c': 3, 'd': false, 'e': undefined, 'f': '', 'g': 0 }; 81 | function pickBy(object) { 82 | const obj = {}; 83 | for (const key in object) { 84 | if (object[key]) { 85 | obj[key] = object[key]; 86 | } 87 | } 88 | return obj; 89 | } 90 | assert.deepEqual( 91 | _.pickBy(object), 92 | pickBy(object) 93 | ) 94 | }) 95 | 96 | describe('fill', () => { 97 | it("_.fill(array, 'a')", () => { 98 | var array = [1, 2, 3] 99 | assert.deepEqual( 100 | _.fill(array, 'a'), 101 | array.fill('a') 102 | ) 103 | }) 104 | it("_.fill(Array(3), 2)", () => { 105 | assert.deepEqual( 106 | _.fill(Array(3), 2), 107 | Array(3).fill(2) 108 | ) 109 | }) 110 | 111 | it("_.fill([4, 6, 8, 10], '*', 1, 3)", () => { 112 | assert.deepEqual( 113 | _.fill([4, 6, 8, 10], '*', 1, 3), 114 | [4, 6, 8, 10].fill('*', 1, 3) 115 | ) 116 | }) 117 | }) 118 | describe('chunk', () => { 119 | const chunk = (input, size) => { 120 | return input.reduce((arr, item, idx) => { 121 | return idx % size === 0 122 | ? [...arr, [item]] 123 | : [...arr.slice(0, -1), [...arr.slice(-1)[0], item]]; 124 | }, []); 125 | }; 126 | it("_.chunk(['a', 'b', 'c', 'd'], 2);", () => { 127 | assert.deepEqual( 128 | _.chunk(['a', 'b', 'c', 'd'], 2), 129 | chunk(['a', 'b', 'c', 'd'], 2) 130 | ) 131 | }) 132 | it("_.chunk(['a', 'b', 'c', 'd'], 3);", () => { 133 | assert.deepEqual( 134 | _.chunk(['a', 'b', 'c', 'd'], 3), 135 | chunk(['a', 'b', 'c', 'd'], 3) 136 | ) 137 | }) 138 | }) 139 | describe('times', () => { 140 | const times = (n, fn = (_, x) => x) => { 141 | return Array.from(Array(n), fn) 142 | }; 143 | it("_.times(10);", () => { 144 | assert.deepEqual( 145 | _.times(10), 146 | times(10) 147 | ) 148 | }) 149 | it("_.times(10, x => x + 1);", () => { 150 | assert.deepEqual( 151 | _.times(10, x => x + 1), 152 | times(10, (_, x) => x + 1) 153 | ) 154 | }) 155 | }) 156 | 157 | describe('assign', () => { 158 | function Foo() { 159 | this.c = 3; 160 | } 161 | function Bar() { 162 | this.e = 5; 163 | } 164 | Foo.prototype.d = 4; 165 | Bar.prototype.f = 6; 166 | const assign = (target, ...sources) => Object.assign(target, ...sources); 167 | it("_.assign({}, new Foo, new Bar);", () => { 168 | assert.deepEqual( 169 | _.assign({}, new Foo, new Bar), 170 | assign({}, new Foo, new Bar) 171 | ) 172 | }) 173 | it("_.assign(new Foo, new Bar);", () => { 174 | assert.deepEqual( 175 | _.assign(new Foo, new Bar), 176 | assign(new Foo, new Bar) 177 | ) 178 | }) 179 | }) 180 | describe('extend', () => { 181 | function Foo() { 182 | this.c = 3; 183 | } 184 | function Bar() { 185 | this.e = 5; 186 | } 187 | Foo.prototype.d = 4; 188 | Bar.prototype.f = 6; 189 | 190 | const extend = (target, ...sources) => { 191 | const length = sources.length; 192 | 193 | if (length < 1 || target == null) return target; 194 | for (let i = 0; i < length; i++) { 195 | const source = sources[i]; 196 | 197 | for (const key in source) { 198 | target[key] = source[key]; 199 | } 200 | } 201 | return target; 202 | }; 203 | 204 | it("_.extend({}, new Foo, new Bar);", () => { 205 | assert.deepEqual( 206 | _.extend({}, new Foo, new Bar), 207 | extend({}, new Foo, new Bar) 208 | ) 209 | }) 210 | it("_.extend(new Foo, new Bar);", () => { 211 | assert.deepEqual( 212 | _.extend(new Foo, new Bar), 213 | extend(new Foo, new Bar) 214 | ) 215 | }) 216 | }) 217 | describe('isDate', () => { 218 | const isDate = (obj) => { 219 | return (obj ? Object.prototype.toString.call(obj) === "[object Date]" : false); 220 | }; 221 | it ('_.Date(null)', () => { 222 | assert.equal( 223 | _.isDate(null), 224 | isDate(null) 225 | ) 226 | }) 227 | it ("_.Date('Mon April 23 2012')", () => { 228 | assert.equal( 229 | _.isDate('Mon April 23 2012'), 230 | isDate('Mon April 23 2012') 231 | ) 232 | }) 233 | it ('_.Date(new Date)', () => { 234 | assert.equal( 235 | _.isDate(new Date), 236 | isDate(new Date) 237 | ) 238 | }) 239 | }) 240 | describe('isEmpty', () => { 241 | const isEmpty = (obj) => { 242 | return (obj ? [Object, Array].includes(obj.constructor) && !Object.entries(obj).length : true); 243 | }; 244 | it ('_.isEmpty(null)', () => { 245 | assert.equal( 246 | _.isEmpty(null), 247 | isEmpty(null) 248 | ) 249 | }) 250 | it ("_.isEmpty('')", () => { 251 | assert.equal( 252 | _.isEmpty(''), 253 | isEmpty('') 254 | ) 255 | }) 256 | it ("_.isEmpty({})", () => { 257 | assert.equal( 258 | _.isEmpty({}), 259 | isEmpty({}) 260 | ) 261 | }) 262 | it ("_.isEmpty([])", () => { 263 | assert.equal( 264 | _.isEmpty([]), 265 | isEmpty([]) 266 | ) 267 | }) 268 | it ("_.isEmpty({a: '1'})", () => { 269 | assert.equal( 270 | _.isEmpty({a: '1'}), 271 | isEmpty({a: '1'}) 272 | ) 273 | }) 274 | }) 275 | describe('isInteger', () => { 276 | it('_.isInteger(3)', () => { 277 | assert.equal( 278 | _.isInteger(3), 279 | Number.isInteger(3) 280 | ) 281 | }) 282 | it('_.isInteger("3")', () => { 283 | assert.equal( 284 | _.isInteger("3"), 285 | Number.isInteger("3") 286 | ) 287 | }) 288 | it('_.isInteger(2.9)', () => { 289 | assert.equal( 290 | _.isInteger(2.9), 291 | Number.isInteger(2.9) 292 | ) 293 | }) 294 | it('_.isInteger(NaN)', () => { 295 | assert.equal( 296 | _.isInteger(NaN), 297 | Number.isInteger(NaN) 298 | ) 299 | }) 300 | }) 301 | describe('isPlainObject', () => { 302 | function isPlainObject(value) { 303 | if (typeof value !== 'object' || value === null) return false 304 | 305 | if (Object.prototype.toString.call(value) !== '[object Object]') return false 306 | 307 | const proto = Object.getPrototypeOf(value); 308 | if (proto === null) return true 309 | 310 | const Ctor = Object.prototype.hasOwnProperty.call(proto, 'constructor') && proto.constructor; 311 | return ( 312 | typeof Ctor === 'function' && 313 | Ctor instanceof Ctor && Function.prototype.call(Ctor) === Function.prototype.call(value) 314 | ); 315 | } 316 | 317 | function Foo() { 318 | this.a = 1; 319 | } 320 | 321 | it('_.isPlainObject(NaN)', () => { 322 | assert.equal( 323 | _.isPlainObject(NaN), 324 | isPlainObject(NaN) 325 | ) 326 | }) 327 | it('_.isPlainObject([1, 2, 3])', () => { 328 | assert.equal( 329 | _.isPlainObject([1, 2, 3]), 330 | isPlainObject([1, 2, 3]) 331 | ) 332 | }) 333 | it('_.isPlainObject(null)', () => { 334 | assert.equal( 335 | _.isPlainObject(null), 336 | isPlainObject(null) 337 | ) 338 | }) 339 | it("_.isPlainObject({ 'x': 0, 'y': 0 })", () => { 340 | assert.equal( 341 | _.isPlainObject({ 'x': 0, 'y': 0 }), 342 | isPlainObject({ 'x': 0, 'y': 0 }) 343 | ) 344 | }) 345 | it("_.isPlainObject(Object.create(null))", () => { 346 | assert.equal( 347 | _.isPlainObject(Object.create(null)), 348 | isPlainObject(Object.create(null)) 349 | ) 350 | }) 351 | it("_.isPlainObject(Object.create(new Foo()))", () => { 352 | assert.equal( 353 | _.isPlainObject(Object.create(new Foo())), 354 | isPlainObject(Object.create(new Foo())) 355 | ) 356 | }) 357 | it("_.isPlainObject(Object.create(new Date()))", () => { 358 | assert.equal( 359 | _.isPlainObject(Object.create(new Date())), 360 | isPlainObject(Object.create(new Date())) 361 | ) 362 | }) 363 | }) 364 | describe('get', () => { 365 | const get = (obj, path, defaultValue) => { 366 | const travel = regexp => 367 | String.prototype.split 368 | .call(path, regexp) 369 | .filter(Boolean) 370 | .reduce((res, key) => (res !== null && res !== undefined ? res[key] : res), obj); 371 | const result = travel(/[,[\]]+?/) || travel(/[,[\].]+?/); 372 | return result === undefined || result === obj ? defaultValue : result; 373 | }; 374 | var obj = { 375 | aa: [{ b: { c: 0 }, 1: 0 }], 376 | dd: { ee: { ff: 2 } }, 377 | gg: { h: 2 }, 378 | "gg.h": 1, 379 | "kk.ll": { "mm.n": [3, 4, { "oo.p": 5 }] } 380 | }; 381 | 382 | it ("should handle falsy values", () => { 383 | var val = _.get(obj, 'aa[0].b.c', 1) 384 | assert.strictEqual(val, get(obj, 'aa[0].b.c', 1)) 385 | assert.notEqual(val, 1) 386 | }) 387 | it ("should handle just bracket notation", () => { 388 | var val = _.get(obj, 'aa[0][1]', 1) 389 | assert.strictEqual(val, get(obj, 'aa[0][1]', 1)) 390 | assert.notEqual(val, 1) 391 | }) 392 | it ("should handle just period notation", () => { 393 | var val = _.get(obj, 'dd.ee.ff', 1) 394 | assert.strictEqual(val, get(obj, 'dd.ee.ff', 1)) 395 | assert.notEqual(val, 1) 396 | }) 397 | it ("should handle neither notation", () => { 398 | var val = _.get(obj, 'aa', 1) 399 | assert.deepEqual(val, get(obj, 'aa', 1)) 400 | assert.notEqual(val, 1) 401 | }) 402 | it ("should handle both notation", () => { 403 | var val = _.get(obj, 'aa[0].b.c', 1) 404 | assert.strictEqual(val, get(obj, 'aa[0].b.c', 1)) 405 | assert.notEqual(val, 1) 406 | }) 407 | it ("should handle array path", () => { 408 | var val = _.get(obj, ['aa', [0], 'b', 'c'], 1) 409 | assert.strictEqual(val, get(obj, ['aa', [0], 'b', 'c'], 1)) 410 | assert.notEqual(val, 1) 411 | }) 412 | it ("should handle undefined without default", () => { 413 | var val = _.get(obj, 'dd.b') 414 | assert.strictEqual(val, get(obj, 'dd.b')) 415 | }) 416 | it ("should handle undefined with default", () => { 417 | var val = _.get(obj, 'dd.b', 1) 418 | assert.strictEqual(val, get(obj, 'dd.b', 1)) 419 | }) 420 | it ("should handle deep undefined without default", () => { 421 | var val = _.get(obj, 'dd.b.c') 422 | assert.strictEqual(val, get(obj, 'dd.b.c')) 423 | }) 424 | it ("should handle deep undefined with default", () => { 425 | var val = _.get(obj, 'dd.b.c', 1) 426 | assert.strictEqual(val, get(obj, 'dd.b.c', 1)) 427 | assert.strictEqual(val, 1); 428 | }) 429 | it ("should handle null default", () => { 430 | var val = _.get(obj, 'dd.b', null) 431 | assert.strictEqual(val, get(obj, 'dd.b', null)) 432 | assert.strictEqual(val, null); 433 | }) 434 | it ("should handle empty path", () => { 435 | var val = _.get(obj, '', 1) 436 | assert.strictEqual(val, get(obj, '', 1)) 437 | assert.notEqual(val, obj); 438 | }) 439 | it ("should handle undefined obj", () => { 440 | var val = _.get(undefined, 'aa') 441 | assert.strictEqual(val, get(undefined, 'aa')) 442 | }) 443 | it ("should handle path contains a key with dots", () => { 444 | var val = _.get(obj, 'gg.h') 445 | assert.strictEqual(val, get(obj, 'gg.h')) 446 | assert.strictEqual(val, 1) 447 | }) 448 | it ("should handle array path of keys with dots", () => { 449 | var val = _.get(obj, ["kk.ll", "mm.n", 0, "oo.p"]) 450 | assert.strictEqual( 451 | val, 452 | get(obj, ["kk.ll", "mm.n", 0, "oo.p"]) 453 | ); 454 | }) 455 | }) 456 | describe('split', () => { 457 | const source = 'a-b-c'; 458 | const separator = '-'; 459 | const limit = 2; 460 | it(`_.split("${source}", "${separator}")`, () => { 461 | assert.deepEqual( 462 | _.split(source, separator), 463 | source.split(separator) 464 | ); 465 | }) 466 | it(`_.split("${source}", "${separator}", ${limit})`, () => { 467 | assert.deepEqual( 468 | _.split(source, separator, limit), 469 | source.split(separator, limit) 470 | ); 471 | }) 472 | }) 473 | describe('inRange', () => { 474 | const inRange = (num, init, final) => { 475 | if(final === undefined){ 476 | final = init; 477 | init = 0; 478 | } 479 | return (num >= Math.min(init, final) && num < Math.max(init, final)); 480 | } 481 | 482 | it('_.inRange(3, 2, 4)', () => { 483 | assert.equal( 484 | _.inRange(3, 2, 4), 485 | inRange(3, 2, 4) 486 | ) 487 | }); 488 | 489 | it('_.inRange(4, 8)', () => { 490 | assert.equal( 491 | _.inRange(4, 8), 492 | inRange(4, 8) 493 | ) 494 | }); 495 | 496 | it('_.inRange(4, 2)', () => { 497 | assert.equal( 498 | _.inRange(4, 2), 499 | inRange(4, 2) 500 | ) 501 | }); 502 | 503 | it('_.inRange(2, 2)', () => { 504 | assert.equal( 505 | _.inRange(2, 2), 506 | inRange(2, 2) 507 | ) 508 | }); 509 | 510 | it('_.inRange(1.2, 2)', () => { 511 | assert.equal( 512 | _.inRange(1.2, 2), 513 | inRange(1.2, 2) 514 | ) 515 | }); 516 | 517 | it('_.inRange(5.2, 4)', () => { 518 | assert.equal( 519 | _.inRange(5.2, 4), 520 | inRange(5.2, 4) 521 | ) 522 | }); 523 | 524 | it('_.inRange(-3, -2, -6)', () => { 525 | assert.equal( 526 | _.inRange(-3, -2, -6), 527 | inRange(-3, -2, -6) 528 | ) 529 | }); 530 | 531 | it('_.inRange(1, 1, 5)', () => { 532 | assert.equal( 533 | _.inRange(1, 1, 5), 534 | inRange(1, 1, 5) 535 | ) 536 | }); 537 | }) 538 | 539 | describe('random', () => { 540 | const random = (a = 1, b = 0) => { 541 | const lower = Math.min(a, b); 542 | const upper = Math.max(a, b); 543 | return lower + Math.random() * (upper - lower); 544 | }; 545 | 546 | const array = Array(1000).fill(0); 547 | 548 | it('random() in range [0, 1]', () => { 549 | assert.ok(array.every(() => { 550 | const randomValue = random(); 551 | return randomValue >= 0 && randomValue <= 1; 552 | })); 553 | }); 554 | 555 | it('random() is float', () => { 556 | assert.ok(array.some(() => { 557 | const randomValue = random(); 558 | return !Number.isInteger(randomValue); 559 | })); 560 | }); 561 | 562 | it('random(5) in range [0, 5]', () => { 563 | assert.ok(array.every(() => { 564 | const randomValue = random(5); 565 | return randomValue >= 0 && randomValue <= 5; 566 | })); 567 | }); 568 | 569 | it('random(5) is float', () => { 570 | assert.ok(array.some(() => { 571 | const randomValue = random(5); 572 | return !Number.isInteger(randomValue); 573 | })); 574 | }); 575 | 576 | it('random(-10) supports negative', () => { 577 | assert.ok(array.every(() => { 578 | const randomValue = random(-10); 579 | return randomValue <= 0; 580 | })); 581 | }); 582 | 583 | it('random(10, 5) swap the bounds', () => { 584 | assert.ok(array.every(() => { 585 | const randomValue = random(10, 5); 586 | return randomValue >= 5 && randomValue <= 10; 587 | })); 588 | }); 589 | 590 | it('random(-10, 10) supports negative', () => { 591 | assert.ok(array.some(() => { 592 | const randomValue = random(-10, 10); 593 | return randomValue > 0; 594 | })); 595 | assert.ok(array.some(() => { 596 | const randomValue = random(-10, 10); 597 | return randomValue < 0; 598 | })); 599 | }); 600 | 601 | it('random(-10, 10) in range [-10, 10]', () => { 602 | assert.ok(array.every(() => { 603 | const randomValue = random(-10, 10); 604 | return randomValue >= -10 && randomValue <= 10; 605 | })); 606 | }); 607 | 608 | it('random(1.2, 5.2) supports floats', () => { 609 | assert.ok(array.every(() => { 610 | const randomValue = random(1.2, 5.2); 611 | return randomValue >= 1.2 && randomValue <= 5.2; 612 | })); 613 | }); 614 | 615 | it('random(100000, 100001) in range [100000, 100001]', () => { 616 | assert.ok(array.every(() => { 617 | const randomValue = random(100000, 100001); 618 | return randomValue >= 100000 && randomValue <= 100001; 619 | })); 620 | }); 621 | }); 622 | 623 | describe('randomInt', () => { 624 | const randomInt = (a = 1, b = 0) => { 625 | const lower = Math.ceil(Math.min(a, b)); 626 | const upper = Math.floor(Math.max(a, b)); 627 | return Math.floor(lower + Math.random() * (upper - lower + 1)) 628 | }; 629 | 630 | const array = Array(1000).fill(0); 631 | 632 | const uniq = (arr) => [...new Set(arr)]; 633 | 634 | it('randomInt() return `0` or `1`', () => { 635 | const randoms = uniq(array.map(() => { 636 | return randomInt(); 637 | })).sort(); 638 | assert.deepStrictEqual(randoms, [0, 1]); 639 | }); 640 | 641 | it('randomInt(5) in range [0, 5]', () => { 642 | assert.ok(array.every(() => { 643 | const randomValue = randomInt(5); 644 | return randomValue >= 0 && randomValue <= 5; 645 | })); 646 | }); 647 | 648 | it('randomInt(5) is integer', () => { 649 | assert.ok(array.some(() => { 650 | const randomValue = randomInt(5); 651 | return Number.isInteger(randomValue); 652 | })); 653 | }); 654 | 655 | it('randomInt(-10) supports negative', () => { 656 | assert.ok(array.every(() => { 657 | const randomValue = randomInt(-10); 658 | return randomValue <= 0; 659 | })); 660 | }); 661 | 662 | it('randomInt(10, 5) swap the bounds', () => { 663 | assert.ok(array.every(() => { 664 | const randomValue = randomInt(10, 5); 665 | return randomValue >= 5 && randomValue <= 10; 666 | })); 667 | }); 668 | 669 | it('randomInt(-10, 10) supports negative', () => { 670 | assert.ok(array.some(() => { 671 | const randomValue = randomInt(-10, 10); 672 | return randomValue > 0; 673 | })); 674 | assert.ok(array.some(() => { 675 | const randomValue = randomInt(-10, 10); 676 | return randomValue < 0; 677 | })); 678 | }); 679 | 680 | it('randomInt(-10, 10) in range [-10, 10]', () => { 681 | assert.ok(array.every(() => { 682 | const randomValue = randomInt(-10, 10); 683 | return randomValue >= -10 && randomValue <= 10; 684 | })); 685 | }); 686 | 687 | it('randomInt(1.2, 5.2) supports floats', () => { 688 | assert.ok(array.every(() => { 689 | const randomValue = randomInt(1.2, 5.2); 690 | return randomValue >= 2 && randomValue <= 5; 691 | })); 692 | }); 693 | 694 | it('randomInt(100000, 100001) return `100000` or `100001`', () => { 695 | const randoms = uniq(array.map(() => { 696 | return randomInt(100000, 100001); 697 | })).sort(); 698 | assert.deepStrictEqual(randoms, [100000, 100001]); 699 | }); 700 | }); 701 | 702 | describe('clamp', () => { 703 | const clamp = (number, boundOne, boundTwo) => { 704 | if (!boundTwo) { 705 | return Math.max(number, boundOne) === boundOne ? number : boundOne; 706 | } else if (Math.min(number, boundOne) === number) { 707 | return boundOne; 708 | } else if (Math.max(number, boundTwo) === number) { 709 | return boundTwo; 710 | } 711 | return number; 712 | }; 713 | it('clamp(-10, -5, 5) returns lower bound if number is less than it', () => { 714 | assert.deepStrictEqual(clamp(-10, -5, 5), -5); 715 | }); 716 | it('clamp(10, -5, 5) returns upper bound if number is greater than it', () => { 717 | assert.deepStrictEqual(clamp(10, -5, 5), 5); 718 | }); 719 | it('clamp(10, -5) treats second parameter as upper bound', () => { 720 | assert.deepStrictEqual(clamp(10, -5), -5); 721 | }); 722 | }); 723 | 724 | describe('padStart', () => { 725 | it('_.padStart("123", 5, "0")', () => { 726 | assert.equal( 727 | _.padStart("123", 5, '0'), 728 | "123".padStart(5, '0') 729 | ); 730 | }) 731 | 732 | it('_.padStart("123", 6, "_-")', () => { 733 | assert.equal( 734 | _.padStart("123", 6, '_-'), 735 | "123".padStart(6, '_-') 736 | ); 737 | }) 738 | }) 739 | 740 | describe('padEnd', () => { 741 | it('_.padEnd("123", 5, "0")', () => { 742 | assert.equal( 743 | _.padEnd("123", 5, '0'), 744 | "123".padEnd(5, '0') 745 | ); 746 | }) 747 | 748 | it('_.padEnd("123", 6, "_-")', () => { 749 | assert.equal( 750 | _.padEnd("123", 6, '_-'), 751 | "123".padEnd(6, '_-') 752 | ); 753 | }) 754 | }) 755 | 756 | describe('upperFirst', () => { 757 | const upperFirst = (string) => { 758 | return string ? string.charAt(0).toUpperCase() + string.slice(1) : '' 759 | } 760 | 761 | it('_.upperFirst("george")', () => { 762 | assert.equal( 763 | _.upperFirst('george'), 764 | upperFirst('george') 765 | ) 766 | }) 767 | 768 | it('_.upperFirst(null)', () => { 769 | assert.equal( 770 | _.upperFirst(null), 771 | upperFirst(null) 772 | ) 773 | }) 774 | 775 | it('_.upperFirst("")', () => { 776 | assert.equal( 777 | _.upperFirst(''), 778 | upperFirst('') 779 | ) 780 | }) 781 | }) 782 | 783 | 784 | 785 | describe('isString', () => { 786 | function isString(str) { 787 | if (str != null && typeof str.valueOf() === "string") { 788 | return true 789 | } 790 | return false 791 | } 792 | 793 | it('_.isString("abc")', () => { 794 | assert.deepEqual(_.isString("abc"), 795 | isString("abc")) 796 | }); 797 | 798 | it('_.isString(1)', () => { 799 | assert.deepEqual(_.isString(1), 800 | isString(1)) 801 | }); 802 | 803 | it('_.isString("")', () => { 804 | assert.deepEqual(_.isString(''), 805 | isString('')) 806 | }) 807 | }); 808 | 809 | 810 | describe('lowerFirst', () => { 811 | const lowerFirst = (string) => { 812 | return string ? string.charAt(0).toLowerCase() + string.slice(1) : '' 813 | } 814 | 815 | it('_.lowerFirst("Fred")', () => { 816 | assert.equal( 817 | _.lowerFirst('fred'), 818 | lowerFirst('fred') 819 | ) 820 | }) 821 | 822 | it('_.lowerFirst(null)', () => { 823 | assert.equal( 824 | _.lowerFirst(null), 825 | lowerFirst(null) 826 | ) 827 | }) 828 | 829 | it('_.lowerFirst("")', () => { 830 | assert.equal( 831 | _.lowerFirst(''), 832 | lowerFirst('') 833 | ) 834 | }) 835 | }) 836 | 837 | 838 | describe('isUndefined', () => { 839 | const definedVariable = 1; //defined variable (will return false) 840 | let undefinedVariable; //undefined variable (will return true) 841 | 842 | it('_.isUndefined(definedVariable)', () => { 843 | assert.equal(_.isUndefined(definedVariable), 844 | (definedVariable === undefined)) 845 | }); 846 | 847 | it('_(definedVariable).isUndefined()', () => { 848 | assert.equal(_(definedVariable).isUndefined(), 849 | (definedVariable === undefined)) 850 | }); 851 | 852 | it('_.isUndefined(undefinedVariable)', () => { 853 | assert.equal(_.isUndefined(undefinedVariable), 854 | (undefinedVariable === undefined)) 855 | }); 856 | 857 | it('_(undefinedVariable).isUndefined()', () => { 858 | assert.equal(_(undefinedVariable).isUndefined(), 859 | (undefinedVariable === undefined)) 860 | }); 861 | 862 | }); 863 | 864 | describe('flatten', () => { 865 | 866 | it('_.flatten(twoLayerArray)', () => { 867 | const testArray = [1,2[3,4]]; 868 | assert.deepEqual(_.flatten(testArray), 869 | testArray.reduce((a,b) => a.concat(b), [])) 870 | }); 871 | 872 | it('_.flatten(multiLayerArray)', () => { 873 | const testArray = [1,2[3,4,[5,6,[7,8]]]]; 874 | assert.deepEqual(_.flatten(testArray), 875 | testArray.reduce((a,b) => a.concat(b), [])) 876 | }); 877 | 878 | }); 879 | 880 | describe('forEach', () => { 881 | it('_.forEach(array)', () => { 882 | const testArray = [1,2,3,4]; 883 | 884 | let lodashOutput = [] 885 | let nativeOutput = [] 886 | 887 | _.forEach(testArray, element => { 888 | lodashOutput.push(element); 889 | }); 890 | testArray.forEach(element => { 891 | nativeOutput.push(element); 892 | }); 893 | 894 | assert.deepEqual(lodashOutput,nativeOutput); 895 | }); 896 | 897 | it('_.forEach(object)', () => { 898 | const testObject = { 899 | 'one':1, 900 | 'two':2, 901 | 'three':3, 902 | 'four':4, 903 | } 904 | 905 | let lodashOutput = [] 906 | let nativeOutput = [] 907 | 908 | _.forEach(testObject, value => { 909 | lodashOutput.push(value); 910 | }); 911 | 912 | Object.entries(testObject).forEach(([key,value]) => { 913 | nativeOutput.push(value); 914 | }); 915 | 916 | assert.deepEqual(lodashOutput,nativeOutput); 917 | }); 918 | }); 919 | 920 | describe('startsWith', () => { 921 | it(`_.startsWith('abc', 'a')`, () => { 922 | assert.deepEqual( 923 | _.startsWith('abc', 'a'), 924 | 'abc'.startsWith('a') 925 | ); 926 | }); 927 | it(`_.startsWith('abc', 'b')`, () => { 928 | assert.deepEqual( 929 | _.startsWith('abc', 'b'), 930 | 'abc'.startsWith('b') 931 | ); 932 | }); 933 | it(`_.startsWith('abc', 'b', 1)`, () => { 934 | assert.deepEqual( 935 | _.startsWith('abc', 'b', 1), 936 | 'abc'.startsWith('b', 1) 937 | ); 938 | }); 939 | }); 940 | 941 | describe('endsWith', () => { 942 | it(`_.endsWith('abc', 'c')`, () => { 943 | assert.deepEqual( 944 | _.endsWith('abc', 'c'), 945 | 'abc'.endsWith('c') 946 | ); 947 | }); 948 | it(`_.endsWith('abc', 'b')`, () => { 949 | assert.deepEqual( 950 | _.endsWith('abc', 'b'), 951 | 'abc'.endsWith('b') 952 | ); 953 | }); 954 | it(`_.endsWith('abc', 'b', 2)`, () => { 955 | assert.deepEqual( 956 | _.endsWith('abc', 'b', 2), 957 | 'abc'.endsWith('b', 2) 958 | ); 959 | }); 960 | }); 961 | 962 | describe('throttle', () => { 963 | function throttle(func, timeFrame) { 964 | var lastTime = 0; 965 | return function () { 966 | var now = new Date(); 967 | if (now - lastTime >= timeFrame) { 968 | func(); 969 | lastTime = now; 970 | } 971 | }; 972 | } 973 | 974 | it('throttle is not called more than once within timeframe', () => { 975 | let callCount = 0; 976 | const fn = throttle(() => callCount++, 100); 977 | 978 | fn(); 979 | fn(); 980 | fn(); 981 | 982 | assert.equal(callCount, 1); 983 | }); 984 | }) 985 | 986 | describe('isFunction', () => { 987 | function isFunction(func) { 988 | return (func && typeof func === "function") 989 | } 990 | 991 | it('_.isFunction(setTimeout)', () => { 992 | assert.deepEqual(_.isFunction(setTimeout), 993 | isFunction(setTimeout)) 994 | }); 995 | 996 | it('_.isFunction(1)', () => { 997 | assert.deepEqual(_.isFunction(1), 998 | isFunction(1)) 999 | }); 1000 | 1001 | it('_.isFunction(abc)', () => { 1002 | assert.deepEqual(_.isFunction("abc"), 1003 | isFunction("abc")) 1004 | }); 1005 | 1006 | }); 1007 | 1008 | describe('unionBy', () => { 1009 | function unionBy(...arrays) { 1010 | const iteratee = (arrays).pop(); 1011 | 1012 | if (Array.isArray(iteratee)) { 1013 | return []; // return empty if iteratee is missing 1014 | } 1015 | 1016 | return [...arrays].flat().filter( 1017 | (set => (o) => set.has(iteratee(o)) ? false : set.add(iteratee(o)))(new Set()), 1018 | ); 1019 | }; 1020 | 1021 | it('should take an iteratee function', () => { 1022 | assert.deepStrictEqual(_.unionBy([2.1], [1.2, 2.3], Math.floor), unionBy([2.1], [1.2, 2.3], Math.floor)); 1023 | }); 1024 | 1025 | it('should output values from the first possible array', () => { 1026 | assert.deepStrictEqual(_.unionBy([{ x: 1, y: 1 }], [{ x: 1, y: 2 }], (x) => x.x), 1027 | unionBy([{ x: 1, y: 1 }], [{ x: 1, y: 2 }], (x) => x.x)); 1028 | }); 1029 | }); 1030 | 1031 | describe('capitalize', () => { 1032 | function capitalize(string) { 1033 | return string ? string.charAt(0).toUpperCase() + string.slice(1).toLowerCase() : ''; 1034 | } 1035 | 1036 | it('_.capitalize("FRED")', () => { 1037 | assert.deepStrictEqual(_.capitalize("FRED"), capitalize("FRED")); 1038 | }); 1039 | 1040 | it('_.capitalize("fred")', () => { 1041 | assert.deepStrictEqual(_.capitalize("fred"), capitalize("fred")); 1042 | }); 1043 | 1044 | it('_.capitalize("HELLO WORLD")', () => { 1045 | assert.deepStrictEqual(_.capitalize("HELLO WORLD"), capitalize("HELLO WORLD")); 1046 | }); 1047 | 1048 | it('_.capitalize("hello world")', () => { 1049 | assert.deepStrictEqual(_.capitalize("hello world"), capitalize("hello world")); 1050 | }); 1051 | }); 1052 | 1053 | describe('defaults', () => { 1054 | it('sets up default values the same way', () => { 1055 | const defaultValues = { a: 1, b: 2, c: 3 }; 1056 | const givenValues = { b: 4 }; 1057 | 1058 | const lodashObject = _.defaults(givenValues, defaultValues); 1059 | const vanillaObject = Object.assign({}, defaultValues, givenValues); 1060 | 1061 | assert.deepStrictEqual(vanillaObject, { a: 1, b: 4, c: 3}); 1062 | assert.deepStrictEqual(vanillaObject, lodashObject); 1063 | }); 1064 | 1065 | it('should handle nested values equally', () => { 1066 | const defaultValues = { a: 1, b: 2, c: { x: 3, y: 4 } }; 1067 | const givenValues = { c: { x: 5 } }; 1068 | 1069 | const lodashObject = _.defaults(givenValues, defaultValues); 1070 | const vanillaObject = Object.assign({}, defaultValues, givenValues); 1071 | 1072 | assert.deepStrictEqual(vanillaObject, { a: 1, b: 2, c: { x: 5 } }) 1073 | assert.deepStrictEqual(vanillaObject, lodashObject); 1074 | }); 1075 | }); 1076 | 1077 | describe('last', () => { 1078 | it('_.last([1,2,3,4,5])', () => { 1079 | assert.deepEqual(_.last([1,2,3,4,5]), [1,2,3,4,5].at(-1)); 1080 | }); 1081 | it('_.last([])', () => { 1082 | assert.deepEqual(_.last([]), [].at(-1)); 1083 | }); 1084 | }); 1085 | describe('first', () => { 1086 | it('_.first([1,2,3,4,5])', () => { 1087 | assert.deepEqual(_.first([1,2,3,4,5]), [1,2,3,4,5].at(0)); 1088 | }); 1089 | it('_.first([])', () => { 1090 | assert.deepEqual(_.first([]), [].at(0)); 1091 | }); 1092 | }) 1093 | describe('head', () => { 1094 | it('_.head([1,2,3,4,5])', () => { 1095 | assert.deepEqual(_.head([1,2,3,4,5]), [1,2,3,4,5].at(0)); 1096 | }); 1097 | it('_.head([])', () => { 1098 | assert.deepEqual(_.head([]), [].at(0)); 1099 | }); 1100 | }) 1101 | }); 1102 | --------------------------------------------------------------------------------