14 ms·
const at = (arr, i) => i >= 0 ? arr[i] : arr[arr.length + i]; Why does the core language need to be extended to incorporate this?
by beaconstudios 5y ago
const at = (arr, i) => i >= 0 ? arr[i] : arr[arr.length + i];
Why does the core language need to be extended to incorporate this?
- cphoover 5y ago+1
- zodiakzz 5y agoJust Google "left-pad" to know why. I think .at() also doesn't have to look up the entire prototype chain so it has performance benefits as well.
- beaconstudios 5y agoconst leftpad = (str, len, char = ' ') => str.length >= len ? str : (char.repeat(len - str.length) + str); Anybody who installs a dependency instead of writing a one line function is just leaving themselves exposed for no real benefit. Providing the at function outside of the prototype chain like I did above will not incur the cost you mentioned, even in the unlikely case that such calls are the bottleneck of your application. Plus, if array lookup calls are your bottleneck then you probably need a different data structure.
- zodiakzz 5y agoReinventing the wheel is error prone. Noone wants to bloat their code/time with unit tests for all these utility methods. Just look at your at() function, I already spotted a bug, if a numeric string is passed: `([].length + '-1') === '0-1'`
- beaconstudios 5y agoyes that is obvious - my point was that the functionality is minimal. with guards: const at = (arr, i) => { if(!(Array.isArray(arr) && typeof i === 'number')) { throw new Error('invalid arguments'); } // same as before }; you wouldn't just commit a single-line function in your code. But there are 10,000 micro-functions that could be in the core of JS. Why bother to add this, especially if it's so easy to implement? There are surely better things the JS committee could be putting their time towards, like providing functionality that is presently lacking in JS (maybe they could finally get around to advancing the pipe operator through the TC stages).
- Osiris 5y agoYour number guard is insufficient. NaN, infinity, 2.23 are all Number but not supported values for the function. This goes to show that it's NOT trivial to implement in a robust way.
- beaconstudios 5y agoIt is trivial. It requires a few more lines than my example, but my example was intended to show the core use case. All I'm getting in replies is nitpicking about my example case... Can't you just assume when reading that I know how to write some simple guard statements? It's hardly rocket science.
- claytongulick 5y agoBecause it is such a common case? To invert your argument, why would you want millions of developers to have to waste their time writing this sort of thing for such a common need? Yes, it's clear that you could write the function correctly. Could everyone? What about bugs that aren't obvious? The runtime errors that result? Why would we want to waste so much developer time and effort globally, when we can just add .at() to the language?
- lhorie 5y agoIf we're going to nitpick, those are actually all valid values for `arr[i]`... (though, to be fair, they don't exactly do what one might necessarily expect)
- borschtplease 5y agoI want you to look at some other languages beside javascript. Take a look at Swift or Ruby. Compared to them it feels like 60% of js code is boilerplate. 200 lines of js can be fit in 60 lines of Swift. All because js stdlib is just sucks. I’m tired of this silly ‘someArray.length ? someArray[someArray.length - 1] : null’, I want just ‘someArray.last()’
- maest 5y ago> Reinventing the wheel is error prone. Having a standard lib for these sort of functions is a common pattern many languages adopt.
- MsMowz 5y agoIsn't that really a pattern that the runtime adopts (i.e. not the language itself)? For ES, the libraries are dependent on the runtime environment; for browsers, there are the web APIs, and for Node, there's npm. I'm not sure how you could have anything more standard given the nature of things.
- Quekid5 5y agoThis might be just a few levels of people talking past each other, but just in case: Yes, the full "standard libarary" you can expect to be present on any given JS VM may vary, but there's no plausible reason that e.g. at() should not work on almost any conceivable platform.
- tomstoms 5y ago> for Node, there's npm True, NodeJS runtime comes with its own «standard library» but npm has nothing to do with it.
- chrismorgan 5y agoQuite incidentally, this is fixed with the addition of one character: const at = (arr, i) => i >= 0 ? arr[i] : arr[arr.length + +i];
- zodiakzz 5y agoNit: it throws if you wanna use a negative bigint as `i` hehehe :( let i = -1n; let test = +i; > Uncaught TypeError: can't convert BigInt to number See this is where standard libraries shine, they consider all the pieces of the puzzle. There's no good reason to disallow bigints here. P.S: Unary plus operator is neat but this TypeError with BigInt is why I am starting to prefer the more verbose `Number(i)`.
- chrismorgan 5y agoHmm, I had completely forgotten about that distinction. I also can’t find it in the spec <https://tc39.es/ecma262/ https://tc39.es/ecma262/>, which says that Number “performs a type conversion when called as a function rather than as a constructor.” <https://tc39.es/ecma262/#sec-number-constructor https://tc39.es/ecma262/#sec-number-constructor>. What this means isn’t spelled out, which seems poor. I would have thought it meant ToNumber <https://tc39.es/ecma262/#sec-tonumber https://tc39.es/ecma262/#sec-tonumber>, but that would throw an exception on BigInt (like unary plus does, <https://tc39.es/ecma262/#sec-unary-plus-operator https://tc39.es/ecma262/#sec-unary-plus-operator>). So now I’m guessing it means ToPrimitive <https://tc39.es/ecma262/#sec-toprimitive https://tc39.es/ecma262/#sec-toprimitive> with preferredType number, which seems to match. This being the case, I believe BigInt is the sole difference between +x and Number(x).
- Osiris 5y agoTheir example does no argument validation. It doesn't check that arr is array, or that i is an integer, etc. The spec actually addresses the argument validation.
- Aachen 5y agoI agree that dependency hell is a problem, but I don't agree that reinventing the wheel is better. Including things a lot of developers will need in the language is probably the best way to approach things like left_pad, at least given these three options (reinvent, third-party, language inclusion).
- lucideer 5y agoYou're assuming this is a decision available to each app developer. In fact these dependencies are usually chosen by the developer of a library that's a dependency of a dependency of a dependency of a dependency of a library you choose as an app developer. Auditing the entire dependency tree of every library you choose is extremely arduous without proper tooling, and the tooling here has never been good (and even the more recently available better tooling is CVE-focused so won't highlight tells like package size/maintenance status/whatever other heuristics you might devise as a proxy for quality).
- easrng 5y agoor const leftpad = (str, len, char = ' ') => str.padStart(len, char);
- beaconstudios 5y agoHaha yes that would be the modern option.
- Cthulhu_ 5y agoBecause at some point someone will - and probably has - make a library out of this, adding yet one more to the tens of thousands of files that my cruddy server can't handle already because of the sheer quantity. Plus you're probably missing a few dozen edge cases and optimizations that the native version would incorporate. We wouldn't have been in node_modules dependency hell if they incorporated a few more things into the standard library or language.
- beaconstudios 5y agoin my experience, the vast majority of node modules I have installed don't provide these tiny little functions, but are: - frameworks like react or express and extensions for the same - meaningful libraries like axios, ramda, date-fns or qs - transpiler/bundler enhancements like webpack loaders or babel presets I would prefer that the JS committee focused on bringing meaningful new capabilities to node and the browser that are currently lacking, like they did with the various HTML5 libraries, fetch(), ES classes and so on, rather than providing tiny functions that aren't even providing new functionality (array indexing is easy and idiomatic in JS). If there was no opportunity cost then sure, add tiny pointless builtin functions all day long.
- lucideer 5y agoYou seem to be garnering a lot of downvotes without replies so I'll bite: it sounds from your comment like you've never looked into the subdependency tree of those frameworks and meaningful libraries that you list (which seems like it would be hard to miss tbh for anyone that's ever opened up the node_modules directory). Those tiny little libs providing tiny little functions are likely not direct dependencies of your application, rather dependencies of a subdependency of a subdependency of a subdependency of your preferred "meaningful" library.
- beaconstudios 5y agoI'm aware of that. Unfortunately that's always going to be the case that some people who write libraries you depend on indirectly are not very good at programming and lean heavily on other dependencies to provide basic functionality. That will be the case regardless of how all-encompassing the stdlib is, and is entirely dependent on the culture of practice of the language you're using. JS is both beginner-friendly and the culture is focused on personal marketability, so you're going to get a lot of beginners land-rushing to put out basic libraries that don't do anything to cater to other beginners, in order to put the number of stars/npm downloads on their blog or CV. That's not going to change with the number of new core features.
- jahewson 5y agoArray parameter should be last, surely? Or why not extend the prototype? If only we had some way to standardise such decisions...
- johnfn 5y agoSubtle readability improvements on patterns that see extensive use actually make a difference. I had the exact same thoughts when `.includes()` was proposed to be added to the spec - it's just `.indexOf() !== -1`! - but time has proven me wrong, and I suspect it will prove us wrong about `at` as well. I've written an `.indexOf() !== -1` check hundreds if not thousands of times, and I've gotten the conditional wrong enough times to actually need to revert a change in prod. I have similar thoughts about .at(). Taking an element from the end of the array is ever-so-slightly error prone. Sure, you won't make a mistake this time, or the next time, but write it a hundred times, or a thousand, and I bet a bug will slip in. It's darn near impossible to get `.at(-1)` wrong, however.
- beaconstudios 5y agoThat's actually a pretty reasonable argument, the 'length - N' form is actually quite prone to off-by-one errors so .at might help there.
- NicolaiS 5y agoIndeed, and as others have stated it is really hard to allow `[-1]` without breaking old code. Also: `[1, NaN, "hi"].indexOf(NaN)` is -1 but `.includes(NaN)` returns true as expected.
- nawgz 5y ago> `[1, NaN, "hi"].indexOf(NaN)` is -1 but `.includes(NaN)` returns true Brutal.
- alerighi 5y agoExcept you would never get to use this method, because it will take years for all browser vendors to implement it, and even then, you still have a number of users that use for a reason or another Internet Explorer. You need to use a transpiler (like Typescript), but at this point, there no reason to implement that in the language, a transpiler can implement the feature by simply adding the method to the prototype of the object, like this: Array.prototype.at = function (i) { return i < 0 ? this[this.length + i] : this[i] }