Obtenir la prochaine paire clé-valeur dans un objet

Compte tenu d'une clé, je souhaite trouver la propriété suivante dans un objet. Je ne peux pas compter sur les clés à commander ou séquentielles (elles sont uuids). Veuillez voir ci-dessous un exemple trivial de ce que je veux:

var db = { a: 1, b: 2, c: 3 } var next = function(db, key) { // ??? } next(db, 'a'); // I want 2 next(db, 'b'); // I want 3 

Je veux aussi une fonction prev (), mais je suis sûr que ce sera la même solution.

Cela semble être un problème aussi trivial, mais je ne peux pas pour la vie de moi comprendre comment le faire.

Heureux pour la solution d'utiliser underscore.js ou être écrit en coffeescript 🙂

La réponse correcte est: vous ne pouvez pas le faire, car les objets sont désordonnés selon les spécifications d'ECMAScript .

Je vous recommanderais d'utiliser une structure ordonnée, comme un tableau, aux fins du problème:

 var db = [ {key: 'a', value: 1}, {key: 'b', value: 2}, {key: 'c', value: 3} ]; 

Ensuite, la fonction next peut être comme:

 var next = function(db, key) { for (var i = 0; i < db.length; i++) { if (db[i].key === key) { return db[i + 1] && db[i + 1].value; } } }; 

Dans le cas où la key n'existe pas sur db ou elle était la dernière, next retours next undefined . Si vous ne demandez jamais le prochain élément, vous pouvez simplifier cette fonction en supprimant l'opérateur ternaire && et en retournant la valeur db[i + 1].value . Directement.

Vous pouvez également utiliser certaines des méthodes d'utilité Underscore.js pour simplifier ensuite:

 var next = function(db, key) { var i = _.pluck(db, 'key').indexOf(key); return i !== -1 && db[i + 1] && db[i + 1].value; }; 

(Dans ce cas, le next pourrait renvoyer parfois false … mais c'est toujours une valeur faussement :))


Maintenant, une réponse plus pragmatique pourrait être que, comme la plupart des navigateurs respecteront l'ordre dans lequel un objet a été initialisé lors de l'itération, vous pouvez simplement l'itérer avec un for in boucle comme le suggèrent les autres réponses. Je recommanderais d'utiliser Object.keys pour simplifier le travail d'itération sur le tableau:

 // Assuming that db is an object as defined in the question. var next = function(db, key) { var keys = Object.keys(db) , i = keys.indexOf(key); return i !== -1 && keys[i + 1] && db[keys[i + 1]]; }; 

Une solution immédiate à cela serait de stocker des données dans un tableau et d'utiliser l'objet pour simplement stocker l'index dans le tableau auquel un objet existe.

 var db = { data: [1, 2, 3], index: { a: 0, b: 1, c: 2 } }; function next(db, key) { var next = db.index[key] + 1; if (next >= db.data.length) { return null; } return db.data[next]; } function prev(db, key) { var next = db.index[key] - 1; if (next < 0) { return null; } return db.data[next]; } function add(db, key, value) { db.index[key] = db.data.push(value) - 1; } function remove(db, key) { var index = db.index[key], x, temp; if (index !== undefined) { delete db.index[key]; db.data.splice(index, 1); // Update indices of any elements after the removed element for (x in db.index) { temp = db.index[x]; if (temp > index) { db.index[x] = temp - 1; } } } } 

L'idée de base est d'utiliser une structure ordonnée, dans ce cas le tableau, pour contenir les données de façon séquentielle. Dans ce cas, next et prev sont à la fois temps constant, l'ajout est amorti à temps constant et la suppression est O (N).

La commande des clés n'est pas garantie par la norme ECMA, donc for/in n'a pas besoin d'être dans l'ordre, les touches ont été ajoutées (bien que, dans la pratique, cela ait tendance à être la mise en œuvre commune). Dans cette solution, j'utilise un tableau pour suivre explicitement l'ordre des inserts.

Edit: J'ai oublié un problème de suppression plus tôt avec l'épissage. L'index devient incorrect pour toutes les valeurs après la valeur épissée pour une suppression. La correction n'affecte pas la complexité du temps de fonctionnement de l'opération. Une version plus rapide avec moins de suppression permet au tableau de devenir clairsemé et, au lieu d'épisser, il suffit de définir l'index à nul pour libérer toute référence stockée là-bas. Cela réduirait l'opération de suppression sur O (1).

 function remove(db, key) { var index = db.index[key]; if (index !== undefined) { delete db.index[key]; db.data[index] = null; } } 
 function next(db, key){ var found = 0; for(var k in db){ if(found){ return db[k]; } if(k == key){ found = 1; } } } 

En utilisant undercore.js, vous pouvez prendre les clés d'un objet et faire le tour. Mais je ne sais pas si les paires de valeurs-clés sont commandées d'une façon ou d'une autre:

 var next = function(db, key) { var keys = _.keys(db); var index = _.indexOf(keys, key); if(index+1<keys.length){ return db[keys[index+1]]; }else{ return null; } } 

JsFiddle: http://jsfiddle.net/QWhN2/