web-dev-qa-db-fra.com

Comment générer une plage de nombres de 0 à n dans ES2015 uniquement?

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à)

89
Aditya Singh

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.

188
Delapouite

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)
82
Aditya Singh

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);
10
nkitku

Pour les nombres 0 à 5

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

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 }
4
Iron Savior

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é.

2
Getriax

Soutenir le delta

const range = (start, end, delta) => {
  return Array.from(
    {length: (end - start) / delta}, (v, k) => (k * delta) + start
  )
};
2
user3500066

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].

1
Marcin Król

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.

1
user10675354
const keys = Array(n).keys();
[...Array.from(keys)].forEach(callback);

en TypeScript

0
PeiSong

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 ]
0
Zack

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) 
  }
}
0
Han Lazarus