Tersest way to create an array of integers from 1..20 in JavaScript
Asked Answered
G

16

74

What would be the tersest way to create this array:

var x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
         11, 12, 13, 14, 15, 16, 17, 18, 19, 20];

For example, a for loop:

var x = [];
for (var i=1;i<=20;i++) {
  x.push(i);
}

Or a while loop:

var x = [], i = 1, endInt = 20;
while (i <= endInt) {
  x.push(i);
  i++;
}

Would there be other examples that would be terser -- in other words -- less code? I'm thinking of things like in Ruby where the equivalent code I believe would be as simple as 1..20. I'm not aware of syntax like that in JavaScript but I'm wondering if there are shorter ways to do that same thing.

UPDATE: I wasn't thinking of removing semicolons or var for answers in the question, but I have to admit the question implies that. I am more curious about algorithms than shaving bytes. Sorry if I was unclear! Also, making it into a function is simple enough, just slap function range(start, end) { /* guts here */ } around it and you're there. The question is are there novel approaches to the "guts."

Glutelin answered 9/6, 2011 at 21:24 Comment(5)
Is there a reason that code brevity is important, or is this just a thought experiment?Lustful
Really a thought experiment more than anything. I was creating an array and thinking actually of PHP, where I can do var $x = range(1, 20);... like, why not in JavaScript?Glutelin
why don't you create the function range() in JS and use that?Downstroke
The push() method is actually not very smart here because of dynamic array growth. If you know the number of fields beforehand, you should use a size-explicit initializing and fill the fields via indexing: x[i-start_i] = i;Overcurious
Can't help but think svicks comment is the proper answer. It is JS after all.Disembodied
B
109

Favorite method

Update Sep13,2015:

Just came up with this new method which works with browsers which support the ES6 standard:

> Array(5).fill().map((x,i)=>i)
[0, 1, 2, 3, 4]

Note the above does a tiny bit of extra work (fills with undefined) but is relatively minor vis-a-vis the speedup you can achieve by using a for loop, and if you forget the .fill you may be confused why your array is mysteriously [empty x 5]. You can encapsulate the above as a custom function, or alternatively use a somewhat more intended method:

> Array.from(Array(5),(x,i)=>i)
[0, 1, 2, 3, 4]

You can of course directly go from that into whatever you want to do, like python's list comprehensions e.g. [i**2 for i in range(5)]:

> Array.from(Array(5), (_,i)=> i**2)
[0, 1, 4, 9, 16]

... or if you want to get more complicated...:

> Array.from(Array(5), (_,i)=> {
    const R = /*some computation*/;
    return /*etc*/;
});

