JavaScript中有字典实现吗?

Posted

技术标签:

【中文标题】JavaScript中有字典实现吗?【英文标题】:Is there a dictionary implementation in JavaScript? 【发布时间】:2011-08-01 11:29:03 【问题描述】:

如何在 javascript 中使用索引器实现数组? .Net 中有没有类似字典的东西?

【问题讨论】:

【参考方案1】:

我正在运行这个实现。第一次添加键值对使其成为一种安全的键类型。 它工作正常并且独立于地图:

Git (is always updated)

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();

【讨论】:

【参考方案2】:
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));

【讨论】:

【参考方案3】:

ECMAScript 6(又名 2015 JavaScript 规范)specifies a dictionary interface, named Map。它支持任何类型的任意键,具有只读的size 属性,不会被对象等原型相关的东西弄乱,并且可以使用新的for...of... 构造或Map.forEach 进行迭代。查看 MDN here 上的文档和浏览器兼容性表 here。

【讨论】:

【参考方案4】:

技术上没有,但您可以使用像字典这样的常规 JavaScript 对象:

var a = "a":"wohoo", 2:"hello2", "d":"hello";
alert(a["a"]);
alert(a[2]);
alert(a["d"]);

【讨论】:

那么我该如何迭代字典呢? @Hosi: for(var x in a) console.log(x, a[x]); - 在 chrome 的 javascript 控制台中运行时。不要期望结果与定义的键的顺序相同,因为它是一个对象。 Dt 不是因为它是“一个对象”,而是因为它是一个无序的集合。 这通常有效,但如果您的其中一个键碰巧被称为"toString""constructor",则会出现令人讨厌的错误。因此我们必须始终使用hasOwnProperty 来检查是否存在。 多么优美的语言。【参考方案5】:

在我的上一个项目中,我的任务是创建一个浏览器客户端应用程序,该应用程序将读取数千行数据中的 10 行,然后对数据进行分组和聚合,以便在网格中显示和绘制图表。目标技术是 html 5、CSS 3 和 EMCS 5。(2013 年 6 月的现代浏览器)。因为旧的浏览器兼容性不是问题,所以外部库仅限于 D3(无 JQuery)。

我需要建立一个数据模型。我之前用 C# 构建了一个,并依靠自定义字典对象来快速访问数据、组和聚合。我已经多年没有使用 JavaScript 了,所以我开始搜索字典。我发现 JavaScript 仍然没有真正的原生字典。我找到了一些示例实现,但没有一个真正符合我的期望。所以我建了一个。

正如我所提到的,我已经多年没有使用 JavaScript。进步(或者可能只是信息的网络可用性)令人印象深刻。我之前的所有工作都是基于类的语言,所以原型基础语言需要一些时间来适应(我还有很长的路要走)。

这个项目和大多数项目一样,是在开始之前就完成的,所以我在进行过程中了解到从基于类的语言过渡到基于原型的语言时会出现的许多新手错误。创建的字典很实用,但一段时间后,我意识到我可以通过减少它的新意来做出一些改进。在我有时间重新编写字典之前,该项目的资金已经耗尽。哦,我的职位同时失去了资金(令人惊讶的是怎么会发生这种情况)。所以我决定使用我学到的知识重新创建字典,并确定字典是否实际上是对数组的性能改进。

