├── .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 | [](https://spectrum.chat/you-dont-need/lodash-underscore)
4 | [](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 | [](https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore)
44 | [](https://www.npmjs.org/package/eslint-plugin-you-dont-need-lodash-underscore)
45 | [](https://travis-ci.org/you-dont-need/You-Dont-Need-Lodash-Underscore)
46 | [](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 |
--------------------------------------------------------------------------------