[edit May,2021]: theoretically tersest way of defining such a function nowadays is f=i=>i?[...f(i-1),i]:[], where you replace f with range1 or whatever the name is, but which would be very slow (quadratic complexity) due to intermediate structures so should never be used. f=i=>i?f(i-1)&&x.push(i)&&x:x=[] is linear complexity but relies on abuse of notation and is unreadable and pollutes global variables as well. But, since defining arrow functions (which don't bind but rather inherit this) is pretty terse nowadays, you could just wrap the above solution:

const range1 = n=> Array.from(Array(n), (_,i)=> i+i);
// range1(5)==[1, 2, 3, 4, 5]

Circumstantially, the tersest way to do a range(N), if you already have a list lying around of exactly that length N, is just to map it: e.g. rather than do Array.from(Array(myArr.length), (_,i)=> i**2), you would just do myArr.map((_,i)=> i**2). (This has no side-effect unless you want it to.)


everything below is historical:

After thinking about it a bit, this is the shortest implementation of the standard range(N) function in JavaScript I could come up with:

function range1(i){return i?range1(i-1).concat(i):[]}

Note: Do not use this in production; it's O(N^2)

Contrast with current top-voted answer:

function range1(i){var x=[];var i=1;while(x.push(i++)<i){};return x}

Example:

> range1(5)
[1, 2, 3, 4, 5]

This is like the poster child for recursion, though I was expecting it to be longer until I thought of ternary-if-statement, which brings it down to 42 necessary characters.

Note that the "standard" range function returning [start,end) can be written by doing .concat(i-1).


Update: Ooh, I discovered an incredibly short version with ugly imperative syntax by abusing for loops, reverse-ordering, the fact that assignments return a value: for(y=[],i=20;y[--i]=i;){} consisting of only 25 characters (though you will want var y which you can insert into a for loop, and +1 if you don't want 0...19). While it is not shorter if you need to define a function, it is shorter than i?r(i-1).concat(i):[] if you do not need to make a function.


Added some performance profiling testcases: it seems that everything besides a standard in-order for-loop is 10x slower, at least on V8. https://jsperf.com/array-range-in-javascript (Of course, none of this matters if you're programming in a functional style anyway and would hit every element with a function call anyway.)

Balcom answered 9/6, 2011 at 21:49 Comment(10)
Very nice! To make a range 1-20: function range(i) {return i>1 ? range(i-1).concat(i-1) : [];} and use range(21) ;~)Joceline
@kooilnc: it's actually even easier/more elegant than that =) see updated answerBalcom
really smart! If I where the OP I would choose this answer.Joceline
Oh, that is awesome! Very neat use of recursion and concat() developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…Glutelin
I should mention though that while my answer is terse (as asked for), it may not be efficient for large ranges, because javascript's .concat may not be an O(1) operation sadly.Balcom
the concat way is very slow compared to the while-- as it executes the function itself many times + executes concat every time and also the if ....Magneto
Thumbs up for the recursive solution (very elegant), but Javascript doesn't implements tail recursion optimisation, so, no offence, but I wouldn't consider it a solution (on the other hand you provide a pletora of properly working alternatives, which warrant for your answer to be marked as a solution).Caricaria
The first ES6 alternative will likely throw a Supplied parameters do not match any signature of call target. error. This is because the fill() method in the Array prototype must receive the fill value. Checkout my solution to use the fill()method.Mayhap
caroso1222's comment is likely a feature/bug/issue with Typescript, and probably not ES6. One could indeed do .fill(0).Balcom
Beware! function range1(i){var x=[];var i=1;while(x.push(i++)<i){};return x} is an endless loop. It crashed the tab in my browser.Kassia
S
32

It can be done with features from the ES6, currently only supported by Firefox thou. I found a compatibility table here: http://kangax.github.io/compat-table/es6/

Array.from(new Array(20), (x,i) => i+1)

If you want to have some other range then I guess you could do

Array.from(new Array(5), (x,i) => i+5)

Which would then be [5,6,7,8,9]

Somatoplasm answered 10/4, 2015 at 10:37 Comment(1)
This works in Chrome as of v45, and is usable if you are using Babel to transpile ES6. You can also do Array.from({ length: 20 }, (v, k) => k + 1);Wright
R
17

You can do this with a while loop where the push happens inside the condition.Array.push returns the length of the array, which happens to be the same as the value in this case. So, you can do the following:

x = []; //normally would use var here
i = 1;  //normally would use var here
while(x.push(i++)<20){}

//at this point, x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

Condensed version (31 characters)

x=[];i=1;while(x.push(i++)<20);

jsFiddle example

Radix answered 9/6, 2011 at 21:32 Comment(8)
Oh! I like that. It's perverse though. My understanding is that .push() returns the length of the array. In Mozilla Docs: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… "The push method is useful for easily appending values to an array. The return value of this method is the new length property of the object upon which the method was called." It just happens that in an array beginning with 1 the last one push()ed is going to be the length. Ha!Glutelin
@EndangeredMassa, x.Push() returns the new length not the value.Coopersmith
@EndangeredMassa. Is x.push(i++) equal to the count of elements in x?Collado
Ah, yes. In this specific case, those are the same.Radix
He's saving space by not using var, but if you implement this, please use var, or whoever maintains your code will find you...Hurried
That only works if you're starting with 1. If you start with 7 for example, you'll get a range from 7 - 26.Jourdain
I feel this is a bit deceptive due to the spacing. If we're comparing conciseness, here is the answer as a single line: x=[];i=1;while(x.push(i++)<20){}.Balcom
well first I thought you exactly tied me (mine is "var x=[],i=0;while(i<20)x[i]=++i" ), but i can shorten mine by 4 characters by removing "var " if i want to make globals. You can trim one character by changing the braces at the end to a semicolon :)Monovalent
N
7

If you're OK with 0-20, here are my latest favs from recent code golfing:

