No Lodash
thescottyjam.github.io
thescottyjam.github.io
1. Many of these examples are just implementing the function (e.g., _.chunk). Yes, Lodash is written in Javascript, so yes, you can implement them yourself if you wanted.
2. Lodash is tree-shakeable, so if you import the functions you use directly, only those will be included in your JS bundle.
Just use Lodash, it's better than having to maintain your own hodgepodge of poorly documented and tested utility functions.
It would actually be cool if lodash started deprecating functions like _.fill (via semver so nothing breaks, of course) someday.
3.0.1
2.6.1
> It doesn't make any sense to remove it.It does. Once the native platform supports the functionality it's redundant to keep it around and makes it less clear to a developer that the library isn't required any more. I'd argue it doesn't make any sense to use it but I'm not going to tell you that you can't. It's possible to accommodate everyone.
I disagree. Keeping it in accommodates everyone (not just your needs). You are not forced to use it, or even bundle it. I use lodash-es w/ tree shaking as it is.
Why not? Until recently they were publishing individual packages for every single Lodash function. Publishing different versions that just do or do not exclude a single function is pretty simple by comparison. It seems like you want the project to hold itself back for no good reason.
Right. But we're talking about group that until recently maintained 281 separate packages. "much more burdensome" is a relative term here and publishing two versions simultaneously is absolutely not that burdensome.
> I prefer the lodash way of doing things, you don't. So don't use it, that is fine.
The solution I'm proposing lets you and me both use it exactly how we want to. I honestly find this debate to be quite baffling.
Why?
Sure but you can't filter in a map, or map in a filter.
So you can do both map and filter as part of a reduce or flatMap, but not either as part of the other. (You may be able to get close by doing ugly things in a map/filter that abuses the second and third arguments, but I don't think you can get a general map out of filter or vice versa, and you shouldn't do the kind of things you'd need to do to get close, just use flatMap.)
I don't use Lodash, but recently began work on a project that does. I was surprised (along with the rest of the team) to find out that Lodash is not tree-shakeable by default. So if you do `import { debounce } from 'lodash';`, you're actually including the entirety of lodash in your bundle. More info here: https://lodash.com/per-method-packages
The recommended solution by the lodash team is to use `babel-plugin-lodash` (including a babel plugin to use less code? no thanks) or import the single modules directly like `import throttle from 'lodash/throttle';`. In the end, since we only used about 3 functions from lodash that were trivial to replicate, we just ditched lodash.
EDIT: There is also a `lodash-es` package with native ESM modules which may solve this issue for some. We avoided this also because it had implications with our toolchain.
Unless you were importing individual functions.
Also we did have a mix of things like import _ from ‘lodash’ and import foo from ‘lodash/foo’, etc
I believe the post you're replying to is referring to what I originally referred to (doing something like `const throttle = require('lodash/throttle')`, which you would install lodash normally for `npm install lodash`).
EDIT: And to be a bit pedantic, importing single modules is not really treeshaking. You're literally just importing and including the entirety of single modules.
I'd say that's basically a poor man's treeshaking because you're telling it you only want that module and its dependencies. Not it trying to figure it out for you.
All of this crud builds up, and you end up with 25MB bundles and slow SPAs.
With that approach fallible humans carry less risk.
Now going onto the lodash package on npm, that's listed as v4.17.21 and hasn't been published in over 2 years: https://www.npmjs.com/package/lodash
Now the lodash package on github, the latest release is listed as v4.0.0 from 2016: https://github.com/lodash/lodash/releases
There have been no commits to the main branch on github in almost 2 years: https://github.com/lodash/lodash/commits/master
I consider lodash to be deprecated at this point, and will always go for a lightweight function pulled from somewhere like SO or No Lodash.
Activity on master really died down mid-2017 [1]
And the most recent closed PR is jdalton this year (Jan 4 '23) saying he's not looking to expand lodash [2]
[1] https://github.com/lodash/lodash/graphs/contributors [2] https://github.com/lodash/lodash/pull/5575#issuecomment-1371...
For example, the replacement for _.concat() is really just advice on how to concatenate arrays in-line. In such cases, I think learning that a packaged function is not needed is pretty nifty.
Here's the one you referenced, lodash.chunk: https://unpkg.com/lodash.chunk – 140 lines after removing comments and whitespace.
That's pretty small compared to a lot of the lodash utilities. Try spot-checking a few on unpkg.
I prefer angus's `just` utilities: https://github.com/angus-c/just
The "bloat" is the result of being battle tested. A slimmer implementation would cut out edge cases that I may or may not run into. If I use Lodash, I know I'm all set.
0 lines of code is preferable to battle-tested code.
10 lines of code is preferable to 1000 if you don't need them.
Not always. Lodash's _.find is 5k. Why is something as simple as find so huge? It has a lot of magic. A lot of that magic provides safety though.
I don't recommend dropping lodash unless you can start using typescript.
1. Functions that have equivalently terse native versions (fill, map, etc)
2. Function that have multi-line equivalents (chunk, zip, etc)
Yes you can _.map an object and you can't myObject.map but I prefer Object.[keys/values](myObject).map(...) but for something like _.chunk I absolutely prefer to use lodash. Maybe one day we will see a new major version of lodash that deprecates all the near-native versions of functions so you can still use them but your editors will do a strikethrough and the JSDoc for the function can steer you to the native version but there are still a lot of useful lodash functions.
There are good reasons not to:
- You don't want to add a build step to your project
- You don't want to introduce any additional knowledge burden on contributors to your code, other than knowing JS. Your "chunk" is the few lines of source code in your repo -- not an external thing with documentation you have to lookup.
- You don't want to deal with the security issues every dependency introduces
Let's specifically address this claim, which is often used in roll-your-own vs use-a-library debates:
> to maintain your own hodgepodge of poorly documented and tested utility functions
I feel, in this particular case, this argument is a straw man. The functions in question are basically 1-liners. The likelihood of bugs are low. The need for documentation is nil: the implementations self-document.
No, they're not. The first one is not a one-liner. What's the point of spending the time writing your own interface, implementation, and tests of every single util that you end up needing, if you could just import lodash and move on?
> What's the point of spending the time writing your own interface, implementation, and tests of every single util
I would not write tests for many of these, and the interfaces usually require no design because these are familiar functions like map, filter, etc, to most experienced developers.
I gave the main reasons answering your questions in my last post, and I think those reasons are powerful enough that I would consider using a lodash a mistake in most situations.
You are essentially making the "leftPad" argument, it's a kind of inverse "NIH" ethos: Why should you ever write your own anything when someone else has done it? And the answer, again, is the same: dependencies have a non-trivial cost.
But suppose you already have usecases for lodash's more complex functions. It can make sense to use a library like lodash to fill in those functions rather than utilizing your own. And if you already have lodash available, you may as well use it for simple functions, too, rather than write your own.
Ironically I don't see all of those in this list. Maybe OP just assumes everyone already knows how not to use those
For example:
_.compact(array)
vs array.filter(value => !!value);
Most JavaScript programmers will immediately know what `array.filter(value => !!value)` does but many will have to lookup the lodash documentation to understand what `_.compact(array)` does.What remains is a pretty useful overview that lets the reader see what each function is doing with emphasis on the how. It is presented as "if you wanna replace this, here's how".
If I'm not already familiar with Lodash and trying to decide whether or not to bring it in as a dependency, and see that all I need is a 1-2 liner in native JS, that's useful information. I might still conclude that Lodash is the right choice, but I think that when examined in the context of the broader industry, where 3rd party libraries are often unnecessary and sometimes actively unhelpful, I think such a page is useful.
Whether or not it's "compelling" will entirely depend on the problem you're currently trying to solve.
That is, I'd love to see how many of the replacements look like
_.now() -> Date.now()
and how many of them are like _.invert(object) ->
function invert(obj) {
const newObj = {};
for (const [key, value] of Object.entries(obj)) {
newObj[value] = key;
}
return newObj;
}
Also clicking through I see a lot of "when lodash was created this was necessary because X, but now we can Y". Kudos for including that. It is both informative and helps this not come off as an attack.It reminds me of https://youmightnotneedjquery.com/ in a good way.
It's similar to the pipe operator seen in other languages (Elixir, F#, etc). There is a proposal to add a pipe operator to JS as well - still in early stages, though.
// pull traits dictionary out of tokens
const extractTraits = R.pipe(
R.map(R.pipe(
R.last,
R.prop('attributes'))),
R.reject(R.isNil),
R.reduce(R.mergeWith(concatValues), {}),
R.map(R.pipe(
R.unless(R.is(Array), R.of),
R.groupBy(R.identity),
R.map(R.count(R.identity)),
R.toPairs,
R.sortBy(R.prop(0)),
R.map(R.zipObj(['name', 'count'])))));Exactly, lodash should be part of the language. https://github.com/mlajtos/es1995
I am not a professional js developer but I do like to build dinky websites. I generally prefer not to bother with build steps or dependencies. For me it's preferable to have my own implementation of a function within the file or project I am working in. That way I can directly inspect what the function is doing, which is "more readable" to me.
Moreover, sometimes I need functionality that is similar but just a bit different from something that lodash does. This seems like a good resource to check for inspiration.
For example, if you have a nullable array. In plain JS, you have to check the presences of the array, then run `find` (or whatever function). Lodash simply treats it like an empty array - returning nothing.
This is particularly handy in React where it creates clutter to have to check for the empty state.
I personally feel this type of ultra-defensive programming where all types are assumed bad only harms you long run, adding complexity where it isn’t necessary to solve problems that don’t exist, and swallowing errors making it harder to debug. Exceptions exist for a reason.
When I switched to TS it was a great incentive to ditch lodash because it was munging all my types. This in turn simplified my code as it made me aware of unnecessary guards, coercions, and errors that lodash was swallowing.
It's also worth noting that lodash lets you import individual functions, e.g.
import throttle from 'lodash/throttle'
so even when you do need a utility function you can avoid pulling the entire library into your code.
"lodash/throttle" loads throttle.js in the lodash package.
"lodash.throttle" loads index.js in the lodash.throttle package.
The latter is being deprecated but not the former.
Assuming you're working on commercial products, how do you decide when to move to new-ish native functions? For example, do you log feature detection for the function a month or two prior to the planned change?
“Browser support” is not an issue anymore unless you do something cutting edge, sell to regulated industries like healthcare, or serve many users in China/Africa et al.
It strikes me as the difference between knowing why your role exists and not realizing you deliver organizational value, over engineering perfection.
Lodash is engineered to deliver “perfection” and performance, to an absolute fault. Try `import {unary} from 'lodash'` and tell me how big the bundle gets. FYI unary should be `fn => x => fn(x)`.
On the other hand, copy-pasting actually-useful utilities like groupBy from some random site defeats the purpose of npm. Luckily there are sane Lodash alternatives like Just:
This doesn't pass the smell test for me, considering how small the library as a whole is, and the many, many use cases that would not involve exposing the code to the user at all.
In fact, I find it hard to think of an effective software dev who does care about 28ms without having specific reason to, due to a niche use case (not a generic web app for Internet things, like a hospital instrument that needs every ms).
Indeed, I guess we just develop different classes of applications. I don't know what a "generic web app for Internet things" is.
And you're on a "generic web app for Internet things". This page loading 27ms slower would not be perceptible.
Personally I've never worked at a product where response time wasn't an important metric.
Using libraries like lodash, instead of adding 12 left-pads of different authors to the dependencies manifest, is an improvement over the status quo.
To use Lodash, a developer needs to know what is available there in the first place. You're taking it for granted, but everyone, you included, had to study its documentation and memorize which functions were there, even if it was only to "look it up" later.
Someone like you could have said a few years ago about Lodash: "why do I need this stupid library, I will just use for loops everywhere, like I learned in college. They get the job done and my role is to provide value, not to care about libraries or code reusability".
With this website, it's the same thing: it's there to teach you which things in Lodash aren't needed anymore and which are. Then it is up to the developer to use what's best. Not everything in the internet is prescriptive.
Blind dogmatism shouldn't have a place in our profession.
For example with lodash, the process for deciding to use it could be, "Hey this looks like it could save me time, let's try it." That's it. You don't have to go on this large, multi-hour/multi-day journey discovering every little thing about it first.
I'm arguing here that highly effective developers are able to use software to solve business problems favoring efficiency over correctness.
And I'm not sure it's efficient to care much about whether or not lodash could be rebuilt internally.
This page is here, for example, so that people who only know about Lodash can know how it's implemented. If they ever start working on a no-Lodash project, they might not need to import Lodash.
This is not a call-to-arms to uninstall Lodash from all projects and replace with hundreds of functions. Why would it be?
Further, I'm not responding to the page itself, but the discussion here around the page. A lot of developers are using their emotions (e.g. disgust, fear) to navigate their professional role, and I'm suggesting that this need not be the case.
You can set all of that aside and be a highly effective developer. You don't need to care about any of this, and I'm seeing a lot of devs here who think they have to care in order to be effective.
Check flatten in this website:
array.flat();
Versus Lodash: function baseFlatten(array, depth, predicate, isStrict, result) {
predicate || (predicate = isFlattenable)
result || (result = [])
if (array == null) {
return result
}
for (const value of array) {
if (depth > 0 && predicate(value)) {
if (depth > 1) {
// Recursively flatten arrays (susceptible to call stack limits).
baseFlatten(value, depth - 1, predicate, isStrict, result)
} else {
result.push(...value)
}
} else if (!isStrict) {
result[result.length] = value
}
}
return result
}
About your point: sure, nobody has to care about all of this. But I don't see why we should give people a hard time because they do on their free time.And I'm not giving people a hard time, I'm saying you don't have to care about all of this to be good at your developer job. If this was an obvious point to you, great!
It says so in there: it is "a description of how one would go about doing the same thing in vanilla JavaScript".
> This page is here, for example, so that people who only know about Lodash can know how it's implemented.
But my larger point here is just that you don't have to care about "only importing lodash if absolutely necessary" to be a good dev. You can care about those things, but for the folks who think they have to care, I'm here to say you don't.
It's not that your coworkers don't know you can get away with doing a bad job, it's that they're not motivated by the same things that you are.
It's true that in some contexts, throwing npm libraries at the wall to see what sticks is delivering organizational value. In many others, it's negative value.
The folks who don't care about being "successful" are not "good devs", because they're not clear about why their job exists in the first place. You don't really get to define success for your role without your organization's input, and when you overly focus on the engineering to the exclusion of delivering value, you are not acting as a "good dev".
Intrinsic motivation is a real thing. You don't have to let your boss or your parents or your teachers tell you what it means to do a good job. You can decide for yourself.
People who just grab libraries from npm and don't take their imports seriously are not the ones delivering value in the places where I've worked. Far from it. Those are the people whose "work" you have to re-do weeks or months later because it didn't actually deliver value. Often times it doesn't work at all, never worked, never compiled, etc. But those people are long gone because they're busy being "successful" and moving on to other roles, creating negative value in other parts of the organization.
You say that only successful people know why their job exists, and unsuccessful people are blind to organizational value. In my experience, you couldn't be more wrong.
Effective developers know the answers to those questions. What else they do is quite varied, and some effective developers care about things like "should we use lodash or not?" But other effective developers don't.
I didn't say "only successful people" do anything. What I said was you don't have to care about how lodash works or be concerned about importing it vs. rolling your own to be an effective developer.
I like Elixir pipelines so this is equivalent. I suppose it depends on preference if someone chain or not.
And one can install only the individual lodash functionality that’s required. Personally I’d do this in the node/js ecosystem because everyone does it and why not? It’s battle tested.
I work primarily in Go so I’m used to writing my own utilities for many things. It isn’t that complicated and end of the day it’s just basic programming.
When almost all software is completely terrible, "everyone does it" is not a compelling reason to do anything. And this approach is indeed battle tested, in battles that were abject massacres (see left-pad).
That said, if focusing on front end dev work I think it removes knowledge of underlying apis which can be counterproductive in the long run. If you have enough knowledge to achieve in plain JS what is done with lodash, you are better set up for success when given a problem not yet solved in your code base or stackoverflow.
The content does not claim that you should not use Lodash. It only presents alternatives to Lodash functions. The actual title "Lodash Replacements" is far more accurate by reflecting what the page contains, and doesn't invite misinterpretations or ideological responses.
I have found that not so much when writing my own projects and tinkering, but specifically for "real" work (at my job) is when I have to deal with iterating over collections all the time. Anything less readable than Lodash is just overly annoying thanks to JS verbosity. Most people wouldn't remember to pull these methods out of a utils directory that we maintain simply for the purpose of ignoring packages like Lodash.
By a collection, I'm talking about an array of objects that follow the same pattern.
But I still like lodash, particularly for functions like these:
- words
- kebabCase / startCase / snakeCase
- partition
- debounce
Basically the more advanced functions for which no readily available alternative exists?
What app? Is this an app? If it is, why didn’t you name it or tell me what I’m missing out on instead of using the default some framework spits out.
[temporarily enables JS for the site]
Oh, it’s just static informational content that should have good SEO and TUI-compatibilty but missed the mark. Don’t need Lodash? How about this page doesn’t need JavaScript! These code examples are the perfect semantic use case for the <details> + <summary> elements.
(And I love JavaScript, but for contexts that need it like applications)
If so, no, it's not entirely useless. Check the article for examples where it isn't.
2. No, for most things there are several differences between what is on Lodash source code and the current native-Javascript implementation. Check this: https://news.ycombinator.com/item?id=35057549
Even though JS is pass by value, if you pass an object, the other nested objects will still be references to its predecessor.