Comment puis-je mettre en œuvre un tableau avec un indexeur en JavaScript? Est là quelque chose comme un dictionnaire .Net?
Réponses
Trop de publicités?John Resig (auteur de jQuery) posté récemment sur dictionnaire des recherches en javascript.
Sa solution consiste à attribuer le dictionnaire des valeurs des propriétés d'un objet. Code collé mot à mot de l'article ci-dessus:
// The dictionary lookup object
var dict = {};
// Do a jQuery Ajax request for the text dictionary
$.get( "dict/dict.txt", function( txt ) {
// Get an array of all the words
var words = txt.split( "\n" );
// And add them as properties to the dictionary lookup
// This will allow for fast lookups later
for ( var i = 0; i < words.length; i++ ) {
dict[ words[i] ] = true;
}
// The game would start after the dictionary was loaded
// startGame();
});
// Takes in an array of letters and finds the longest
// possible word at the front of the letters
function findWord( letters ) {
// Clone the array for manipulation
var curLetters = letters.slice( 0 ), word = "";
// Make sure the word is at least 3 letters long
while ( curLetters.length > 2 ) {
// Get a word out of the existing letters
word = curLetters.join("");
// And see if it's in the dictionary
if ( dict[ word ] ) {
// If it is, return that word
return word;
}
// Otherwise remove another letter from the end
curLetters.pop();
}
}
Dans mon dernier projet, j'ai été chargé de la création d'un navigateur client application capable de lire les 10 de milliers de lignes de données, puis de les regrouper et de les agréger les données pour l'affichage dans les grilles et de cartographie. La cible ont été les technologies HTML 5, CSS 3 et de l'EMCS 5.(navigateur moderne en Juin 2013). Car les anciens compatibilité du navigateur n'a pas été une préoccupation des bibliothèques externes ont été limitées à D3 (pas de JQuery).
J'ai besoin de construire un modèle de données. J'avais construit un avant de en C# et s'est appuyé sur de dictionnaire des objets d'accéder rapidement aux données, les groupes et agrégats. Je n'avais pas travaillé dans le JavaScript dans les années j'ai donc commencé à chercher un dictionnaire. J'ai trouvé le JavaScript n'ont toujours pas un vrai natif dictionnaire. J'ai trouvé quelques exemples d'implémentations, mais rien qui a vraiment répondu à mes attentes. J'ai donc créé une.
Comme je l'ai mentionné, je n'avais pas travaillé en JavaScript depuis des années. Les avancées (ou peut-être juste le web, la disponibilité de l'information) ont été assez impressionnant. Tout mon travail a été avec des cours en classe de langues, de sorte le prototype de la base de langue a fallu un certain temps pour s'habituer à (et j'ai encore un long chemin à parcourir).
Ce projet, comme la plupart, est due avant de commencer, j'ai donc appris que je suis allé faire beaucoup de newb erreurs qui seraient attendus lors de la transition d'une classe à un prototype de base de la langue. Le dictionnaire a été créé fonctionnel, mais après un certain temps, j'ai réalisé quelques améliorations que j'ai pu faire en le rendant moins newbish. Le projet s'est déroulé de financement avant que j'ai eu le temps de retravailler le dictionnaire. Oh, et ma position perdu de financement en même temps (incroyable de voir comment ce qui peut arriver). Je décide donc de recréer le dictionnaire à l'aide de ce que j'avais appris et de déterminer si le dictionnaire a été effectivement une amélioration des performances sur un tableau.
/*
* Dictionary Factory Object
* Holds common object functions. similar to V-Table
* this.New() used to create new dictionary objects
* Uses Object.defineProperties so won't work on older browsers.
* Browser Compatibility (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties)
* Firefox (Gecko) 4.0 (2), Chrome 5, IE 9, Opera 11.60, Safari 5
*/
function Dict() {
/*
* Create a new Dictionary
*/
this.New = function () {
return new dict();
};
/*
* Return argument f if it is a function otherwise return undefined
*/
function ensureF(f) {
if (isFunct(f)) {
return f;
}
}
function isFunct(f) {
return (typeof f == "function");
}
/*
* Add a "_" as first character just to be sure valid property name
*/
function makeKey(k) {
return "_" + k;
};
/*
* Key Value Pair object - held in array
*/
function newkvp(key, value) {
return {
key: key,
value: value,
toString: function () { return this.key; },
valueOf: function () { return this.key; }
};
};
/*
* Return the current set of keys.
*/
function keys(a) {
// remove the leading "-" character from the keys
return a.map(function (e) { return e.key.substr(1); });
// Alternative: Requires Opera 12 vs. 11.60
// -- Must pass the internal object instead of the array
// -- Still need to remove the leading "-" to return user key values
// Object.keys(o).map(function (e) { return e.key.substr(1); });
};
/*
* Return the current set of values.
*/
function values(a) {
return a.map(function(e) { return e.value; } );
};
/*
* Return the current set of key value pairs.
*/
function kvPs(a) {
// remove the leading "-" character from the keys
return a.map(function (e) { return newkvp(e.key.substr(1), e.value); });
}
/*
* Returns true if key exists in the dictionary.
* k - Key to check (with the leading "_" character)
*/
function exists(k, o) {
return o.hasOwnProperty(k);
}
/*
* Array Map implementation
*/
function map(a, f) {
if (!isFunct(f)) { return; }
return a.map(function (e, i) { return f(e.value, i); });
}
/*
* Array Every implementation
*/
function every(a, f) {
if (!isFunct(f)) { return; }
return a.every(function (e, i) { return f(e.value, i) });
}
/*
* Returns subset of "values" where function "f" returns true for the "value"
*/
function filter(a, f) {
if (!isFunct(f)) {return; }
var ret = a.filter(function (e, i) { return f(e.value, i); });
// if anything returned by array.filter, then get the "values" from the key value pairs
if (ret && ret.length > 0) {
ret = values(ret);
}
return ret;
}
/*
* Array Reverse implementation
*/
function reverse(a, o) {
a.reverse();
reindex(a, o, 0);
}
/**
* Randomize array element order in-place.
* Using Fisher-Yates shuffle algorithm.
* (Added just because:-)
*/
function shuffle(a, o) {
var j, t;
for (var i = a.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
t = a[i];
a[i] = a[j];
a[j] = t;
}
reindex(a, o, 0);
return a;
}
/*
* Array Some implementation
*/
function some(a, f) {
if (!isFunct(f)) { return; }
return a.some(function (e, i) { return f(e.value, i) });
}
/*
* Sort the dictionary. Sorts the array and reindexes the object.
* a - dictionary array
* o - dictionary object
* sf - dictionary default sort function (can be undefined)
* f - sort method sort function argument (can be undefined)
*/
function sort(a, o, sf, f) {
var sf1 = f || sf; // sort function method used if not undefined
// if there is a customer sort function, use it
if (isFunct(sf1)) {
a.sort(function (e1, e2) { return sf1(e1.value, e2.value); });
}
else {
// sort by key values
a.sort();
}
// reindex - adds O(n) to perf
reindex(a, o, 0);
// return sorted values (not entire array)
// adds O(n) to perf
return values(a);
};
/*
* forEach iteration of "values"
* uses "for" loop to allow exiting iteration when function returns true
*/
function forEach(a, f) {
if (!isFunct(f)) { return; }
// use for loop to allow exiting early and not iterating all items
for(var i = 0; i < a.length; i++) {
if (f(a[i].value, i)) { break; }
}
};
/*
* forEachR iteration of "values" in reverse order
* uses "for" loop to allow exiting iteration when function returns true
*/
function forEachR(a, f) {
if (!isFunct(f)) { return; }
// use for loop to allow exiting early and not iterating all items
for (var i = a.length - 1; i > -1; i--) {
if (f(a[i].value, i)) { break; }
}
}
/*
* Add a new Key Value Pair, or update the value of an existing key value pair
*/
function add(key, value, a, o, resort, sf) {
var k = makeKey(key);
// Update value if key exists
if (exists(k, o)) {
a[o[k]].value = value;
}
else {
// Add a new Key value Pair
var kvp = newkvp(k, value);
o[kvp.key] = a.length;
a.push(kvp);
}
// resort if requested
if (resort) { sort(a, o, sf); }
};
/*
* Removes an existing key value pair and returns the "value" If the key does not exists, returns undefined
*/
function remove(key, a, o) {
var k = makeKey(key);
// return undefined if the key does not exist
if (!exists(k, o)) { return; }
// get the array index
var i = o[k];
// get the key value pair
var ret = a[i];
// remove the array element
a.splice(i, 1);
// remove the object property
delete o[k];
// reindex the object properties from the remove element to end of the array
reindex(a, o, i);
// return the removed value
return ret.value;
};
/*
* Returns true if key exists in the dictionary.
* k - Key to check (without the leading "_" character)
*/
function keyExists(k, o) {
return exists(makeKey(k), o);
};
/*
* Returns value assocated with "key". Returns undefined if key not found
*/
function item(key, a, o) {
var k = makeKey(key);
if (exists(k, o)) {
return a[o[k]].value;
}
}
/*
* changes index values held by object properties to match the array index location
* Called after sorting or removing
*/
function reindex(a, o, i){
for (var j = i; j < a.length; j++) {
o[a[j].key] = j;
}
}
/*
* The "real dictionary"
*/
function dict() {
var _a = [];
var _o = {};
var _sortF;
Object.defineProperties(this, {
"length": { get: function () { return _a.length; }, enumerable: true },
"keys": { get: function() { return keys(_a); }, enumerable: true },
"values": { get: function() { return values(_a); }, enumerable: true },
"keyValuePairs": { get: function() { return kvPs(_a); }, enumerable: true},
"sortFunction": { get: function() { return _sortF; }, set: function(funct) { _sortF = ensureF(funct); }, enumerable: true }
});
// Array Methods - Only modification to not pass the actual array to the callback function
this.map = function(funct) { return map(_a, funct); };
this.every = function(funct) { return every(_a, funct); };
this.filter = function(funct) { return filter(_a, funct); };
this.reverse = function() { reverse(_a, _o); };
this.shuffle = function () { return shuffle(_a, _o); };
this.some = function(funct) { return some(_a, funct); };
this.sort = function(funct) { return sort(_a, _o, _sortF, funct); };
// Array Methods - Modified aborts when funct returns true.
this.forEach = function (funct) { forEach(_a, funct) };
// forEach in reverse order
this.forEachRev = function (funct) { forEachR(_a, funct) };
// Dictionary Methods
this.addOrUpdate = function(key, value, resort) { return add(key, value, _a, _o, resort, _sortF); };
this.remove = function(key) { return remove(key, _a, _o); };
this.exists = function(key) { return keyExists(key, _o); };
this.item = function(key) { return item(key, _a, _o); };
this.get = function (index) { if (index > -1 && index < _a.length) { return _a[index].value; } } ,
this.clear = function() { _a = []; _o = {}; };
return this;
}
return this;
}
L'une des révélations que j'avais tout en essayant mentalement à concilier classe vs prototype des objets, c'est que le prototype est en fait un v-table pour les objets créés. En outre des fonctions dans un boîtier peut également agir comme v-les entrées de la table. À mesure que le projet progressait, j'ai commencé à utiliser l'Objet Usines où un haut niveau d'objet contenu des fonctions communes pour le type d'objet et inclus un "ce.Nouveau(args)" méthode qui a été utilisée pour créer des objets réels utilisés dans la solution. C'est le style que j'ai utilisé pour le dictionnaire.
La base du dictionnaire est un Tableau, un Objet et un objet KeyValuePair. Le "addOrUpdate" méthode prend une clé et une valeur et:
- Crée un KeyValuePair
- Ajoute une nouvelle propriété de l'objet à l'aide de la clé, comme le nom de la propriété et la longueur du tableau comme valeur de la propriété
- Ajouter le KeyValuePair au Tableau, faire l'objet d'une nouvelle propriété la valeur de l'indice dans le tableau
REMARQUE: j'ai lu la propriété de l'objet de noms peut commencer par "presque tout" caractère Unicode. Le projet serait de traiter avec les données du client qui peut commencer par "tout" caractère Unicode. Pour assurer le dictionnaire n'a pas blowup en raison d'une défaillance de la propriété nom, je le préfixe d'un trait de soulignement (_) à la clé et de la bande qui se dégagent lors de la restitution des clés externes pour le dictionnaire.
Pour le dictionnaire pour être fonctionnel, le Tableau interne et l'Objet doit être synchronisé. Pour garantir cela, ni le Tableau ni l'Objet sont exposés à l'extérieur. Je voulais éviter les modifications accidentelles telles que celles qui peuvent se produire quand un "Si" test n'a qu'un seul signe égal et la gauche ont de la valeur est définie par erreur.
If(dict.KeyObj["SomeKey"] = "oops") { alert("good luck tracing this down:-)"); }
Cette erreur typique avec le dictionnaire peut être très difficile à suivre, vers le bas quand les bugs (les symptômes) commencent à apparaître dans le calcul, affichage, etc. Par conséquent, le "ce" de propriété n'aurait pas accès à l'un ou l'autre. Ce protectionnisme est une des raisons que je n'ai pas creuser plus en prototypes. Il avait traversé mon esprit pour utiliser un objet interne avec le Tableau et l'Objet exposé et de l'objet interne lors de l'utilisation de l ' "appel" ou "appliquer" méthodes et je peut regarder que plus tard que je ne suis pas encore sûr que je n'aurais pas à exposer que l'objet interne qui irait à l'encontre de l'objectif de protéger le noyau de la Matrice de l'Objet.
J'ai corrigé certaines des newb erreurs que j'ai faite avec le premier dictionnaire de l'objet que j'ai créé.
- Le "Dict()" fonction contient la plupart du code de travail pour chaque
dictionnaire de l'objet. Les critères que j'ai utilisé pour déterminer si un
clos de la fonction doit être utilisée contre fonctionnalité dans le réel
dictionnaire de l'objet:
- Plus qu'une seule ligne de code
- Utilisée par d'autres fonctions clos
- Peut être sujet à changement provoquant la croissance que je découvre des bugs/problèmes
- Utilisé la méthode de la baie et de la propriété des noms de où il fait sens. À venir à partir de C# j'ai fait des choses que fait mon dictionnaire de moins en moins utilisable comme aide "Le comte" au lieu de "longueur" ou "ForEach" au lieu de "forEach". Par à l'aide du Tableau des noms, le dictionnaire peut maintenant être utilisé comme un Tableau dans la plupart des cas. Malheureusement je n'ai pas été en mesure de trouver un moyen de créer un support de accesseur (ex: val = dict[key]) et qui peut être une bonne chose de toute façon. Quand on pense à ce sujet, j'ai eu de la difficulté d'être sûr que des choses comme val = dict[12] a fonctionné correctement. Le nombre 12 pourrait facilement avoir été utilisé comme une clé, de sorte que je ne pouvais pas penser à une bonne façon de connaître le "l'intention" d'un tel appel.
- Entièrement clos le trait de soulignement préfixe de la manipulation. Dans le projet, j'ai été de travail, j'ai eu cette propagation et répété dans les différents modèle de données objets. Il était laid!