[...'0'.repeat(21)].map((_,i)=>i)
Array.from({length:21},(_,i)=>i)
Array(21).fill().map((_,i)=>i)
[...Array(21)].map((_,i)=>i)
Array(21).map((_,i)=>i)
[...Array(21).keys()]
Noisy answered 29/5, 2021 at 15:18 Comment(3)
Array(21).map((_,i)=>i) is the same as Array(21), both lead to an empty array of length 21.Crackling
I is the index. It maps to the index, not to the values, which end up in _Noisy
Array(21).map((_,i)=>i) produces an array of empty for me.Chronicle
M
5

while-- is the way to go

var a=[],b=10;while(b--)a[b]=b+1

returns [1,2,3,4,5,6,7,8,9,10]

explained with start & length

var array=[],length=20,start=5;while(length--)array[length]=length+start

returns [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]

want range?

explained with start & end

var array=[],end=30,start=25,a=end-start+1;while(a--)array[a]=end--

returns [25, 26, 27, 28, 29, 30]

for --

for(var a=[],b=20;b>0;b--,a[b]=b+1)

for++

for(var a=[],b=0;b<20;b++,a[b]=b+1)

WHY is this theway to go?

  1. while -- is prolly the fastest loop;

  2. direct setting is faster than push & concat;

  3. [] is also faster than new Array(10);

  4. it's not much longer code than all the others

byte saving techniques:

  1. use the arguments as a placholder forthe in function variables
  2. don't use new Array(),push(),concat() if not needed
  3. place "(){};," only when needed.
  4. use a,b,c,d... in short functions.

so if u want a function for this

with start,end (range)

function range(a,b,c,d){d=[];c=b-a+1;while(c--)d[c]=b--;return d}

so now range(3,7) returns [3,4,5,6,7]

u save bytes in many ways here and this function is also very fast as it does not use concat, push, new Array and it's made with a while --

Magneto answered 3/6, 2013 at 18:32 Comment(0)
M
4

Using ES6

numArr = Array(5).fill(0).reduce(arr=>{ arr.push(arr.length); return arr },[])

Mayhap answered 10/9, 2016 at 18:7 Comment(1)
a little shorter: Array( 5 ).fill().reduce( arr => ( arr.push( arr.length ), arr ), [] )Kassia
B
2

if you accept to have a counter starting from 0 instead of 1...

const zeroNineteen = [...Array(20).keys()]; 
// outputs [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]

if you really need it to start with 1:

const terserst = [...Array(21).keys()].slice(1);

it's 30 characters and yet readable, don't you think?

Bourque answered 2/4, 2021 at 6:57 Comment(2)
The best tbh :)Secateurs
Same length: [...Array(21).keys()].slice(1), [...Array(20)].map((_,i)=>i+1)Noisy
B
2
Array.from({length: n}).map((_, i) => i);
Bossism answered 16/1, 2022 at 20:41 Comment(1)
Executing this in an environment that runs it results in an empty array. I might be missing the intended context. Can you provide more detail?Glutelin
H
1

I can't think of a way with less characters than ~46:

var a=[];while(a.length<20)a.push(a.length+1);

Granted, you could make a function out of that.

Reading your comments about a function, you could do something like

var range = function (start, end) {
    var arr = [];

    while (start <= end) {
        arr.push(start++)
    }

    return arr;
};

Then range(1, 20) would return the array as expected.

Hurried answered 9/6, 2011 at 21:31 Comment(2)
I'm not trying to replicate range() - for example the way Underscore.js does: documentcloud.github.com/underscore/#range -- var x = _.range(1, 20); -- the question is more about the underlying methods and control structures to create the array. I was curious if there were ones I was not aware of.Glutelin
@artlung: nope, you pretty much got them all. Loops or creating your own method, which is essentially a loop.Hurried
M
1

If you are looking to shave characters off anyway possible without regard for readability, this is the best I can do:

var x=[],i=0
while(i<20)
  x[i]=i+++1

Not a lot better than yours though.

Edit:

Actually this works better and shaves off a couple characters:

var x=[],i=0
while(i<20)
  x[i]=++i

Edit 2:

And here's my entry for a general "range" function in the least number of characters:

function range(s,e){var x=[];while(s<e+1)x.push(s++);return x}

Again, don't write code this way. :)

