JavaScript中是否有字典实现?
我如何在JavaScript中使用索引器实现一个数组? 在.Net中有没有像字典的东西?
从技术上讲,不是,但是你可以使用像字典这样的常规JavaScript对象:
var a = {"a":"wohoo", 2:"hello2", "d":"hello"}; alert(a["a"]); alert(a[2]); alert(a["d"]);
John Resig(jQuery的作者) 最近发布了JavaScript中的字典查找。
他的解决scheme是将字典值分配为对象的属性。 从上面的文章逐字粘贴的代码:
// 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数据结构库,它允许你在字典中使用任何types的对象。
在我的最后一个项目中,我的任务是创build一个浏览器客户端应用程序,该应用程序将读取数以千计的数据行,然后对数据进行分组和汇总,以便在网格中显示和绘制图表。 目标技术是HTML 5,CSS 3和EMCS 5(2013年6月的现代浏览器)。 由于旧的浏览器兼容性不是一个问题,外部库仅限于D3(无JQuery)。
我需要build立一个数据模型。 我之前在C#中创build了一个,并依靠自定义字典对象来快速访问数据,组和聚合。 多年以来我一直没有用JavaScript工作,所以我开始search字典。 我发现JavaScript仍然没有真正的本地字典。 我发现了一些示例实现,但没有真正满足我的期望。 所以我build了一个
正如我所提到的,我从来没有在JavaScript工作多年。 这些进步(或可能只是networking信息的可用性)相当令人印象深刻。 我之前的所有工作都是使用基于类的语言,所以原型基础语言花了一些时间去习惯(而且我还有很长的路要走)。
这个项目和大多数项目一样,在开始之前就已经到期了,所以我学习了许多新的错误,当从一个基于类的语言转换到一个基于原型的语言时,这个错误是可以预料的。 创build的字典是function性的,但过了一段时间,我意识到一些改进,我可以通过使其不那么新鲜。 在我有时间重新翻译字典之前,这个项目耗尽了资金。 哦,我的位置在同一时间失去了资金(惊人的如何可能发生)。 所以我决定用我学到的东西重新创build字典,并确定字典实际上是否是一个数组的性能改进。
/* * 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; }
我试图从心理上调和类与原型对象的过程中,其中一个显而易见的事情是原型基本上是创build对象的一个v表。 另外,机箱中的function也可以用作v表条目。 随着项目的进展,我开始使用Object Factories,其中顶级对象包含对象types的通用函数,并包含一个“this.New(args)”方法,用于创build解决scheme中使用的实际对象。 这是我用于字典的风格。
字典的核心是一个数组,一个对象和一个KeyValuePair对象。 “addOrUpdate”方法带有一个键和一个值,并且:
- 创build一个KeyValuePair
- 使用键作为属性名称和数组长度作为属性值将新属性添加到对象
- 将KeyValuePair添加到数组中,使对象的新属性值成为数组中的索引
注:我读对象的属性名称可以“几乎任何”Unicode字符开始。 该项目将处理可以“任何”Unicode字符开始的客户数据。 为了确保字典不会由于属性名称无效而爆炸,我在键上加了一个下划线(_),并在字典外部返回键时去掉下划线。
为使字典正常工作,必须保持内部数组和对象同步。 为了确保这个数组和对象都不暴露在外部。 我想避免意外的改变,比如当一个“If”testing只有一个等号,而左边的值被错误地设置时可能发生的变化。
If(dict.KeyObj[“SomeKey”] = “oops”) { alert(“good luck tracing this down:-)”); }
当字典(症状)开始出现在计算,显示等等时,这个与字典的典型错误可能很难追踪。因此“this”属性将无法访问任何一个。 这种保护主义是我没有深入挖掘原型的原因之一。 它使用了一个内部对象来暴露数组和对象,并在使用“调用”或“应用”方法时传递该内部对象,我可能稍后会看到,因为我仍然不确定我不会必须暴露内部的对象,这将打败保护核心arrays和对象的目的。
我修复了我用我创build的第一个字典对象所做的一些newb错误。
- “Dict()”函数包含每个字典对象的大部分工作代码。 我用来确定是否应该使用封闭函数与实际字典对象中的function的标准:
- 不止一行代码
- 由其他封闭的函数使用
- 当我发现错误/问题时,可能会有所改变,从而导致增长
- 使用的数组方法和属性名称是有道理的。 来自C#我做的事情,使我的字典使用“计数”而不是“长度”或“ForEach”而不是“forEach”可用。 通过使用数组名称,字典现在可以在大多数情况下用作数组。 不幸的是,我无法find一个方法来创build一个支架访问器(例如val = dict [key]),这可能是件好事。 在考虑这个问题时,我很难确定像val = dict [12]这样的东西是否正常工作。 12号很容易被当作钥匙,所以我想不出有一个很好的方法来知道这个电话的“意图”。
- 完全封闭下划线前缀处理。 在我工作的这个项目中,我把这个分散开来,并在各种数据模型对象中重复使用。 这是丑陋的!
在JS中,{“index”:anyValue}只是一个字典。 你也可以参考JSON的定义(http://www.json.org/);
我在Javascript中使用的.Net字典中最近的实现是一个哈希对象(请参阅链接: http : //www.mojavelinux.com/articles/javascript_hashes.html )。 它在底层实现了一个数组,并且具有与.Net字典相似的命名方法。
像其他人一样使用对象。 如果你将string以外的其他东西存储为key,那么只需对它们进行json化即可。 请参阅此博客文章,了解JavaScript中不同字典实现的性能统一性。
ECMAScript 6(又名2015 JavaScript规范) 指定了一个名为Map的字典界面 。 它支持任意types的任意键,具有只读size
属性,不会与像对象这样的原型相关的东西混杂在一起,并且可以使用for...of...
构造或Map.forEach
的新for...of...
来迭代。 在这里查看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));