我正在寻找以下所有替代方法,以创建一个包含1到N的JavaScript数组,其中N仅在运行时才知道。
var foo = [];
for (var i = 1; i <= N; i++) {
foo.push(i);
}
在我看来,应该有一种没有循环的方法。
1 ... N
吗?
我正在寻找以下所有替代方法,以创建一个包含1到N的JavaScript数组,其中N仅在运行时才知道。
var foo = [];
for (var i = 1; i <= N; i++) {
foo.push(i);
}
在我看来,应该有一种没有循环的方法。
1 ... N
吗?
Answers:
如果我能得到想要的结果,则需要一个数字数组,1..n
以后可以循环使用。
如果这是您所需要的,您可以代替吗?
var foo = new Array(45); // create an empty array with length 45
然后在您要使用它时...(未优化,例如)
for(var i = 0; i < foo.length; i++){
document.write('Item: ' + (i + 1) + ' of ' + foo.length + '<br/>');
}
例如,如果您不需要在数组中存储任何内容,则只需要一个长度合适的容器即可进行迭代……这可能会更容易。
在此处查看其运行情况:http : //jsfiddle.net/3kcvm/
var n = 45;
然后循环1..n
就可以了。
foo.length = M
---截止信息将丢失。观看操作==> jsfiddle.net/ACMXp
var n = 45;
。
new Array(45);
不会“创建45个元素的数组”(含义与之相同[undefined,undefined,..undefined]
)。而是“与创建长度为45的空数组”([undefined x 45]
)相同var foo = []; foo.length=45;
。这就是原因forEach
,并且map
在这种情况下将不适用。
在ES6中,使用Array from()和keys()方法。
Array.from(Array(10).keys())
//=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
使用传播算子的较短版本。
[...Array(10).keys()]
//=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
map
到数组以调整值([...Array(10).keys()].map(x => x++);
),以从1开始
map(x => x++)
,map(x => ++x)
因为返回值之后会发生:)
map
当您可以简单地slice
? [...Array(N+1).keys()].slice(1)
keys
,只有一张地图->Array.from(Array(10)).map((e,i)=>i+1)
from
Array.from(Array(10), (e,i)=>i+1)
您可以这样做:
var N = 10;
Array.apply(null, {length: N}).map(Number.call, Number)
结果:[0、1、2、3、4、5、6、7、8、9]
或具有随机值:
Array.apply(null, {length: N}).map(Function.call, Math.random)
结果:[0.7082694901619107、0.9572225909214467、0.8586748542729765、0.8653848143294454、0.008339877473190427、0.9911756622605026、0.8133423360995948、0.8377588465809822、0.5577575915958732、0.16363654541783035]
首先,请注意Number.call(undefined, N)
等于Number(N)
,它只是返回N
。稍后我们将使用该事实。
Array.apply(null, [undefined, undefined, undefined])
等价于Array(undefined, undefined, undefined)
,产生一个三元素数组并分配undefined
给每个元素。
您如何将其概括为N个元素?考虑一下如何Array()
工作,如下所示:
function Array() {
if ( arguments.length == 1 &&
'number' === typeof arguments[0] &&
arguments[0] >= 0 && arguments &&
arguments[0] < 1 << 32 ) {
return [ … ]; // array of length arguments[0], generated by native code
}
var a = [];
for (var i = 0; i < arguments.length; i++) {
a.push(arguments[i]);
}
return a;
}
从ECMAScript 5开始,Function.prototype.apply(thisArg, argsArray)
还接受了鸭子类型的类似数组的对象作为其第二个参数。如果我们调用Array.apply(null, { length: N })
,它将执行
function Array() {
var a = [];
for (var i = 0; i < /* arguments.length = */ N; i++) {
a.push(/* arguments[i] = */ undefined);
}
return a;
}
现在我们有了一个N元素数组,每个元素都设置为undefined
。当我们调用.map(callback, thisArg)
它时,每个元素都将设置为的结果callback.call(thisArg, element, index, array)
。因此,[undefined, undefined, …, undefined].map(Number.call, Number)
将图中的每个元件(Number.call).call(Number, undefined, index, array)
,其是相同的Number.call(undefined, index, array)
,其中,因为我们观察到更早,计算结果为index
。这样就完成了其元素与其索引相同的数组。
为什么要麻烦Array.apply(null, {length: N})
而不是公正Array(N)
?毕竟,这两个表达式都将导致一个由N个元素组成的未定义元素数组。不同之处在于,在前一个表达式中,每个元素都显式设置为undefined,而在后一个表达式中,从未设置每个元素。根据以下文档.map()
:
callback
仅针对已分配值的数组索引调用;对于已删除或从未分配值的索引,不会调用它。
因此,Array(N)
不足;Array(N).map(Number.call, Number)
将导致长度为N的未初始化数组。
由于此技术依赖Function.prototype.apply()
于ECMAScript 5 中指定的行为,因此在ECMAScript 5 之前的浏览器(例如Chrome 14和Internet Explorer 9)中将无法使用。
Function.prototype.call
第一个参数是this
直接在Array.prototype.map
迭代器参数上进行映射的对象这一事实具有一定的精髓。
map
我认为,这里真正重要的见解是对未分配值的特质。另一个版本(可能更长一些,但可能更清晰)是: Array.apply(null, { length: N }).map(function(element, index) { return index; })
Array.apply(null, new Array(N)).map(function(_,i) { return i; })
或者,对于es6和arrow函数,甚至更短: Array.apply(null, new Array(N)).map((_,i) => i)
...
)和密钥方法[ ...Array(N).keys() ].map( i => i+1);
Array(N).fill().map((_, i) => i+1);
Array.from(Array(N), (_, i) => i+1)
{ length: N }
黑客Array.from({ length: N }, (_, i) => i+1)
所有上述形式通过改变初始化为几乎任何期望的值可以产生阵列i+1
所需表达(例如i*2
,-i
,1+i*2
,i%2
和等等)。如果表达式可以由某些函数表示,f
则第一种形式将变得简单
[ ...Array(N).keys() ].map(f)
Array.from({length: 5}, (v, k) => k+1);
// [1,2,3,4,5]
由于数组是undefined
在每个位置初始化的,因此的值v
将为undefined
f
即[ ...Array(N).keys() ].map((i) => f(i))
甚至更简单
[ ...Array(N).keys() ].map(f)
k++
,请使用++k
。
数组天生地管理它们的长度。在遍历它们时,它们的索引可以保存在内存中并在那一点被引用。如果需要知道随机索引,则indexOf
可以使用该方法。
这就是说,为了您的需要,您可能只想声明一个特定大小的数组:
var foo = new Array(N); // where N is a positive integer
/* this will create an array of size, N, primarily for memory allocation,
but does not create any defined values
foo.length // size of Array
foo[ Math.floor(foo.length/2) ] = 'value' // places value in the middle of the array
*/
利用散布运算符(...
)和keys
方法,您可以创建大小为N的临时数组以生成索引,然后创建一个可以分配给变量的新数组:
var foo = [ ...Array(N).keys() ];
您可以先创建所需数组的大小,将其填充为undefined,然后使用创建一个新数组map
,该数组将每个元素设置为索引。
var foo = Array(N).fill().map((v,i)=>i);
这应该初始化为大小为N的长度,并在一次通过中填充数组。
Array.from({ length: N }, (v, i) => i)
在上面的示例中,如果您真的想从1..N中捕获值,则可以代替注释和混乱,有两个选项:
++i
)。 在不使用索引的情况下(可能是更有效的方法)是创建数组,但使N表示N + 1,然后移到最前面。
因此,如果您需要100个数字:
let arr; (arr=[ ...Array(101).keys() ]).shift()
在ES6中,您可以执行以下操作:
Array(N).fill().map((e,i)=>i+1);
http://jsbin.com/molabiluwa/edit?js,控制台
编辑:更改Array(45)
为,Array(N)
因为您已经更新了问题。
console.log(
Array(45).fill(0).map((e,i)=>i+1)
);
.join.split
版本-但我仍然认为不起眼的循环更好。
map
它将很快成为此类事情的标准。
const gen = N => [...(function*(){let i=0;while(i<N)yield i++})()]
.fill()
有必要。我看到是在对节点的repl进行测试时,但是由于Array(1)[0] === undefined
,对Array(1).fill(undefined)
make中的fill()的调用有何不同?
使用非常流行的Underscore _.range方法
// _.range([start], stop, [step])
_.range(10); // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
_.range(1, 11); // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
_.range(0, 30, 5); // => [0, 5, 10, 15, 20, 25]
_.range(0, -10, -1); // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
_.range(0); // => []
function range(start, end) {
var foo = [];
for (var i = start; i <= end; i++) {
foo.push(i);
}
return foo;
}
然后由
var foo = range(1, 5);
在Javascript中没有内置的方法可以执行此操作,但是如果您需要多次执行此操作,则可以创建一个非常有效的实用程序函数。
编辑:我认为,以下是更好的范围函数。也许只是因为我对LINQ有偏见,但我认为它在更多情况下更有用。你的旅费可能会改变。
function range(start, count) {
if(arguments.length == 1) {
count = start;
start = 0;
}
var foo = [];
for (var i = 0; i < count; i++) {
foo.push(start + i);
}
return foo;
}
Array
,而不是Array.prototype
因为没有任何理由(甚至可能会被认为是相当愚蠢的)有每个数组此方法。
Array.range(1, 5)
可能会更合适,但是写作有点酷[].range(1, 5)
。
foo = [1, 2, 3]; bar = foo.range(0, 10);
。但这只是...令人困惑。bar = Array.range(0, 10)
更清晰明了。该范围与实例无关,因此没有理由使其成为实例方法。
填写Array
v8 的最快方法是:
[...Array(5)].map((_,i) => i);
结果将是: [0, 1, 2, 3, 4]
这个问题有很多复杂的答案,但是很简单:
[...Array(255).keys()].map(x => x + 1)
此外,尽管上面的内容简短(简洁),但我认为以下内容会更快(最大长度为:
127,Int8,
255,Uint8,
32,767,Int16,
65,535,Uint16,
2,147,483,647,Int32,
4,294,967,295,Uint32。
(基于最大整数值),还有更多关于Typed Arrays的信息:
(new Uint8Array(255)).map(($,i) => i + 1);
尽管此解决方案也不是很理想,但是它创建了两个数组,并使用了额外的变量声明“ $”(不确定使用此方法解决该问题的任何方法)。我认为以下解决方案是绝对最快的方法:
for(var i = 0, arr = new Uint8Array(255); i < arr.length; i++) arr[i] = i + 1;
在执行此语句后,您可以随时在当前作用域中简单地使用变量“ arr”。
如果您想用它做一个简单的功能(通过一些基本的验证):
function range(min, max) {
min = min && min.constructor == Number ? min : 0;
!(max && max.constructor == Number && max > min) && // boolean statements can also be used with void return types, like a one-line if statement.
((max = min) & (min = 0)); //if there is a "max" argument specified, then first check if its a number and if its graeter than min: if so, stay the same; if not, then consider it as if there is no "max" in the first place, and "max" becomes "min" (and min becomes 0 by default)
for(var i = 0, arr = new (
max < 128 ? Int8Array :
max < 256 ? Uint8Array :
max < 32768 ? Int16Array :
max < 65536 ? Uint16Array :
max < 2147483648 ? Int32Array :
max < 4294967296 ? Uint32Array :
Array
)(max - min); i < arr.length; i++) arr[i] = i + min;
return arr;
}
//and you can loop through it easily using array methods if you want
range(1,11).forEach(x => console.log(x));
//or if you're used to pythons `for...in` you can do a similar thing with `for...of` if you want the individual values:
for(i of range(2020,2025)) console.log(i);
//or if you really want to use `for..in`, you can, but then you will only be accessing the keys:
for(k in range(25,30)) console.log(k);
console.log(
range(1,128).constructor.name,
range(200).constructor.name,
range(400,900).constructor.name,
range(33333).constructor.name,
range(823, 100000).constructor.name,
range(10,4) // when the "min" argument is greater than the "max", then it just considers it as if there is no "max", and the new max becomes "min", and "min" becomes 0, as if "max" was never even written
);
因此,使用上述功能,上述超慢的“简单单缸”将变得超快,甚至更短:
range(1,14000);
fill
方法的现代javascript :Array(255).fill(0,0,255)
您可以使用此:
new Array(/*any number which you want*/)
.join().split(',')
.map(function(item, index){ return ++index;})
例如
new Array(10)
.join().split(',')
.map(function(item, index){ return ++index;})
将创建以下数组:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
new Array(10).join().split(',').map(function() {return ++arguments[1]});
呢?
join
,一次split
,到一次,以及一次您实际要执行的操作)不是很好-我知道它们似乎已经失宠了出于某种原因,但是简单地使用一个好的老式循环会更好!
ES6可以达到目的:
[...Array(12).keys()]
查看结果:
[...Array(12).keys()].map(number => console.log(number))
[...Array(N + 1).keys()].slice(1)
将产生1..N
[...Array(N + 1).keys()]
这段代码返回一个空数组,但是只是在生产模式下,使用开发模式才有效。
.keys()
答案已经给出年前,有500多个upvotes。您的答案有什么好处?
这可能是生成数字数组的最快方法
最短的
var a=[],b=N;while(b--)a[b]=b+1;
排队
var arr=(function(a,b){while(a--)b[a]=a;return b})(10,[]);
//arr=[0,1,2,3,4,5,6,7,8,9]
如果要从1开始
var arr=(function(a,b){while(a--)b[a]=a+1;return b})(10,[]);
//arr=[1,2,3,4,5,6,7,8,9,10]
需要功能吗?
function range(a,b,c){c=[];while(a--)c[a]=a+b;return c}; //length,start,placeholder
var arr=range(10,5);
//arr=[5,6,7,8,9,10,11,12,13,14]
为什么?
while
是最快的循环
直接设置比 push
[]
比 new Array(10)
简短...看第一个代码。然后在这里查看所有其他功能。
如果你喜欢生活中不可缺少的
for(var a=[],b=7;b>0;a[--b]=b+1); //a=[1,2,3,4,5,6,7]
要么
for(var a=[],b=7;b--;a[b]=b+1); //a=[1,2,3,4,5,6,7]
console.time()
可以凭直觉来运行,但是作为证明,您需要jsperf.com,它显示了其他人(不同硬件等)的跨浏览器结果
var a=[],b=N;while(b--){a[b]=a+1};
如果使用lodash,则可以使用_.range:
_.range([start=0], end, [step=1])
创建从开始到结束但不包括结束的数字数组(正数和/或负数)。如果指定了负开始而没有结束或步长,则使用-1步长。如果未指定end,则将其设置为从start开始,然后设置为0。
例子:
_.range(4);
// ➜ [0, 1, 2, 3]
_.range(-4);
// ➜ [0, -1, -2, -3]
_.range(1, 5);
// ➜ [1, 2, 3, 4]
_.range(0, 20, 5);
// ➜ [0, 5, 10, 15]
_.range(0, -4, -1);
// ➜ [0, -1, -2, -3]
_.range(1, 4, 0);
// ➜ [1, 1, 1]
_.range(0);
// ➜ []
使用ES6,您可以执行以下操作:
// `n` is the size you want to initialize your array
// `null` is what the array will be filled with (can be any other value)
Array(n).fill(null)
map
因此forEach
可以正常工作。
最终摘要报告.. Drrruummm Rolll-
这是在不使用ES6的情况下生成大小为N(此处为10)的数组的最短代码。上面的Cocco的版本很接近,但不是最短的。
(function(n){for(a=[];n--;a[n]=n+1);return a})(10)
但是,这场Code Golf 的无可争议的获胜者(解决源代码最少字节中的特定问题的竞争者)是Niko Ruotsalainen。使用数组构造器和ES6传播算符。(大多数ES6语法是有效的typeScript,但以下语法不是有效的。因此,在使用它时应谨慎对待)
[...Array(10).keys()]
a
。循环应为for(var a=[];n--;a[n]=n+1)
ES6中还有另一种方法,使用Array.from,它带有 2个参数,第一个是arrayLike(在这种情况下是具有length
属性的对象),第二个是映射函数(在这种情况下,我们将项目映射到其索引)
Array.from({length:10}, (v,i) => i)
这更短,可以用于其他序列,例如生成偶数
Array.from({length:10}, (v,i) => i*2)
而且,与其他大多数方法相比,它具有更好的性能,因为它仅循环通过数组一次。检查代码段以进行一些比较
// open the dev console to see results
count = 100000
console.time("from object")
for (let i = 0; i<count; i++) {
range = Array.from({length:10}, (v,i) => i )
}
console.timeEnd("from object")
console.time("from keys")
for (let i =0; i<count; i++) {
range = Array.from(Array(10).keys())
}
console.timeEnd("from keys")
console.time("apply")
for (let i = 0; i<count; i++) {
range = Array.apply(null, { length: 10 }).map(function(element, index) { return index; })
}
console.timeEnd("apply")
Array.from({length:N}, (v,i) => i+1)
使用=>
ES6标准中的新Array方法和函数语法(撰写本文时仅Firefox)。
通过填充孔undefined
:
Array(N).fill().map((_, i) => i + 1);
Array.from
轮流“孔”进入undefined
这样Array.map
按预期工作:
Array.from(Array(5)).map((_, i) => i + 1)
Array.from({length: N}, (v, k) => k)
。
Array.prototype.map.call
(例如,对于从返回的NodeLists而言),这是一个很好的解决方案document.querySelectorAll
。developer.mozilla.org/en/docs/Web/JavaScript/Reference/...
Array.from
这样会将稀疏值变成未定义。而是Array(5)
将其称为参数对象,该对象又会将稀疏值解释为未定义值:)
Array(...Array(9)).map((_, i) => i);
console.log(Array(...Array(9)).map((_, i) => i))
[...Array(9)].map((_, i) => i)
只是另一个ES6版本。
通过使用Array.from
第二个可选参数:
Array.from(arrayLike [,mapFn [,thisArg]])
我们可以从空Array(10)
位置构建编号数组:
Array.from(Array(10), (_, i) => i)
var arr = Array.from(Array(10), (_, i) => i);
document.write(arr);
[...Array(11).keys()].slice(1)
。
似乎,在这个相当完整的答案列表中,当前唯一没有出现的风味是具有生成器的风味。因此要补救:
const gen = N => [...(function*(){let i=0;while(i<N)yield i++})()]
可以这样使用:
gen(4) // [0,1,2,3]
这样做的好处是,您不必增加就可以...为了从@ igor-shubin提供的答案中汲取灵感,您可以非常轻松地创建随机数组:
const gen = N => [...(function*(){let i=0;
while(i++<N) yield Math.random()
})()]
而不是像这样冗长的运营成本:
const slow = N => new Array(N).join().split(',').map((e,i)=>i*5)
// [0,5,10,15,...]
您可以改为:
const fast = N => [...(function*(){let i=0;while(i++<N)yield i*5})()]
您可以使用Es6中的数组填充和映射;就像一些人在回答这个问题时提出的建议一样。以下是一些示例:
Example-One: Array(10).fill(0).map((e,i)=>i+1)
Result-One: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Example-Two: Array(100/10).fill(0).map((e,i)=>(i*10)+10)
Result-Two:[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
我更喜欢这样做,因为我发现它简单直接。
Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
来源:https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
使用ES6
const generateArray = n => [...Array(n)].map((_, index) => index + 1);
Array.from(Array(n))
如果不支持传播运算符,也可以使用。
Object.keys(Array.apply(0, Array(3))).map(Number)
返回[0, 1, 2]
。与Igor Shubin的出色答案非常相似,但欺骗性略低(一个字符更长)。
Array(3) // [undefined × 3]
生成长度为n = 3的数组。不幸的是,这个数组对我们几乎没有用,所以我们必须…Array.apply(0,Array(3)) // [undefined, undefined, undefined]
使数组可迭代。注意:null最常见的是apply的第一个arg,而0则更短。Object.keys(Array.apply(0,Array(3))) // ['0', '1', '2']
然后获取数组的键(之所以起作用,是因为Arrays是typeof数组是具有键索引的对象。Object.keys(Array.apply(0,Array(3))).map(Number) // [0, 1, 2]
并映射到键,将字符串转换为数字。