Есть ли реализация словаря в JavaScript?
Как я могу реализовать массив с индексатором в JavaScript? Есть ли что-то вроде словаря в .Net?
10 ответов:
технически нет, но вы можете использовать обычный объект JavaScript, такой как словарь:
var a = {"a":"wohoo", 2:"hello2", "d":"hello"}; alert(a["a"]); alert(a[2]); alert(a["d"]);
John Resig (автор jQuery) недавно написал при поиске словаря в javascript.
его решение состоит в том, чтобы присвоить значения словаря как свойства объекта. Код вставлен дословно сверху статьи:
// 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(); } }
вы можете попробовать ведра, это библиотека структуры данных javascript, она позволяет использовать любой тип объекта в словаре.
мне нужно было построить модель данных. Я построил его раньше в C# и полагался на пользовательские объекты словаря для быстрого доступа к данным, группам и агрегатам. Я не работал в JavaScript в течение многих лет, поэтому я начал искать словарь. Я обнаружил, что JavaScript все еще не имеет истинного родного словаря. Я нашел несколько примеров реализации, но ничего, что действительно соответствовало моим ожиданиям. Так что я построил один.
Как я уже упоминал, я не работал в JavaScript в течение многих лет. Достижения (или, может быть, просто доступность информации в интернете) были довольно впечатляющими. Все мои предыдущая работа была с языками на основе классов, поэтому базовый язык прототипа занял некоторое время, чтобы привыкнуть (и мне еще предстоит пройти долгий путь).
этот проект, как и большинство, должен был начаться до его начала, поэтому я узнал, как я пошел, делая многие из ошибок newb, которые ожидались бы при переходе от класса на основе прототипа на основе языка. Созданный словарь был функциональным, но через некоторое время я понял некоторые улучшения, которые я мог бы сделать, сделав его менее новым. Проект кончилось финансирование прежде, чем я успел переделать словарь. О, и моя позиция потеряла финансирование в то же время (удивительно, как это может произойти). Поэтому я решил воссоздать словарь, используя то, что я узнал, и определить, действительно ли словарь был улучшением производительности по сравнению с массивом.
/* * 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; }
одно из прозрений, которые я имел, пытаясь мысленно примирить класс против объектов прототипа, заключается в том, что прототип в основном является v-таблицей для созданных объектов. Вдобавок функции в приложении могут также подействовать как входы v-таблицы. По мере развития проекта я начал использовать фабрики объектов, где объект верхнего уровня содержал общие функции для типа объекта и включал "это.Метод New (args)", который использовался для создания реальных объектов, используемых в решении. Это стиль, который я использовал для словаря.
ядро словаря-это массив, объект и объект KeyValuePair. "Метод addOrUpdate" принимает ключ и значение и:
- создает KeyValuePair
- добавляет новое свойство к объекту, используя ключ в качестве имени свойства и длина массива в качестве значения свойства
- добавьте KeyValuePair в массив, сделав объект новым свойством значение индекса в массиве
Примечание: я читаю, что имена свойств объекта могут начинаться с" почти любого " символа Юникода. Проект будет иметь дело с данными о клиентах, которые могут начинаться с "любой" символ Юникода. Чтобы убедиться, что словарь не взорвался из-за недопустимого имени свойства, я добавляю префикс подчеркивания (_) к ключу и удаляю это подчеркивание при возврате ключей, внешних по отношению к словарю.
чтобы словарь был функциональным, внутренний массив и объект должны быть синхронизированы. Чтобы гарантировать это, ни массив, ни объект не подвергаются внешнему воздействию. Я хотел избежать случайных изменений, таких как те, которые могут произойти, когда тест "если" имеет только один равный знак и слева есть значение устанавливается по ошибке.
If(dict.KeyObj[“SomeKey”] = “oops”) { alert(“good luck tracing this down:-)”); }
эта типичная ошибка со словарем может быть очень трудно отследить, когда ошибки (симптомы) начинают появляться в вычислении, отображении и т. д. Следовательно, свойство "this" не будет иметь доступа ни к одному из них. Этот протекционизм - одна из причин, по которой я больше не копался в прототипах. Мне пришло в голову использовать внутренний объект с открытым массивом и объектом и передать этот внутренний объект при использовании "вызовите "или" примените " методы, и я могу посмотреть на это позже, поскольку я все еще не уверен, что мне не придется раскрывать этот внутренний объект, который победит цель защиты основного массива и объекта.
я исправил некоторые ошибки newb, которые я сделал с первым объектом словаря, который я создал.
- функция "Dict ()" содержит большую часть рабочего кода для каждого объект Dictionary. Критерии, которые я использовал, чтобы определить, является ли закрытая функция должна быть использована против функциональность в реальных объект Dictionary:
- более одной строки кода
- используется другими вложенными функциями
- могут быть изменены, вызывая рост, как я обнаруживаю ошибки / проблемы
- используется способ выбора и имена свойств, где это имело смысл. Приход из C# я сделал то, что сделало мой словарь менее удобным для использования "Граф" вместо "длина" или "объекту", а не "объекту". От используя имена, словарь теперь можно использовать в качестве массива в большинстве случаи. К сожалению, я не смог найти способ создать скобку accessor (например, val = dict[key]), и это может быть хорошо в любом случае. Когда думаю об этом мне было трудно быть уверенным, что такие вещи, как val = dict[12] работал правильно. Число 12 могло бы быть используется как ключ, поэтому я не мог придумать хороший способ узнать "намерение" такого призыва.
- полностью заключена обработка префикса подчеркивания. В проекте I было работая, я это разложил и повторил в различных моделях данных объекты. Это было ужасно!
в JS, {"index": anyValue} это просто словарь. Вы также можете обратиться к определению JSON (http://www.json.org/)
ближайшая реализация, которую я использовал для словаря .Net в Javascript, - это хэш-объект (см. ссылку:http://www.mojavelinux.com/articles/javascript_hashes.html). он реализует массив под капотом и имеет аналогичные имена методов для словаря .Net.
использовать объект, как пишут другие люди. Если вы храните что-то другое, чем строки в качестве ключа, то просто jsonize их. Смотрите это блоге для повышения производительности различных реализаций словаря в javascript.
ECMAScript 6 (он же 2015 JavaScript spec), задает интерфейс словаря с именем Map. Он поддерживает произвольные ключи любого типа, только для чтения
size
свойство, не загромождено прототипами, связанными с такими вещами, как объекты, и может быть повторено с помощью новогоfor...of...
построить илиMap.forEach
. Проверьте документацию по MDN здесь, и таблица совместимости браузера здесь.
var nDictionary = Object.create(null); function setDictionary(index, value) { nDictionary[index] = value; } function getDictionary(index) { return nDictionary[index]; } setDictionary(81403, "test 1"); setDictionary(81404, "test 2"); setDictionary(81405, "test 3"); setDictionary(81406, "test 4"); setDictionary(81407, "test 5"); alert(getDictionary(81403));
у меня есть эта реализация работает. Первое добавление пары значений ключа делает его безопасным типом ключа. Он отлично работает и не зависит от карты:
function Dictionary() { this.dictionary = []; this.validateKey = function(key){ if(typeof key == 'undefined' || key == null){ return false; } if(this.dictionary.length){ if (!this.hasOwnProperty(this.dictionary[0], "key")) { return false; } if(typeof this.dictionary[0].key != typeof key){ return false; } } return true; }; this.hasOwnProperty = function (obj, prop) { var proto = obj.__proto__ || obj.constructor.prototype; return (prop in obj) && (!(prop in proto) || proto[prop] !== obj[prop]); }; } Dictionary.prototype = { Add: function(key, value) { if(!this.validateKey(key)){ return false; } if(!this.ContainsKey(key)){ this.dictionary.push({ key: key, value: value }); return true; } return false; }, Any: function() { return this.dictionary.length > 0; }, ContainsKey: function(key) { if(!this.validateKey(key)){ return false; } for (var i = 0; i < this.dictionary.length; i++) { var keyValuePair = this.dictionary[i]; if (typeof keyValuePair != "undefined" && keyValuePair != null) { if (this.hasOwnProperty(keyValuePair, "key")) { if (keyValuePair.key == key) { return true; } } } } return false; }, ContainsValue: function(value) { for (var i = 0; i < this.dictionary.length; i++) { var keyValuePair = this.dictionary[i]; if(typeof keyValuePair != "undefined" && keyValuePair != null){ if (this.hasOwnProperty(keyValuePair, "value")) { if(value == null && keyValuePair.value == null){ return true; } if ((value != null && keyValuePair.value == null) || (value == null && keyValuePair.value != null)) { continue; } // compare objects content over json. if(JSON.stringify(value) === JSON.stringify(keyValuePair.value)){ return true; } } } } return false; }, Count: function() { return this.dictionary.length; }, GetValue: function(key){ if(!this.validateKey(key)){ return null; } for (var i = 0; i < this.dictionary.length; i++) { var keyValuePair = this.dictionary[i]; if (typeof keyValuePair != "undefined" && keyValuePair != null) { if (this.hasOwnProperty(keyValuePair, "key")) { if (keyValuePair.key == key) { return keyValuePair.value; } } } } return null; }, Keys: function(){ var keys = []; for (var i = 0; i < this.dictionary.length; i++) { var keyValuePair = this.dictionary[i]; if (typeof keyValuePair != "undefined" && keyValuePair != null) { if (this.hasOwnProperty(keyValuePair, "key")) { keys.push(keyValuePair.key); } } } return keys; }, Remove: function(key){ if(!this.validateKey(key)){ return; } for (var i = 0; i < this.dictionary.length; i++) { var keyValuePair = this.dictionary[i]; if (typeof keyValuePair != "undefined" && keyValuePair != null) { if (this.hasOwnProperty(keyValuePair, "key")) { if (keyValuePair.key == key) { this.dictionary.splice(i, 1); return; } } } } }, Values: function(){ var values = []; for (var i = 0; i < this.dictionary.length; i++) { var keyValuePair = this.dictionary[i]; if (typeof keyValuePair != "undefined" && keyValuePair != null) { if (this.hasOwnProperty(keyValuePair, "value")) { values.push(keyValuePair.value); } } } return values; }, };
вот как вы его используете:
var dic = new Dictionary(); var success = dic.Add("test", 5); success = dic.Add("test1", 4); success = dic.Add("test2", 8); success = dic.Add(3, 8); var containsKey = dic.ContainsKey("test2"); containsKey = dic.ContainsKey(3); var containsValue = dic.ContainsValue(8); var value = dic.GetValue("test1"); var keys = dic.Keys(); var values = dic.Values(); dic.Remove("test1"); var keys = dic.Keys(); var values = dic.Values();