如何在 ES2015 中生成从 0 到 n 范围的数字?

我总是发现 JavaScript 中缺少 range函数,因为它可以在 python 和其他程序中使用?在 ES2015 中是否有简洁的方法来生成某个范围内的数字?

编辑: 我的问题是和提到的问题没有重复,因为我的问题限定在 ES2015,而不是 ECMASCRIPT-5。此外,我需要的范围是从 0 开始,而不是具体的起始数字(虽然这将是很好的,如果有)

181591 次浏览

可以对新创建的数组的键使用扩展运算符。

[...Array(n).keys()]

或者

Array.from(Array(n).keys())

如果使用 TypeScript,则必须使用 Array.from()语法

我还发现了一种更直观的使用 Array.from的方法:

const range = n => Array.from({length: n}, (value, key) => key)

现在这个 range函数将返回从0到 n-1的所有数字

支持 startend的范围的修改版本是:

const range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start);

剪辑 正如@marco6所建议的,如果它适合您的用例,您可以将其作为静态方法

Array.range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start);

把它当做

Array.range(3, 9)

支持三角洲部队

const range = (start, end, delta) => {
return Array.from(
{length: (end - start) / delta}, (v, k) => (k * delta) + start
)
};
const keys = Array(n).keys();
[...Array.from(keys)].forEach(callback);

在 Typescript

您还可以使用一个带有步骤支持的一行程序来完成这项工作,如下所示:

((from, to, step) => ((add, arr, v) => add(arr, v, add))((arr, v, add) => v < to ? add(arr.concat([v]), v + step, add) : arr, [], from))(0, 10, 1)

结果是 [0, 1, 2, 3, 4, 5, 6 ,7 ,8 ,9]

这是另一个不使用 Array的变体。

let range = (n, l=[], delta=1) => {
if (n < 0) {
return l
}
else {
l.unshift(n)
return range(n - delta, l)
}
}

还有台阶和三角洲

最小的和一行的

[...Array(N)].map((_, i) => from + i * step);

例子及其他选择

