6 ms·
Is that really 5 minutes? (For when left-pad was relevant) var cache = [ '', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '
by axlee 6y ago
Is that really 5 minutes? (For when left-pad was relevant)
var cache = [
'',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' '
];
function leftPad (str, len, ch) {
// convert `str` to a `string`
str = str + '';
// `len` is the `pad`'s length now
len = len - str.length;
// doesn't need to pad
if (len <= 0) return str;
// `ch` defaults to `' '`
if (!ch && ch !== 0) ch = ' ';
// convert `ch` to a `string` cuz it could be a number
ch = ch + '';
// cache common use cases
if (ch === ' ' && len < 10) return cache[len] + str;
// `pad` starts with an empty string
var pad = '';
// loop
while (true) {
// add `ch` to `pad` if `len` is odd
if (len & 1) pad += ch;
// divide `len` by 2, ditch the remainder
len >>= 1;
// "double" the `ch` so this operation count grows logarithmically on `len`
// each time `ch` is "doubled", the `len` would need to be "doubled" too
// similar to finding a value in binary search tree, hence O(log(n))
if (len) ch += ch;
// `len` is 0, exit the loop
else break;
}
// pad `str`!
return pad + str;
}
- TheDong 6y agoOne of the points of the article is that when you code a dependency for your purpose, you get smaller code. For example, I know I'll always pass it a string, so I don't need to type-check. I started with the problem of "I need a function that pads a string with a character out to some length". Coding it up took under 1 minute, easily under 5 minutes. function leftPad (str, len, ch) { const neededPadding = len - str.length; if (neededPadding <= 0) { return str; } return ch.repeat(neededPadding) + str; } It took me longer to write this comment. The fact that the left-pad code has optimizations (which don't matter for the place I'm using it) and type-checks (which don't matter; my higher level unit tests would catch that mistake) is beside the point.
- scarface74 6y agoUnless you are doing front end code, why does the size of the code matter?
- TheDong 6y ago"smaller" there was short-hand for "more focused, purpose-built to your problem, less generic". All of those properties make the code easier to reason about and test. Being easier to reason about, and being a more exact abstraction for my needs, are both incredibly valuable properties.
- scarface74 6y agoMore generic is better. If I go into a codebase that uses standard libraries. Even if I don’t know how to do something about it someone on the Internet does. Your custom framework - not so much. I don’t have to care about how the underlying libraries work. I can treat them as a black box.
- TheDong 6y ago> I don’t have to care about how the underlying libraries work. I can treat them as a black box. When I wrote haskell, the majority of the libraries did just work, and I didn't have to dig into their code to find bugs often. When I wrote javascript, hundreds of the libraries I used did not just work. I usually had to care very much about their details because they were poorly implemented, full of bugs and incorrect abstractions, and often abandoned soon after. I agree that there's benefits in reusing some well-socialized and well-implemented generic frameworks and abstractions. It's not worth using generic abstractions that are not well understood, buggy, and don't match your needs closely. In that case, write your own. More generic is not always better. Above, I'm arguing that it's important for code to be easier to reason about. If a generic abstraction helps with that, cool, but it's not always going to be the case.
- scarface74 6y agoThat’s also why I stay away from the clusterf%%% of front end development and JS if possible except for simple AWS Lambda scripts that have one dependency - AWS SDK. Any other scripting I do with Python. Any more complicated development it’s using a language with an ecosystem with adults - C# or Go.
- deleted 6y ago[deleted]
- cogman10 6y agoThis is actually a great example of why you SHOULDN'T use left pad. I've not profiled it, but I'm going to guess that now-a-days this will be faster than the current implementation on npm. function leftPad (str, len, ch) { str += ''; len = len - str.length; if (len <= 0) return str; if (!ch && ch !== 0) ch = ' '; ch += ''; return ch.repeat(len) + str; } Why? because the VM is (very likely) going to do exactly what the cache would have done. It can replace `ch.repeat(len) + str;` with a presized string allocation and a memcpy of ch + str characters.
- goldenkey 6y agoThis code checks for ch !== 0, yet the bypass branch afterwards calls .repeat. The Number type in JS has no repeat method. Static typing would actually fix this kind of coding error.
- reificator 6y ago> This code checks for ch !== 0, yet the Number type in JS has no repeat method. > Static typing would actually fix this kind of coding error. That's not a coding error, that's addressing an oddity of JS coercion rules that a less experienced developer could easily have missed. > if (!ch && ch !== 0) ch = ' ' That code says that if `ch` is falsey and not equal to 0, then set it to a space. The only arguable falsey value that should be excluded here is a literal `false`, but that's not a single character and is fairly ambiguous either way. I'd certainly fall on the side that a literal false should not be converted to `'false'` here. > ch += ''; The next line converts to to a string by adding it to the empty string. > return ch.repeat(len) + str; So by the time it gets to this line we know ch is a string. Static typing is great, but the bug you claim is there is not actually there.
- cogman10 6y agoFYI, I edited the post after the bug was pointed out to eliminate it and bring this method up to parity with leftpad.
- 6y ago
- reificator 6y agoYes, particularly when you know the types that will be passed in. True that typechecking in JS can be a little timeconsuming, so it might take 10-15 for me to write a general purpose left pad. Also I seem to remember that someone benchmarked the cached version and found it to be slower than the naive approach anyway. I could be mistaken there.
- ghj 6y agoThis is so hilariously overengineered: not failing when passed the wrong types, arbitrarily caching padding of len < 10, etc The most egregious is the bad big O analysis for a pointless binary search. The loop does indeed run O(log(n)) times but `ch += ch` still takes O(ch.length) which is growing exponentially. It ends up being a complicated way of still taking O(n) time while creating a lot of intermediate strings. It isn't any faster than just creating the padding with a loop or `new Array(len).fill(ch).join('')` or `ch.repeat(len)`
- Ajedi32 6y agoDid you run benchmarks to validate those assumptions? The developers of left-pad did: https://github.com/left-pad/left-pad/blob/master/perf/perf.js https://github.com/left-pad/left-pad/blob/master/perf/perf.j... It's not overengineered if thousands of downstream projects are relying on it, some of which might see significant benefits from those performance optimizations.
- cogman10 6y agoToo bad that's a flawed benchmarking methodolgy. JITs are notoriously hard to correctly profile and the benchmark lib isn't even sort of doing the right thing. For example, it's missing warmup. The results aren't being consumed in a way that wouldn't optimize them away. The framework itself imposes a pretty large amount of overhead (moreso than I'd expect from leftpad). It is somewhat likely that what they are measuring isn't leftpad performance, but rather how fast the JIT ends up optimizing the benchmark code. I'd suggest watching this video https://vimeo.com/78900556 https://vimeo.com/78900556 It's about microprofiling the JVM, but the principles are the same for other JIT based languages (such as javascript).
- Ajedi32 6y agoYeah benchmarks can be much more difficult to get right than it might seem at first glance. Good thing they didn't try to write their own benchmarking code, otherwise they might have fallen into those traps you just mentioned. Luckily, they didn't, and instead pulled in the the `benchmark` library as a development dependency. The author of said library works on V8, and already considered all those problems and much, much, more[1]. [1]: https://mathiasbynens.be/notes/javascript-benchmarking https://mathiasbynens.be/notes/javascript-benchmarking
- tluyben2 6y agoWell, my point was more that if a programmer thinks '5 minutes work' it's often 10+ minutes; so when a programmer thinks 'an afternoon', you can possibly lose a week. And then the article really doesn't work. And yeah, I would and do write leftpad myself it it's not in the stdlib. But if there is a large library full of similar (string) functions that I might need, I would include that library. Not a singular dependency for this type of function.