171 votes

Vérification si quelque chose est itérable

Dans les documents MDN : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of

La construction for...of est décrite comme capable d'itérer sur des objets "itérables". Mais y a-t-il un bon moyen de décider si un objet est itérable ?

J'ai essayé de trouver des propriétés communes pour les tableaux, les itérateurs et les générateurs, mais n'ai pas réussi à le faire.

En dehors de faire un for ... of dans un bloc try et de vérifier les erreurs de type, y a-t-il un moyen propre de le faire ?

237voto

Tomas Kulich Points 4364

La bonne manière de vérifier l'itérabilité est la suivante :

function isIterable(obj) {
  // vérifie les valeurs nulles et indéfinies
  if (obj == null) {
    return false;
  }
  return typeof obj[Symbol.iterator] === 'function';
}

Pourquoi cela fonctionne (protocole d'itération en profondeur) : https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols

Étant donné que nous parlons de for..of, je suppose que nous sommes dans l'esprit ES6.

De plus, ne soyez pas surpris si cette fonction retourne true si obj est une chaîne de caractères, car les chaînes itèrent sur leurs caractères.

63voto

Jacque Goupil Points 1
if (Symbol.iterator in Object(value)) {

}

Object encapsulera tout ce qui n'est pas un objet en un objet. Cela empêche l'opérateur in de déclencher des exceptions et élimine le besoin de vérifier des cas particuliers. null et undefined deviennent des objets vides, et la condition s'évalue à true pour une chaîne de caractères, un tableau, une Map, un Set, et ainsi de suite.

Alternativement, dans un environnement qui supporte le chaining optionnel:

if (value?.[Symbol.iterator]) {

}

La syntaxe ?. fait en sorte que null et undefined soient ignorés, et l'accès aux propriétés est sécurisé pour tous les autres types de valeur. Cela peut ne pas être lisible comme de l'anglais, mais vérifier la véracité d'une propriété pour en affirmer l'existence est assez idiomatique.

Je recommanderais personnellement d'enregistrer ce code au lieu de définir une fonction, car il est relativement court et moins ambigu qu'un appel à une fonction isIterable(x).


Gardez à l'esprit que le mot itérable fait spécifiquement référence au protocole itérable, même s'il existe différentes manières de boucler sur des objets. La liste suivante n'est pas exhaustive :

32voto

adius Points 533

Pourquoi si verbeux?

const isIterable = objet =>
  objet != null && typeof objet[Symbol.iterator] === 'function'

16voto

dotancohen Points 4657

Réponse 2022

Si vous vous demandez "Est-ce que foo est itérable" alors vous venez probablement d'un langage (PHP, Python) où cette question a une seule réponse. Dans le Javascript moderne, il existe différents types d'itérables. Par conséquent, vous devez vérifier la capacité à itérer en fonction de ce que vous voulez faire avec la variable.

TL;DR

  • Testez la capacité à itérer en utilisant forEach() avec !!foo.forEach, retourne true sur un Array.
  • Testez la capacité à itérer en utilisant for..of avec !!foo[Symbol.iterator], retourne true sur un Array ou une chaîne de caractères.
  • Testez la capacité à itérer en utilisant for..in avec !!Object.keys(Object(foo)).length, retourne true sur un Array, une chaîne de caractères ou un Object.

Réponse longue

Définissons quelques variables :

const someNumber = 42;
42

const someArray = [1,2,3];
(3) [1, 2, 3]

const someString = "Hello";
"Hello, world!"

const someObject = {a:"A", b:"B"};
{a: "A", b: "B"}

Test de l'itérabilité avec forEach()

Quels types peuvent être itérés avec forEach(), testés avec !!foo.forEach :

someNumber.forEach(x=>console.log(x));
VM1526:1 Uncaught TypeError: someNumber.forEach n'est pas une fonction à :1:12

someArray.forEach(x=>console.log(x));
VM916:1 1
VM916:1 2
VM916:1 3
undefined

someString.forEach(x=>console.log(x));
VM957:1 Uncaught TypeError: someString.forEach n'est pas une fonction à :1:12

someObject.forEach(x=>console.log(x));
VM994:1 Uncaught TypeError: someObject.forEach n'est pas une fonction à :1:12

Seul le Array semble être itérable avec forEach().

Test de l'itérabilité avec for..of

Quels types peuvent être itérés avec for..of, testés avec !!foo[Symbol.iterator] :

for (x of someNumber) { console.log(x); }
VM21027:1 Uncaught TypeError: someNumber n'est pas itérable à :1:11

for (x of someArray) { console.log(x); }
VM21047:1 1
VM21047:1 2
VM21047:1 3
undefined

for (x of someString) { console.log(x); }
VM21065:1 H
VM21065:1 e
VM21065:1 l
VM21065:1 l
VM21065:1 o
undefined

for (x of someObject) { console.log(x); }
VM21085:1 Uncaught TypeError: someObject n'est pas itérable à :1:11

​Le Array et la chaîne de caractères semblent être itérables avec for..of, mais pas l'Object. Et à la fois le Nombre et l'Object ont généré une erreur.

Test de l'itérabilité avec for..in

Quels types peuvent être itérés avec for..in, testés avec !!Object.keys(Object(foo)).length :

for (x in someNumber) { console.log(x); }
undefined

for (x in someArray) { console.log(x); }
VM20918:1 0
VM20918:1 1
VM20918:1 2
undefined

for (x in someString) { console.log(x); }
VM20945:1 0
VM20945:1 1
VM20945:1 2
VM20945:1 3
VM20945:1 4
undefined

for (x in someObject) { console.log(x); }
VM20972:1 a
VM20972:1 b
undefined

​Le Array, la chaîne de caractères et l'Object semblent tous être itérables avec for..in. Et bien qu'il n'ait pas itéré, le Nombre n'a pas généré d'erreur.

Dans le Javascript moderne, ES6, je vois forEach utilisé beaucoup plus souvent que for..in ou for..of. Mais les développeurs Javascript doivent être conscients des différences entre les trois approches, et du comportement différent de chaque approche.

11voto

Francesco Casula Points 2508

En passant, ATTENTION à la définition d'itérable. Si vous venez d'autres langages, vous vous attendriez à ce qu'un objet que vous pouvez itérer avec, disons, une boucle for est itérable. Je crains que ce ne soit pas le cas ici, où itérable signifie quelque chose qui implémente le protocole d'itération.

Pour rendre les choses plus claires, tous les exemples ci-dessus retournent false sur cet objet {a: 1, b: 2} car cet objet n'implémente pas le protocole d'itération. Vous ne pourrez donc pas itérer dessus avec un for...of MAIS vous le pouvez toujours avec un for...in.

Donc, si vous voulez éviter des erreurs douloureuses, rendez votre code plus spécifique en renommant votre méthode comme indiqué ci-dessous:

/**
 * @param variable
 * @returns {boolean}
 */
const hasIterationProtocol = variable =>
    variable !== null && Symbol.iterator in Object(variable);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X