J'ai toujours trouvé la fonction range
manquante dans JavaScript car elle est disponible dans python et autres? Existe-t-il un moyen concis de générer une plage de nombres dans ES2015?
EDIT: Ma question est différente de la copie mentionnée car elle est spécifique à ES2015 et non à ECMASCRIPT-5. De plus, j'ai besoin que la plage commence à 0 et non à un numéro de départ spécifique (bien que ce serait bien si c'est là)
Vous pouvez utiliser l'opérateur spread sur les clés d'un tableau nouvellement créé.
[...Array(n).keys()]
ou
Array.from(Array(n).keys())
La syntaxe Array.from()
est nécessaire si vous utilisez TypeScript.
J'ai aussi trouvé un moyen plus intuitif d'utiliser Array.from
:
const range = n => Array.from({length: n}, (value, key) => key)
Maintenant, cette fonction range
renverra tous les nombres de 0 à n-1
Une version modifiée de la gamme pour prendre en charge start
et end
est la suivante:
const range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start);
EDIT Comme suggéré par @ marco6, vous pouvez utiliser cette méthode comme méthode statique si cela convient à votre cas d'utilisation.
Array.range = (start, end) => Array.from({length: (end - start)}, (v, k) => k + start);
et l'utiliser comme
Array.range(3, 9)
Avec Delta
Pour 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
Pour TypeScript
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];
Mise à jour
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);
Modifier
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);
Pour les nombres 0 à 5
[...Array(5).keys()];
=> [0, 1, 2, 3, 4]
Un grand nombre de ces solutions reposent sur l'instanciation d'objets Array réels, ce qui peut permettre d'accomplir la tâche dans de nombreux cas, mais ne peut pas prendre en charge des cas tels que range(Infinity)
. Vous pouvez utiliser un générateur simple pour éviter ces problèmes et prendre en charge des séquences infinies:
function* range( start, end, step = 1 ){
if( end === undefined ) [end, start] = [start, 0];
for( let n = start; n < end; n += step ) yield n;
}
Exemples:
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 }
Donc, dans ce cas, ce serait bien si l'objet Number se comportait comme un objet Array avec l'opérateur spread.
Par exemple Objet Array utilisé avec l'opérateur spread:
let foo = [0,1,2,3];
console.log(...foo) // returns 0 1 2 3
Cela fonctionne comme ceci parce que l'objet Array a un itérateur intégré.
Dans notre cas, nous avons besoin d’un objet Number pour avoir une fonctionnalité similaire:
[...3] //should return [0,1,2,3]
Pour ce faire, nous pouvons simplement créer un itérateur de nombre à cette fin.
Number.prototype[Symbol.iterator] = function *() {
for(let i = 0; i <= this; i++)
yield i;
}
Il est maintenant possible de créer des plages de 0 à N avec l'opérateur spread.
[... N] // retourne maintenant 0 ... N tableau
http://jsfiddle.net/01e4xdv5/4/
À votre santé.
Soutenir le delta
const range = (start, end, delta) => {
return Array.from(
{length: (end - start) / delta}, (v, k) => (k * delta) + start
)
};
Vous pouvez également le faire avec une seule doublure avec un support comme celui-ci:
((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)
Le résultat est [0, 1, 2, 3, 4, 5, 6 ,7 ,8 ,9]
.
Vous pouvez utiliser une fonction de générateur, qui crée la plage paresseusement uniquement lorsque cela est nécessaire:
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()
);
Vous pouvez utiliser une fonction de générateur d'ordre supérieur pour mapper sur le générateur 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 vous êtes courageux, vous pouvez même généraliser l’approche du générateur pour s’adresser à une plage beaucoup plus large (jeu de mots):
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]
);
Gardez à l'esprit que les générateurs/itérateurs sont par nature des états, c'est-à-dire qu'il y a un changement d'état implicite à chaque invocation de next
. L'état est une bénédiction mitigée.
const keys = Array(n).keys();
[...Array.from(keys)].forEach(callback);
en TypeScript
Cette fonction renverra une séquence entière.
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 ]
Voici une autre variante qui n'utilise pas Array
.
let range = (n, l=[], delta=1) => {
if (n < 0) {
return l
}
else {
l.unshift(n)
return range(n - delta, l)
}
}