[...Array(10)].map((_, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]


Array.from(Array(10)).map((_, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]


Array.from(Array(10).keys()).map(i => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]


[...Array(10).keys()].map(i => 4 + i * -2);
//=> [4, 2, 0, -2, -4, -6, -8, -10, -12, -14]


Array(10).fill(0).map((_, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]


Array(10).fill().map((_, i) => 4 + i * -2);
//=> [4, 2, 0, -2, -4, -6, -8, -10, -12, -14]

范围函数

const range = (from, to, step) =>
[...Array(Math.floor((to - from) / step) + 1)].map((_, i) => from + i * step);


range(0, 9, 2);
//=> [0, 2, 4, 6, 8]


// can also assign range function as static method in Array class (but not recommended )
Array.range = (from, to, step) =>
[...Array(Math.floor((to - from) / step) + 1)].map((_, i) => from + i * step);


Array.range(2, 10, 2);
//=> [2, 4, 6, 8, 10]


Array.range(0, 10, 1);
//=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]


Array.range(2, 10, -1);
//=> []


Array.range(3, 0, -1);
//=> [3, 2, 1, 0]

作为迭代器

class Range {
constructor(total = 0, step = 1, from = 0) {
this[Symbol.iterator] = function* () {
for (let i = 0; i < total; yield from + i++ * step) {}
};
}
}


[...new Range(5)]; // Five Elements
//=> [0, 1, 2, 3, 4]
[...new Range(5, 2)]; // Five Elements With Step 2
//=> [0, 2, 4, 6, 8]
[...new Range(5, -2, 10)]; // Five Elements With Step -2 From 10
//=>[10, 8, 6, 4, 2]
[...new Range(5, -2, -10)]; // Five Elements With Step -2 From -10
//=> [-10, -12, -14, -16, -18]


// Also works with for..of loop
for (i of new Range(5, -2, 10)) console.log(i);
// 10 8 6 4 2

仅作为发电机

const Range = function* (total = 0, step = 1, from = 0) {
for (let i = 0; i < total; yield from + i++ * step) {}
};


Array.from(Range(5, -2, -10));
//=> [-10, -12, -14, -16, -18]


[...Range(5, -2, -10)]; // Five Elements With Step -2 From -10
//=> [-10, -12, -14, -16, -18]


// Also works with for..of loop
for (i of Range(5, -2, 10)) console.log(i);
// 10 8 6 4 2


// Lazy loaded way
const number0toInf = Range(Infinity);
number0toInf.next().value;
//=> 0
number0toInf.next().value;
//=> 1
// ...

用 step/delta 从-到

使用迭代器

class Range2 {
constructor(to = 0, step = 1, from = 0) {
this[Symbol.iterator] = function* () {
let i = 0,
length = Math.floor((to - from) / step) + 1;
while (i < length) yield from + i++ * step;
};
}
}
[...new Range2(5)]; // First 5 Whole Numbers
//=> [0, 1, 2, 3, 4, 5]


[...new Range2(5, 2)]; // From 0 to 5 with step 2
//=> [0, 2, 4]


[...new Range2(5, -2, 10)]; // From 10 to 5 with step -2
//=> [10, 8, 6]

使用发电机

const Range2 = function* (to = 0, step = 1, from = 0) {
let i = 0,
length = Math.floor((to - from) / step) + 1;
while (i < length) yield from + i++ * step;
};


[...Range2(5, -2, 10)]; // From 10 to 5 with step -2
//=> [10, 8, 6]


let even4to10 = Range2(10, 2, 4);
even4to10.next().value;
//=> 4
even4to10.next().value;
//=> 6
even4to10.next().value;
//=> 8
even4to10.next().value;
//=> 10
even4to10.next().value;
//=> undefined

因此,在这种情况下,如果 号码对象的行为类似于具有传播操作符的 Array 对象,那就太好了。

例如,与传播操作符一起使用的 数组对象:

let foo = [0,1,2,3];
console.log(...foo) // returns 0 1 2 3

它之所以这样工作,是因为 Array 对象具有内置的迭代器。
在我们的示例中,我们需要一个 号码对象来具有类似的功能:

[...3] //should return [0,1,2,3]

为此,我们可以简单地创建 Number 迭代器。

Number.prototype[Symbol.iterator] = function *() {
for(let i = 0; i <= this; i++)
yield i;
}

现在可以使用扩展运算符创建从0到 N 的范围。

[ ... N ]//现在返回0... N 数组

Http://jsfiddle.net/01e4xdv5/4/

干杯。

很多这样的解决方案都是建立在实例化实际 Array 对象的基础上的,实例化实际 Array 对象可以在很多情况下完成工作,但不能支持像 range(Infinity)这样的情况。您可以使用一个简单的生成器来避免这些问题,并支持无限序列:

function* range( start, end, step = 1 ){
if( end === undefined ) [end, start] = [start, 0];
for( let n = start; n < end; n += step ) yield n;
}

例子:

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


i = range(10, Infinity);
i.next(); // { value: 10, done: false }
i.next(); // { value: 11, done: false }
i.next(); // { value: 12, done: false }
i.next(); // { value: 13, done: false }
i.next(); // { value: 14, done: false }

数字0到5

[...Array(5).keys()];
=> [0, 1, 2, 3, 4]

您可以使用生成器函数,该函数仅在需要时才惰性地创建范围:

function* range(x, y) {
while (true) {
if (x <= y)
yield x++;


else
return null;
}
}


const infiniteRange = x =>
range(x, Infinity);
  

console.log(
Array.from(range(1, 10)) // [1,2,3,4,5,6,7,8,9,10]
);


console.log(
infiniteRange(1000000).next()
);

您可以使用高阶生成器函数映射 range生成器:

function* range(x, y) {
while (true) {
if (x <= y)
yield x++;


else
return null;
}
}


const genMap = f => gx => function* (...args) {
for (const x of gx(...args))
yield f(x);
};


const dbl = n => n * 2;


console.log(
Array.from(
genMap(dbl) (range) (1, 10)) // [2,4,6,8,10,12,14,16,18,20]
);

如果你无所畏惧,你甚至可以将生成器方法推广到更广的范围(双关语) :

const rangeBy = (p, f) => function* rangeBy(x) {
while (true) {
if (p(x)) {
yield x;
x = f(x);
}


else
return null;
}
};


const lte = y => x => x <= y;


const inc = n => n + 1;


const dbl = n => n * 2;


console.log(
Array.from(rangeBy(lte(10), inc) (1)) // [1,2,3,4,5,6,7,8,9,10]
);


console.log(
Array.from(rangeBy(lte(256), dbl) (2)) // [2,4,8,16,32,64,128,256]
);

请记住,生成器/迭代器本质上是有状态的,也就是说,每次调用 next都会有一个隐式的状态更改。国家是喜忧参半。

这个函数会返回一个整数数列。

const integerRange = (start, end, n = start, arr = []) =>
(n === end) ? [...arr, n]
: integerRange(start, end, start < end ? n + 1 : n - 1, [...arr, n]);


$> integerRange(1, 1)
<- Array [ 1 ]


$> integerRange(1, 3)
<- Array(3) [ 1, 2, 3 ]


$> integerRange(3, -3)
<- Array(7) [ 3, 2, 1, 0, -1, -2, -3 ]

生成器现在允许您以惰性方式生成数字序列,并对大范围使用更少的内存。

虽然这个问题明确说明了 ES2015,但是我希望许多 Typecript 用户最终会在这里结束,并且向 ES 的转换非常简单..。

function range(end: number): IterableIterator<number>;
// tslint:disable-next-line:unified-signatures
function range(begin: number, end: number): IterableIterator<number>;


function *range(begin: number, end: number = NaN): IterableIterator<number> {
let num = 0;
if (isNaN(end)) {
end = begin;
} else {
num = begin;
}
while (num < end) {
yield num++;
}
}

前两个函数声明只是为了在 IDE 中提供更多信息性的完成建议。

步骤 ES6的范围,工作原理类似于 python list(range(start, stop[, step])):

const range = (start, stop, step = 1) => {
return [...Array(stop - start).keys()]
.filter(i => !(i % Math.round(step)))
.map(v => start + v)
}

例子:

range(0, 8) // [0, 1, 2, 3, 4, 5, 6, 7]
range(4, 9) // [4, 5, 6, 7, 8]
range(4, 9, 2) // [4, 6, 8]
range(4, 9, 3) // [4, 7]

不如直接绘制地图。

数组(n)。Map ((值,索引) ... .)已经完成了80% 。但由于某些奇怪的原因,它不起作用。但是有一个变通办法。

Array(n).map((v,i) => i) // does not work
Array(n).fill().map((v,i) => i) // does dork

一定范围内

Array(end-start+1).fill().map((v,i) => i + start) // gives you a range

奇怪的是,这两个迭代器返回相同的结果: Array(end-start+1).entries()Array(end-start+1).fill().entries()

还有很多方法可以做

// Using `repeat` and `map`
const gen = n => [...'.'.repeat(n)].map((_,i) => i);


console.log('gen ', gen(5));




// Using `repeat` and `split`
const gen2 = n => ' '.repeat(n).split('').map((_,i) => i);


console.log('gen2 ', gen2(5));


// Using `concat` with recursive approach
const gen3 = n => n ? gen3(n-1).concat(n-1) : [];


console.log('gen3 ', gen3(5));




const range = (start, end, step = 1) =>
start > end ? [] : [start].concat(range(start + step, end, step));


console.log('range', range(2, 10,2));