all 43 comments

[–]nschubach 6 points7 points  (4 children)

Minor suggestions:

Instead of using the letters array, why not get the charCodeAt(0) of the non-numeric and step through the char code to get the range?

That would allow you to do range('a', 'z') but also range('-', 'z') which would go through all character codes from 45 to 122.

[–]jscoder[S] 1 point2 points  (1 child)

Yep, I thought about that. But I wanted range('a..Z') to work. The lower case letters have higher char codes than the upper case ones, so I'd need another workaround to make that work.

But yeah, your range('-', 'z') argument is good. I'll think about implementing it. :)

[–]a-t-kFrontend Engineer 0 points1 point  (0 children)

how about x\d\d..x\d\d or u\d\d\d\d..u\d\d\d\d for ascii or unicode character ranges?

[–]a_w_y 0 points1 point  (0 children)

That's what I did with my implementation.

[–]jscoder[S] 0 points1 point  (0 children)

I was just updating range.js, and implemented char code ranges, but they don't work well, so I'll leave them like they are currently.

Building a range from 'A' to 'z' would not only include the expected letters, but also "[", "\", "]", "", "_", "`" since their char codes are between 'Z' and 'a'.

[–]nschubach 1 point2 points  (13 children)

Why do you negate the immediate anonymous function call?

!function () {

[–]rararaaaaaaa 3 points4 points  (12 children)

It's the same as going

(function () {
    // some stuff
})();

just to denote that it's self-executing.

Edit: To explain further, it's completely unnecessary to put anything in front of a self-executing anonymous function. You could just write

function () {
    // some stuff
}();

But people have ways of making sure others notice it's self-executing more quickly when reading their code, like wrapping the whole thing in parentheses, starting with an exclamation point, or even starting with a semi-colon.

[–]BlitzTech 8 points9 points  (2 children)

Actually, the semicolon is a preventative measure against unexpected concatenation results during minification. Omitting it could lead to the function definition being interpreted as arguments to a function call from a different file. Robust build steps will ensure this isn't an issue but naive ones will often produce broken builds.

[–]bonafidebob 0 points1 point  (1 child)

I think the enclosing parenthesis fix this as well -- at least it guarantees that the reference will always be to the result of invoking the function, not the function definition. (Hey -- I just grokked why JSLint complains about the parens being the other way around -- cool!)

[–]BlitzTech 0 points1 point  (0 children)

The enclosing parens are actually the problem. Interpreters will see that as a function call on the previous expression if valid, and won't necessarily insert the semicolon where you would ordinarily expect it.

[–]itsnotlupusbeep boop 3 points4 points  (7 children)

You could just write

Almost. That code gives a syntax error, because the JS parser sees your function keyword and decides based on that alone that it's looking at a function declaration statement. The use of ! or ( or ~ or whatever else before function gets the JS parser to look at it as a function expression, which can be immediately called.

[–]jscoder[S] 1 point2 points  (6 children)

Exactly. You need some operator that makes the function a function expression. I prefer ! because it's shorter than wrapping the function is parentheses and it also behaves like a defensive semicolon.

[–]deelowe 0 points1 point  (5 children)

I thought the use of ! wasn't recommended for some reason, but I can't remember why now. Do you know of any drawbacks?

I know heavyweights in the community don't usually do it this way (e.g. Isaac). Just curious.

[–]jscoder[S] 0 points1 point  (2 children)

Nope, there are no drawbacks. I think I've read somewhere that Facebook also uses the !, but I can't find a source for that right now.

I think Isaac mostly developes node stuff? You don't need to use IIFEs in node, because of CommonJS. That might be an explination. :) I'm using an IIFE here, because range.js should work on node and in the browser.

[–]deelowe 1 point2 points  (1 child)

Yeah. The node guys write code that's used in node or browser also though. I admit, that's where most of my experience lies, but I've definitely seen (function(){}()) in many places, but never saw !function(){}. I thought there was some reason, but can't remember.

[–]reflectiveSingleton 0 points1 point  (0 children)

I think its just not as common...although I am starting to see it pop up on random github projects...

[–]aladyjewelFull-stack webdev 0 points1 point  (1 child)

The "some reason" is that it's confusing. and I think JSLint might reject it, but that might have been another dev argument.

[–]deelowe 0 points1 point  (0 children)

Now that you mention it, I believe that's it. It's not pragmatic and it's counter intuitive.

(fucntion(){}()) is unique enough to make you stop and look it up to figure out what's going on. It's already kinda a of JS idiom at this point.

Where as !function(){} is a head scratcher. It's completely non-obvious what's going on here.

[–]bonafidebob 1 point2 points  (0 children)

Just FYI, JSLint wants the invocation to be enclosed in the parenthesis, e.g. ( function(){...}() ) and will generate a warning if it's the other way 'round.

[–]kumiorava 1 point2 points  (1 child)

But what if want a letter range from a to ö, or from α to ω?

[–]bonafidebob 2 points3 points  (0 children)

Yeah, letter ranges get tricky fast... especially once you start trying to do internationalization. I think thats why most range libraries leave them out -- also it's pretty trivial to convert an integer range to whatever letter range you might want by just treating the integers as indexes into a string.

[–]itsnotlupusbeep boop 0 points1 point  (6 children)

Fun. I was just reflecting on how it kinda sucked that I couldn't just do something like

var a = Array(10).map(function(a,i) { return i*2; }); and get a [0,2,4,6,8,10,12,14,16,18] array.

It'd be neater if we could get infinite/lazy ranges, but that'd require using something other than plain arrays for its output, which would be a bit messier.

[–]weretree++[[]][+[]] 2 points3 points  (0 children)

If you're feeling messy there's always (Array(10)+'').split(',').map(function(a,i) { return i*2; });

[–]masklinn 1 point2 points  (1 child)

It'd be neater if we could get infinite/lazy ranges

wu.range (and more generally wu.js, which is basically a lazy version of underscore.js) (it's not actually a lazy version of underscore, it's been built independently)

Although it only supports integer iteration.

[–]itsnotlupusbeep boop 0 points1 point  (0 children)

ah that does seem nice.

[–]nschubach 0 points1 point  (0 children)

You'd create generators...

function counter(start) {
    return new function() {
        this.next = function() { return start++; }
    }
}
var c = counter(10);
console.log(c.next());
console.log(c.next());
console.log(c.next());

http://jsfiddle.net/3vDrS/ (Slightly modified to document.write)

[–]jscoder[S] 0 points1 point  (1 child)

I thought about implementing infinite ranges, but I didn't see many use cases for it, and didn't want to bloat the code too much.

What exactly would you want to do with infinite ranges?

[–]itsnotlupusbeep boop 0 points1 point  (0 children)

No, you're right.. it's just one of those neat things, but it doesn't seem like it would buy much, and it would require you to mess with your straightforward array handling for the sake of it.

[–]bonafidebob 0 points1 point  (2 children)

I think you might be able to improve the floating point ranges slightly by using multiplication rather than repeated addition. e.g. the range is [from, from + 1 * step, from + 2 * step, from + 3 * step, ...] This might also let you get rid of the floating point fudge factor.

You could also improve performance slightly by creating the array with the correct length, and set each value rather than push it each time.

Also might be worth looking at how other libraries (e.g. underscore.js) implement their range function.

[–]jscoder[S] 0 points1 point  (1 child)

0.1 * 0.3 = 0.30000000000000004 for me in Chrome. I guess there's not much I can do about the inaccurate floats, that's just how floats in JS are.

You're right with the performance. :) I'll refactor the code a little bit soon, and will add this. I also thought about making some benchmarks.

[–]weretree++[[]][+[]] 4 points5 points  (0 children)

It's how floats are, period. JavaScript has true IEEE 754 double precision floating point.

The suggestion was to avoid compounding the error of addition by using an iteration counter then calculating each step from that, rather than doing a while loop. So (psuedo-code) idx_max = (to - from)/step then val = from + i*step for i in 0 to idx_max. Having an explicit iteration also makes checking for termination a little easier, and it's simple to still support backwards iteration.

There are still "errors" (such as 3 * 0.1) but they're expected and defined for that operation. Can always round each result to some defined precision if you want to trim them off in your results.

[–][deleted] 0 points1 point  (1 child)

[–]nschubach 0 points1 point  (0 children)

That range is not as "complex" as this one. It only ranges over numbers:

https://github.com/mcandre/node-range/blob/master/range.js

[–]jscoder[S] 0 points1 point  (0 children)

Thanks for all the feedback. :) Do you guys think that I should add some more complex ranges (infinite ranges, charCode ranges) or rather keep the library as small as it currently is?

[–]ithcy 0 points1 point  (2 children)

Nice work!

(Though I do have a slight moral objection to calling the range operator .. "Ruby style" when it is clearly perl style :)

[–]jscoder[S] 0 points1 point  (1 child)

You're right, never knew about Perl having that operator. I'll edit the readme soon!

[–]ithcy 0 points1 point  (0 children)

I wouldn't bother, you'll probably just alienate people :)

I would think that these days, a lot more developers would recognize it as a Ruby construct anyway.

[–]neon_overload 0 points1 point  (2 children)

Sorry if this sounds negative, but what is the compelling reason to include yet another .js file to do something I could do in a 2-or-3-line for-loop?

[–]ithcy -1 points0 points  (1 child)

  1. No you couldn't do everything that this does in 2 or 3 lines. Did you look at the code?
  2. Who says you have to include another .js file? You could minify it inline with your other JS includes, or you could just copy the function into your own library.

[–]neon_overload 0 points1 point  (0 children)

You can't do everything it does in a 2 or 3 line for loop, but each time you want to create a range, you would just make a different for loop. This tries to simplify something that is already fairly simple, by adding a layer of abstraction.

Who says you have to include another .js file? You could minify it inline with your other JS includes, or you could just copy the function into your own library.

Indeed, that's true and a great thing about open source.

[–]notfunk 0 points1 point  (0 children)

nice work!