Comment passer à l'élément précédent / suivant d'un tableau

Disons que nous avons une liste d'entiers:

var fibonacci = [1,1,2,3,5,8,13,21]; 

Je veux pouvoir obtenir l'élément suivant et précédent (juste pour déplacer le pointeur de l'élément, sans modifier le tableau) de la manière suivante (exemple, peut-être sans prototype pour redéfinir l'interface Array mais pourquoi pas):

 fibonacci.prev(); // returns false fibonacci.next(); // returns 1 fibonacci.next(); // returns 1 fibonacci.next(); // returns 2 fibonacci.next(); // returns 3 fibonacci.next(); // returns 5 fibonacci.next(); // returns 8 fibonacci.prev(); // returns 5 fibonacci.next(); // returns 8 fibonacci.next(); // returns 13 fibonacci.next(); // returns false 

Si vous souhaitez conserver la liste en tant que Array , vous devrez changer son [[prototype]] pour que cela ressemble à une collection itérative:

 Array.prototype.next = function() { return this[++this.current]; }; Array.prototype.prev = function() { return this[--this.current]; }; Array.prototype.current = 0; 

Maintenant, chaque Array aura les méthodes prev et next , et la propriété current , qui indique les éléments "actuels". Une mise en garde: la propriété current peut être modifiée, ce qui entraîne des résultats imprévisibles.

Post scriptum: je ne recommande pas de rendre le prev et le next retour false lorsque l'index est hors de portée. Si vous voulez vraiment, vous pouvez changer les méthodes à quelque chose comme:

 Array.prototype.next = function() { if (!((this.current + 1) in this)) return false; return this[++this.current]; }; 

MISE À JOUR mi-2016

Je met à jour cette réponse car il semble que cela continue de recevoir des points de vue et des votes. J'aurais dû préciser que la réponse donnée est une preuve de concept et, en général, étendre le prototype des classes autochtones est une mauvaise pratique et devrait être évitée dans les projets de production.

En particulier, ce n'est pas beaucoup parce qu'il va se passer for...in cycles – ce qui devrait toujours être évité pour les tableaux et c'est vraiment une mauvaise pratique pour itérer à travers leurs éléments – et aussi parce que depuis IE9, nous pouvons le faire de manière fiable à la place:

 Object.defineProperty(Array.prototype, "next", { value: function() { return this[++this.current]; }, enumerable: false }); 

Le problème principal est que l'extension des classes autochtones n'est pas à l'épreuve du futur , c'est -à- dire qu'il se peut que ECMA introduise une méthode next pour les tableaux qui seront probablement incompatibles avec votre implémentation. Il est déjà arrivé même avec des frameworks JS très communs – le dernier cas était MooTools ' contains extension de tableau qui a amené ECMA à modifier le nom à includes (mauvaise action, IMO, puisque nous avons déjà dans des objets Element.classList comme Element.classList ).

Cela étant dit, ce n'est pas que vous ne devez pas étendre les prototypes natifs, mais vous devez savoir ce que vous faites. Le premier conseil que je peux vous donner est de choisir des noms qui ne vont pas entrer en conflit avec les futures extensions standard, par exemple myCompanyNext au lieu de tout juste next . Cela vous coûtera un certain code d'élégance, mais vous fera dormir le son.

Encore mieux, dans ce cas, vous pouvez effectivement étendre la classe Array :

 function MyTraversableArray() { if (typeof arguments[0] === "number") this.length = arguments[0]; else this.push.apply(this, arguments); this.current = 0; } MyTraversableArray.prototype = []; MyTraversableArray.prototype.constructor = MyTraversableArray; MyTraversableArray.prototype.next = function() { return this[++this.current]; }; MyTraversableArray.prototype.prev = function() { return this[--this.current]; }; 

Dans ES6, en outre, il est plus facile d'étendre les cours indigènes:

 class MyTraversableArray extends Array { next() { return this[++this.current]; } } 

Hélas, les transpilateurs ont du mal aux extensions de classes indigènes , et Babel a retiré son support. Mais c'est parce qu'ils ne peuvent pas répliquer exactement certains comportements qui n'ont aucune influence dans notre cas, afin que vous puissiez adhérer à l'ancien code ES3 ci-dessus.

Je recommande généralement d'ajouter des choses à Array.prototype raison de la quantité de JavaScript vraiment négatif. Par exemple, si vous définissez Array.protoype.next = function () {} et quelqu'un a le code suivant, il y a un problème:

 var total = 0, i, myArr = [0,1,2]; for(i in myArr) { total += myArr[i]; } total; //is probably "3next" 

Cette mauvaise utilisation des boucles for-in est inquiétante. Donc, vous demandez des problèmes en ajoutant au prototype de Array . Cependant, il est assez facile de créer une enveloppe pour faire ce que vous cherchez à faire:

 var iterifyArr = function (arr) { var cur = 0; arr.next = (function () { return (++cur >= this.length) ? false : this[cur]; }); arr.prev = (function () { return (--cur < 0) ? false : this[cur]; }); return arr; }; var fibonacci = [1, 1, 2, 3, 5, 8, 13]; iterifyArr(fibonacci); fibonacci.prev(); // returns false fibonacci.next(); // returns 1 fibonacci.next(); // returns 1 fibonacci.next(); // returns 2 fibonacci.next(); // returns 3 fibonacci.next(); // returns 5 fibonacci.next(); // returns 8 fibonacci.prev(); // returns 5 fibonacci.next(); // returns 8 fibonacci.next(); // returns 13 fibonacci.next(); // returns false 

Quelques notes:

Tout d'abord, vous voulez probablement que le retour soit undefined au lieu de false si vous dépassez la fin. Deuxièmement, parce que cette méthode se cache à l'aide d'une fermeture, vous n'avez pas accès à elle sur votre tableau. Vous pourriez donc avoir une méthode cur() pour saisir la valeur actuelle:

 //Inside the iterifyArr function: //... arr.cur = (function () { return this[cur]; }); //... 

Enfin, vos exigences ne sont pas claires quant à la mesure dans laquelle le "pointeur" est maintenu jusqu'à la fin. Prenez le code suivant par exemple (en supposant que fibonacci est défini comme ci-dessus):

 fibonacci.prev(); //false fibonacci.prev(); //false fibonacci.next(); //Should this be false or 1? 

Dans mon code, ce serait false , mais vous voudrez peut-être qu'il soit 1 , auquel cas il faudrait apporter quelques modifications simples à mon code.

Oh, et parce que la fonction renvoie arr , vous pouvez "itérer" un tableau sur la même ligne que vous le définissez, de la manière suivante:

 var fibonacci = iterifyArr([1, 1, 2, 3, 5, 8, 13]); 

Cela pourrait vous rendre un peu plus propre. Vous pouvez également réinitialiser l'itérateur en ré-appeler iterifyArr sur votre tableau, ou vous pouvez écrire une méthode pour le réinitialiser assez facilement (il suffit de configurer le cur pour 0).