Monovalent answered 9/6, 2011 at 21:31 Comment(5)
What's that i+++1 supposed to do? Where are your semicolons? Is this JavaScript?Overcurious
The construction is using the ++ operator, and adding 1 +1 to it. Not Crockford-compliant, but clever and terse.Glutelin
Hey it runs and produces the correct result. And no, I don't write code this way! Semicolons are optional (I always use them however), and i+++1 is the same as (i++) + 1Monovalent
Why would you ever use i+++1 instead of i+=2, that hurts my eyes.Hurried
@Robert: i+=2 is not the same thing, but you can actually use ++i in this case and it works. Note that it won't work in the general case, but only because the array index happens to be 1 less than the value.Monovalent
J
1

I suppose this is the shortest way:

var i=0, arr = [];
while (i++<20){
  arr.push(i);
}

or associating on the 'perverse' code in EndangeredMassa's answer:

var i,arr; while (i=i||1, (arr=arr||[]).push(i++)<20){}
Joceline answered 9/6, 2011 at 21:37 Comment(0)
T
1

You could always create a function...

function createNumArray(a, b) {
   var arr = [], 
       i = a;

    while((arr[arr.length] = i) < b) {i++}
    return arr;
}

Which allows you to write succinct code later on such as...

var arr = createNumArray(1, 20);
Tiny answered 9/6, 2011 at 21:48 Comment(0)
C
0

In my knowledge, the option of using for loop, as you mentioned, is the most tersest.

That is,

var x = [];
for (var i=1;i<=20;i++) {
  x.push(i);
}
Collado answered 9/6, 2011 at 21:28 Comment(0)
C
0
var i = 0;
var x = [];
while (i++ < 20) x.push(i);

JSFiddle

Coopersmith answered 9/6, 2011 at 21:31 Comment(1)
While this code snippet may solve the question, including an explanation out of the code really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. Please try not to crowd your code with explanatory comments, this reduces the readability of both the code and the explanations!Unstick
J
0

I'd extend Array's prototype to make it simple to access:

Array.prototype.range = function(start, end) {
    if (!this.length) {
        while (end >= start) {
            this.push(start++);
        }
    } else {
        throw "You can only call 'range' on an empty array";
    }
    return this;
};

var array = [].range(1, 20);

While the above is the nearest I can think of with respect to the syntactic sugar you're looking for, you may want to try out CoffeeScript.

It supports the notation you're after.

CoffeeScript:

test = [1..20]

alert test

Renders to JavaScript:

var test;
test = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
alert(test);

You can try out live examples on their site, and see the conversion it does as you type.

Just click the TRY COFFEESCRIPT link at the top, and you'll get a console where you can test some code.

Jourdain answered 9/6, 2011 at 21:45 Comment(6)
and all's good until your js needs to cooperate with some 3rd party library that also likes to extend built-in objects....Monovalent
Doesn't bring a new algorithm, but does allow for interesting and terse usage. I may use that.Glutelin
@rob: If a person uses a library, they should know exactly what it does. I certainly wouldn't throw out the practice of extending objects just because of a possibility of some conflict with a library. If there's a conflict, you simply resolve it. No big deal. At least it shouldn't be for a halfway competent programmer anyway.Jourdain
@artlung: Yeah, it's such a simple process, I don't think you'll find anything too algorithmically mind-blowing. I personally like extending the prototype of native classes (except for Object). It's a great feature of the language, so you might as well enjoy it. :o)Jourdain
Yeah, and JavaScript does not include the, I guess they call it "syntactic sugar" of 1..20, so, it is what it is. Thanks for the feedback!Glutelin
@artlung: Have you checked out CoffeeScript? I'm not big on it, but some people love it. It's all about syntactic sugar. And it appears as though it supports that 1..20 type of range notation.Jourdain
S
0

There's always the IterableInt:

   for (const z of new IterableInt(15)) {
          // 15,14,13....3,2,1
    }

implemented like so:

class IterableInt {


  constructor(val) {
    if (val < 0) {
      throw new Error('cannot iterate over a negative number.')
    }
    this.val = val;
  }

  static create(val){
    return new IterableInt(val);
  }

  [Symbol.iterator]() {

    const self = this;
    return {
      next() {
        const value = self.val--;
        const done = value <= 0;
        return {value, done}
      }
    }
  }
}

to do things like:

  for (const z of new IterableInt(3,15)) {
          // 3,4,5...14,15
    }

go to this gist: https://gist.github.com/ORESoftware/1aca4ae704b355c45702d11c0e245776

Starryeyed answered 22/12, 2022 at 1:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.