/*
* 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-table。此外,外壳中的功能也可以像 v-table 条目一样起作用。随着项目的进展,我开始使用对象工厂,其中***对象包含对象类型的常用函数,并包含一个“this.New(args)”方法,用于创建解决方案中使用的实际对象。这是我用于字典的样式。

字典的核心是一个Array、一个Object和一个KeyValuePair对象。 “addOrUpdate”方法接受一个键和一个值:

    创建一个 KeyValuePair 使用键作为属性名称向对象添加新属性 并将数组长度作为属性值 将 KeyValuePair 添加到数组中,使对象成为新属性 值数组中的索引

注意:我读到的对象属性名称可以以“几乎任何”Unicode 字符开头。该项目将处理可以以“任何”Unicode 字符开头的客户数据。为确保字典不会因为无效的属性名称而爆炸,我在键前加上下划线 (_),并在返回字典外部的键时去掉该下划线。

为了使字典正常工作,内部数组和对象必须保持同步。为了确保这一点,Array 和 Object 都不会暴露在外部。我想避免意外的变化,例如当“If”测试只有一个等号而左边的值被错误设置时可能发生的变化。

If(dict.KeyObj[“SomeKey”] = “oops”)  alert(“good luck tracing this down:-)”); 

当错误(症状)开始出现在计算、显示等中时,这种典型的字典错误可能很难追踪。因此,“this”属性将无法访问其中任何一个。这种保护主义是我没有深入研究原型的原因之一。在使用“调用”或“应用”方法时,我曾想过使用暴露数组和对象的内部对象并传递该内部对象,我可能会稍后再看,因为我仍然不确定我不会必须暴露该内部对象,这将破坏保护核心数组和对象的目的。

我修复了我在创建第一个字典对象时犯的一些新手错误。

“Dict()”函数包含每个函数的大部分工作代码 字典对象。我用来确定是否 应使用封闭函数与实际中的功能 字典对象: 不止一行代码 由其他封闭函数使用 当我发现错误/问题时,可能会发生变化并导致增长 在有意义的地方使用了数组方法和属性名称。未来 从 C# 我做了一些使我的字典不那么有用的事情 “计数”而不是“长度”或“ForEach”而不是“forEach”。经过 使用数组名称,字典现在可以在大多数情况下用作数组 案例。不幸的是,我无法找到创建支架的方法 访问器(例如 val = dict[key]),无论如何这可能是一件好事。 在考虑它时,我很难确定像 val = dict[12] 工作正常。数字 12 很容易被 用作钥匙,所以我想不出一个好的方法来了解 此类电话的“意图”。 完全封闭下划线前缀处理。在我的项目中 工作,我把它展开并在各种数据模型中重复 对象。太丑了!

【讨论】:

用键对 10K 对象进行基准测试:加载/访问 - Firefox:25.5/20.4 Chrome:29.8/15.7 IE:25.7/18.8。见codeproject.com/Articles/684815/…【参考方案6】:

像其他人写的那样使用对象。如果您将字符串以外的内容存储为键,则只需将它们 jsonize 即可。请参阅此blog post,了解 javascript 中不同字典实现的性能考量。

【讨论】:

【参考方案7】:

我在 Javascript 中使用的最接近 .Net 字典的实现是哈希对象(请参阅链接:http://www.mojavelinux.com/articles/javascript_hashes.html)。它在底层实现了一个数组,并具有与 .Net 字典类似的命名方法。

【讨论】:

【参考方案8】:

在 JS 中,"index":anyValue 只是一个字典。您也可以参考 JSON 的定义 (http://www.json.org/)

【讨论】:

没有。字典是将任意类型的值映射到任意类型的值的容器。 JavaScript“对象”强制字符串作为键,这太残废了,不能称为字典。 另外,它与 JSON 无关。 JSON 是一种文本格式信息,一些 JavaScript 数据结构可以被序列化。 感谢@HereticMonkey 的更正,如果年轻的我的回答不准确,我很抱歉。但是从技术上讲,无论它多么残缺,JavaScript中的对象都可以被认为是一种字典。请参阅这两个关于 en.wikipedia.org/wiki/Associative_array en.wikibooks.org/wiki/A-level_Computing/AQA/Paper_1/… 的维基页面,***也提到了 JSON。【参考方案9】:

John Resig(jQuery 的作者)posted recently 谈 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中有字典实现吗?的主要内容,如果未能解决你的问题,请参考以下文章

Javascript 字典应用实例

在JavaScript中有类似mulitiSet的东西吗?

JavaScript:字段或属性

JavaScript 中的 C# 字典等效项

有啥用?和:[重复]

JavaScript中有睡眠功能吗? [复制]