javascript - para - ¿Cómo generar un rango de números de 0 an en ES2015 solamente?
random entre 0 y 10 javascript (12)
Aquí hay otra variación que no usa
Array
.
let range = (n, l=[], delta=1) => {
if (n < 0) {
return l
}
else {
l.unshift(n)
return range(n - delta, l)
}
}
Siempre he encontrado la función de
range
que falta en JavaScript, ya que está disponible en Python y otros.
¿Hay alguna forma concisa de generar un rango de números en ES2015?
EDITAR: MI pregunta es diferente del duplicado mencionado ya que es específica de ES2015 y no de ECMASCRIPT-5. También necesito que el rango comience desde 0 y no un número de inicio específico (aunque sería bueno si eso está allí)
Con Delta
Para 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, 8);
even4to10.next().value
//=> 8
even4to10.next().value
//=> 10
even4to10.next().value
//=> undefined
Para mecanografiado
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];
Actualizar
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);
Editar
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);
Entonces, en este caso, sería bueno si el objeto Number se comportara como un objeto Array con el operador spread.
Por ejemplo, el objeto Array utilizado con el operador de propagación:
let foo = [0,1,2,3];
console.log(...foo) // returns 0 1 2 3
Funciona así porque el objeto Array tiene un iterador incorporado.
En nuestro caso, necesitamos un objeto
Number
para tener una funcionalidad similar:
[...3] //should return [0,1,2,3]
Para hacer eso, simplemente podemos crear un iterador de números para ese propósito.
Number.prototype[Symbol.iterator] = function *() {
for(let i = 0; i <= this; i++)
yield i;
}
Ahora es posible crear rangos de 0 a N con el operador de propagación.
[... N] // ahora devuelve 0 ... N array
http://jsfiddle.net/01e4xdv5/4/
Aclamaciones.
Esta función devolverá una secuencia entera.
const integerRange = (start, end, n = start, arr = []) =>
(n === end) ? [...arr, n]
: integerRange(start, end, start < end ? n + 1 : n - 1, [...arr, n]);
$> intSequence(1, 1)
<- Array [ 1 ]
$> intSequence(1, 3)
<- Array(3) [ 1, 2, 3 ]
$> intSequence(3, -3)
<- Array(7) [ 3, 2, 1, 0, -1, -2, -3 ]
Muchas de estas soluciones se basan en la creación de instancias de objetos Array reales, que pueden hacer el trabajo para muchos casos, pero no pueden admitir casos como el
range(Infinity)
.
Podría usar un generador simple para evitar estos problemas y admitir secuencias infinitas:
function* range( start, end, step = 1 ){
if( end === undefined ) [end, start] = [start, 0];
for( let n = start; n < end; n += step ) yield n;
}
Ejemplos:
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 }
Para apoyar el delta
const range = (start, end, delta) => {
return Array.from(
{length: (end - start) / delta}, (v, k) => (k * delta) + start
)
};
Puede usar el operador de propagación en las teclas de una matriz recién creada.
[...Array(n).keys()]
o
Array.from(Array(n).keys())
La sintaxis
Array.from()
es necesaria si se trabaja con TypeScript
Puede usar una función de generador, que crea el rango de manera perezosa solo cuando es necesario:
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()
);
Puede usar una función de generador de orden superior para mapear sobre el generador de
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]
);
Si no tiene miedo, incluso puede generalizar el enfoque del generador para abordar un rango mucho más amplio (juego de palabras):
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]
);
Tenga en cuenta que los generadores / iteradores tienen un estado inherente, es decir, hay un cambio de estado implícito con cada invocación de
next
.
El estado es una bendición mixta.
También encontré una forma más intuitiva usando
Array.from
:
const range = n => Array.from({length: n}, (value, key) => key)
Ahora esta función de
range
devolverá todos los números a partir de 0 a n-1
Una versión modificada del rango para admitir
start
y
end
es:
const range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start);
EDITAR Como lo sugiere @ marco6, puede poner esto como un método estático si se adapta a su caso de uso
Array.range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start);
y úsalo como
Array.range(3, 9)
También puede hacerlo con un forro único con soporte de pasos como este:
((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)
El resultado es
[0, 1, 2, 3, 4, 5, 6 ,7 ,8 ,9]
.
Para los números del 0 al 5
[...Array(5).keys()];
=> [0, 1, 2, 3, 4]
const keys = Array(n).keys();
[...Array.from(keys)].forEach(callback);
en mecanografiado