Есть ли реализация словаря в JavaScript? - PullRequest
54 голосов
/ 08 апреля 2011

Как я могу реализовать массив с индексатором в JavaScript?Есть ли что-то похожее на словарь в .Net?

Ответы [ 10 ]

89 голосов
/ 08 апреля 2011

Технически нет, но вы можете использовать обычный объект JavaScript, например, словарь:

var a = {"a":"wohoo", 2:"hello2", "d":"hello"};
alert(a["a"]);
alert(a[2]);
alert(a["d"]);
10 голосов
/ 08 апреля 2011

Джон Резиг (автор 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();
  }
}
9 голосов
/ 29 сентября 2012

Вы можете попробовать buckets , это библиотека структуры данных javascript, которая позволяет вам использовать любой тип объекта в словаре.

7 голосов
/ 20 ноября 2013

В моем последнем проекте мне было поручено создать клиентское приложение для браузера, которое будет считывать 10 тысяч строк данных, а затем группировать и агрегировать данные для отображения в сетках и для построения диаграмм.Целевыми технологиями были HTML 5, CSS 3 и EMCS 5. (современный браузер в июне 2013 года).Поскольку совместимость со старыми браузерами не была проблемой, внешние библиотеки были ограничены 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-таблица для созданных объектов.Кроме того, функции в корпусе могут также действовать как записи в V-таблице.По мере развития проекта я начал использовать фабрики объектов, где объект верхнего уровня содержал общие функции для типа объекта и включал метод «this.New (args)», который использовался для создания реальных объектов, используемых в решении.Это стиль, который я использовал для словаря.

Ядром словаря является массив, объект и объект KeyValuePair.Метод «addOrUpdate» принимает ключ и значение и:

  1. Создает KeyValuePair
  2. Добавляет новое свойство к объекту, используя ключ в качестве имени свойства и длину массива какзначение свойства
  3. Добавьте KeyValuePair к массиву, сделав новое значение свойства объекта индексом в массиве

ПРИМЕЧАНИЕ: Я прочитал имена свойств объектаможет начинаться с «почти любого» символа Unicode.Проект будет иметь дело с данными клиентов, которые могут начинаться с «любого» символа Unicode.Чтобы словарь не разрушился из-за неверного имени свойства, я добавляю к ключу знак подчеркивания (_) и удаляю этот знак подчеркивания при возврате ключей, внешних по отношению к словарю.

Чтобы словарь работал,внутренний массив и объект должны быть синхронизированы.Чтобы гарантировать, что ни Массив, ни Объект не выставлены снаружи.Я хотел избежать случайных изменений, таких как те, которые могут произойти, когда тест «Если» имеет только один знак равенства, а значение «левый» установлено по ошибке.

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

Это типичный еОшибка со словарем может быть очень трудно отследить, когда ошибки (симптомы) начинают обнаруживаться при вычислении, отображении и т. д. Следовательно, свойство «this» не будет иметь доступа ни к одному из них. Этот протекционизм - одна из причин, по которой я больше не копался в прототипах. Мне пришло в голову использовать внутренний объект с открытыми массивом и объектом и передавать этот внутренний объект при использовании методов «call» или «apply», и я могу посмотреть на это позже, так как я все еще не уверен, что не буду должны выставить тот внутренний объект, который победил бы цель защиты Массива и Объекта ядра.

Я исправил некоторые ошибки новичка, которые я сделал с первым созданным мной словарем.

  • Функция «Dict ()» содержит большую часть рабочего кода для каждого словарь объекта. Критерии, которые я использовал, чтобы определить, является ли закрытая функция должна использоваться против функциональности в реальном объект словаря:
    • Более одной строки кода
    • Используется другими вложенными функциями
    • Может произойти изменение, вызвавшее рост при обнаружении ошибок / проблем
  • Использовал метод Array и имена свойств там, где это имело смысл. приход из C # я сделал вещи, которые сделали мой словарь менее пригодным для использования, как с «Количество» вместо «длина» или «ForEach» вместо «forEach». От используя имена массивов, словарь теперь можно использовать как массив в большинстве случаев. К сожалению, я не смог найти способ создать скобку accessor (например, val = dict [key]), и это может быть хорошо в любом случае. Когда я думал об этом, мне было трудно быть уверенным, что такие вещи, как val = dict [12] работал правильно. Число 12 легко могло быть используется в качестве ключа, поэтому я не мог придумать хороший способ узнать «намерение» такого вызова.
  • Полностью заключено в префикс подчеркивания. В проекте я был работая, я разложил это и повторил в различных моделях данных объекты. Это было ужасно!
6 голосов
/ 08 апреля 2011

В JS {"index": anyValue} - это просто словарь.Вы также можете обратиться к определению JSON (http://www.json.org/)

2 голосов
/ 19 августа 2014

ECMAScript 6 (он же спецификация JavaScript 2015), указывает интерфейс словаря с именем Map .Он поддерживает произвольные ключи любого типа, имеет свойство "только для чтения" size, не перегружен связанными с прототипами материалами, такими как объекты, и может быть перебран с использованием новой конструкции for...of... или Map.forEach.Ознакомьтесь с документацией по MDN здесь и таблицей совместимости браузера здесь .

2 голосов
/ 22 апреля 2011

Ближайшая реализация, которую я использовал для словаря .Net в Javascript, - это хеш-объект (см. Ссылку: http://www.mojavelinux.com/articles/javascript_hashes.html).. Он реализует массив под капотом и имеет методы, названные так же, как и в словаре .Net. .

1 голос
/ 31 мая 2012

Используйте объект, как пишут другие люди.Если вы храните в качестве ключа что-то кроме строк, просто jsonize их.См. Это сообщение в блоге , в котором приведены сведения о производительности различных реализаций словаря в javascript.

0 голосов
/ 30 января 2018

У меня запущена эта реализация. Первое добавление пары ключ-значение делает его тип ключа безопасным. Он отлично работает и не зависит от карты:

Git (всегда обновляется)

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();
0 голосов
/ 27 октября 2014
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));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...