Commit | Line | Data |
---|---|---|
4bbf6ff1 | 1 | /** |
c89533c4 | 2 | * Dictionary implementation relying on an object or if supported on a Map to hold key => value data. |
4bbf6ff1 | 3 | * |
58d7e8f8 | 4 | * If you're looking for a dictionary with object keys, please see `WoltLabSuite/Core/ObjectMap`. |
f5b26fab | 5 | * |
10f1c560 | 6 | * @author Tim Duesterhus, Alexander Ebert |
c839bd49 | 7 | * @copyright 2001-2018 WoltLab GmbH |
4bbf6ff1 | 8 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
58d7e8f8 | 9 | * @module WoltLabSuite/Core/Dictionary |
4bbf6ff1 | 10 | */ |
59ab4d0f | 11 | define(['Core'], function(Core) { |
565853e8 TD |
12 | "use strict"; |
13 | ||
d0023381 | 14 | var _hasMap = objOwns(window, 'Map') && typeof window.Map === 'function'; |
4bbf6ff1 AE |
15 | |
16 | /** | |
17 | * @constructor | |
18 | */ | |
a0c09b4b | 19 | function Dictionary() { |
4bbf6ff1 | 20 | this._dictionary = (_hasMap) ? new Map() : {}; |
f0115fd9 | 21 | } |
4bbf6ff1 AE |
22 | Dictionary.prototype = { |
23 | /** | |
24 | * Sets a new key with given value, will overwrite an existing key. | |
25 | * | |
f64defda AE |
26 | * @param {(number|string)} key key |
27 | * @param {?} value value | |
4bbf6ff1 AE |
28 | */ |
29 | set: function(key, value) { | |
ab8ebbc4 AE |
30 | if (typeof key === 'number') key = key.toString(); |
31 | ||
4bbf6ff1 | 32 | if (typeof key !== "string") { |
ab8ebbc4 | 33 | throw new TypeError("Only strings can be used as keys, rejected '" + key + "' (" + typeof key + ")."); |
4bbf6ff1 AE |
34 | } |
35 | ||
36 | if (_hasMap) this._dictionary.set(key, value); | |
37 | else this._dictionary[key] = value; | |
38 | }, | |
39 | ||
40 | /** | |
41 | * Removes a key from the dictionary. | |
42 | * | |
f64defda | 43 | * @param {(number|string)} key key |
4bbf6ff1 | 44 | */ |
f5b26fab | 45 | 'delete': function(key) { |
ab8ebbc4 AE |
46 | if (typeof key === 'number') key = key.toString(); |
47 | ||
f5b26fab | 48 | if (_hasMap) this._dictionary['delete'](key); |
4bbf6ff1 AE |
49 | else this._dictionary[key] = undefined; |
50 | }, | |
51 | ||
52 | /** | |
53 | * Returns true if dictionary contains a value for given key and is not undefined. | |
54 | * | |
f64defda | 55 | * @param {(number|string)} key key |
4bbf6ff1 AE |
56 | * @return {boolean} true if key exists and value is not undefined |
57 | */ | |
58 | has: function(key) { | |
ab8ebbc4 AE |
59 | if (typeof key === 'number') key = key.toString(); |
60 | ||
4bbf6ff1 AE |
61 | if (_hasMap) return this._dictionary.has(key); |
62 | else { | |
f5336f4f | 63 | return (objOwns(this._dictionary, key) && typeof this._dictionary[key] !== "undefined"); |
4bbf6ff1 AE |
64 | } |
65 | }, | |
66 | ||
67 | /** | |
ab8ebbc4 | 68 | * Retrieves a value by key, returns undefined if there is no match. |
4bbf6ff1 | 69 | * |
f64defda | 70 | * @param {(number|string)} key key |
4bbf6ff1 AE |
71 | * @return {*} |
72 | */ | |
73 | get: function(key) { | |
ab8ebbc4 AE |
74 | if (typeof key === 'number') key = key.toString(); |
75 | ||
4bbf6ff1 AE |
76 | if (this.has(key)) { |
77 | if (_hasMap) return this._dictionary.get(key); | |
78 | else return this._dictionary[key]; | |
79 | } | |
80 | ||
ab8ebbc4 | 81 | return undefined; |
4bbf6ff1 AE |
82 | }, |
83 | ||
84 | /** | |
85 | * Iterates over the dictionary keys and values, callback function should expect the | |
86 | * value as first parameter and the key name second. | |
87 | * | |
88 | * @param {function<*, string>} callback callback for each iteration | |
89 | */ | |
90 | forEach: function(callback) { | |
91 | if (typeof callback !== "function") { | |
92 | throw new TypeError("forEach() expects a callback as first parameter."); | |
93 | } | |
94 | ||
95 | if (_hasMap) { | |
96 | this._dictionary.forEach(callback); | |
97 | } | |
98 | else { | |
99 | var keys = Object.keys(this._dictionary); | |
100 | for (var i = 0, length = keys.length; i < length; i++) { | |
5f335673 | 101 | callback(this._dictionary[keys[i]], keys[i]); |
4bbf6ff1 AE |
102 | } |
103 | } | |
5f335673 AE |
104 | }, |
105 | ||
106 | /** | |
107 | * Merges one or more Dictionary instances into this one. | |
108 | * | |
109 | * @param {...Dictionary} var_args one or more Dictionary instances | |
110 | */ | |
111 | merge: function() { | |
112 | for (var i = 0, length = arguments.length; i < length; i++) { | |
113 | var dictionary = arguments[i]; | |
114 | if (!(dictionary instanceof Dictionary)) { | |
115 | throw new TypeError("Expected an object of type Dictionary, but argument " + i + " is not."); | |
116 | } | |
117 | ||
118 | dictionary.forEach((function(value, key) { | |
119 | this.set(key, value); | |
120 | }).bind(this)); | |
121 | } | |
59ab4d0f MS |
122 | }, |
123 | ||
124 | /** | |
125 | * Returns the object representation of the dictionary. | |
126 | * | |
127 | * @return {object} dictionary's object representation | |
128 | */ | |
129 | toObject: function() { | |
130 | if (!_hasMap) return Core.clone(this._dictionary); | |
131 | ||
132 | var object = { }; | |
133 | this._dictionary.forEach(function(value, key) { | |
134 | object[key] = value; | |
135 | }); | |
136 | ||
137 | return object; | |
4bbf6ff1 AE |
138 | } |
139 | }; | |
140 | ||
10f1c560 TD |
141 | /** |
142 | * Creates a new Dictionary based on the given object. | |
143 | * All properties that are owned by the object will be added | |
144 | * as keys to the resulting Dictionary. | |
145 | * | |
146 | * @param {object} object | |
147 | * @return {Dictionary} | |
148 | */ | |
149 | Dictionary.fromObject = function(object) { | |
150 | var result = new Dictionary(); | |
151 | ||
152 | for (var key in object) { | |
f5336f4f | 153 | if (objOwns(object, key)) { |
10f1c560 TD |
154 | result.set(key, object[key]); |
155 | } | |
156 | } | |
157 | ||
158 | return result; | |
159 | }; | |
160 | ||
4bbf6ff1 AE |
161 | Object.defineProperty(Dictionary.prototype, 'size', { |
162 | enumerable: false, | |
163 | configurable: true, | |
164 | get: function() { | |
165 | if (_hasMap) { | |
166 | return this._dictionary.size; | |
167 | } | |
168 | else { | |
169 | return Object.keys(this._dictionary).length; | |
170 | } | |
171 | } | |
172 | }); | |
173 | ||
174 | return Dictionary; | |
565853e8 | 175 | }); |