我一直发现range
JavaScript中缺少该函数,因为它在python和其他版本中可用?ES2015中是否有任何简洁的方法来生成数字范围?
编辑:我的问题与提到的重复问题有所不同,因为它是ES2015而非ECMASCRIPT-5特有的。我还需要范围从0开始,而不是具体的起始数字(尽管如果有的话会很好)
[...Array(5)].map((_,i) => i+1)
我一直发现range
JavaScript中缺少该函数,因为它在python和其他版本中可用?ES2015中是否有任何简洁的方法来生成数字范围?
编辑:我的问题与提到的重复问题有所不同,因为它是ES2015而非ECMASCRIPT-5特有的。我还需要范围从0开始,而不是具体的起始数字(尽管如果有的话会很好)
[...Array(5)].map((_,i) => i+1)
Answers:
您可以在新创建的数组的键上使用散布运算符。
[...Array(n).keys()]
要么
Array.from(Array(n).keys())
该Array.from()
如果与打字稿工作语法是必要的
function range (start, end) { return [...Array(1+end-start).keys()].map(v => start+v) }
Array.from(Array(n).keys())
。
[...Array(n).keys()]
在Typescript中不起作用吗?是否有意偏离其他JS实现?
Array(5).keys().slice()
片段,而slice不是数组迭代器的方法。下面是它不工作的例子typescriptlang.org/play/...
我还发现了一种更直观的方法Array.from
:
const range = n => Array.from({length: n}, (value, key) => key)
现在此range
函数将返回从0到n-1的所有数字
的范围内的修改版本,以支持start
和end
是:
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)
interface ArrayConstructor { range(n: number): number[]; }
Array.range = n => Array.from({length: n}, (value, key) => key);
然后到处都是Array.range(x)...
[ts] Property 'range' does not exist on type 'ArrayConstructor'
。对吗?
与三角洲
对于javascript
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((v, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]
Array(10).fill().map((v, i) => 4 + i * -2);
//=> [4, 2, 0, -2, -4, -6, -8, -10, -12, -14]
[...Array(10)].map((v, i) => 4 + i * 2);
//=> [4, 6, 8, 10, 12, 14, 16, 18, 20, 22]
const range = (from, to, step) =>
Array(~~((to - from) / step) + 1) // '~~' is Alternative for Math.floor()
.fill().map((v, i) => from + i * step);
range(0, 9, 2);
//=> [0, 2, 4, 6, 8]
Array.range = (from, to, step) => Array.from({
length: ~~((to - from) / step) + 1
},
(v, k) => from + k * step
);
Array.range = (from, to, step) => [...Array(~~((to - from) / step) + 1)].map(
(v, k) => from + k * 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
// Or
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
class Range2 {
constructor(to = 0, step = 1, from = 0) {
this[Symbol.iterator] = function*() {
let i = 0,
length = ~~((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]
// Or
const Range2 = function*(to = 0, step = 1, from = 0) {
let i = 0, length = ~~((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
对于打字稿
interface _Iterable extends Iterable < {} > {
length: number;
}
class _Array < T > extends Array < T > {
static range(from: number, to: number, step: number): number[] {
return Array.from(
( < _Iterable > { length: Math.floor((to - from) / step) + 1 }),
(v, k) => from + k * step
);
}
}
_Array.range(0, 9, 1);
//=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
更新资料
class _Array<T> extends Array<T> {
static range(from: number, to: number, step: number): number[] {
return [...Array(~~((to - from) / step) + 1)].map(
(v, k) => from + k * step
);
}
}
_Array.range(0, 9, 1);
编辑
class _Array<T> extends Array<T> {
static range(from: number, to: number, step: number): number[] {
return Array.from(Array(~~((to - from) / step) + 1)).map(
(v, k) => from + k * step
);
}
}
_Array.range(0, 9, 1);
Array.from(Array(~~((to - from) / step) + 1).keys())
这些解决方案中的许多解决方案都基于实例化实际的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 }
因此,在这种情况下,如果Number对象的行为类似于使用spread运算符的Array对象,那就更好了。
例如,与传播运算符一起使用的Array对象:
let foo = [0,1,2,3];
console.log(...foo) // returns 0 1 2 3
之所以这样工作,是因为Array对象具有内置的迭代器。
在我们的例子中,我们需要一个Number对象来具有类似的功能:
[...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/
干杯。
您可以使用生成器函数,该函数仅在需要时才懒惰地创建范围:
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
。国家是喜忧参半。
步骤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]
您也可以使用一种带有阶梯支撑的衬板来完成此操作,如下所示:
((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]
。
此函数将返回整数序列。
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,但我希望很多Typescript用户最终会来到这里,并且转换为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中提供更多有用的完成建议。