diff --git a/src/lib/string_set.js b/src/lib/string_set.js index bac46526b..9d03d40d1 100644 --- a/src/lib/string_set.js +++ b/src/lib/string_set.js @@ -1,37 +1,54 @@ function StringSet(items) { this._items = {}; + this._nums = {}; this._length = items ? items.length : 0; if (!items) return; for (let i = 0, l = items.length; i < l; i++) { + this.add(items[i]); if (items[i] === undefined) continue; - this._items[items[i]] = i; + if (typeof items[i] === 'string') this._items[items[i]] = i; + else this._nums[items[i]] = i; + } } StringSet.prototype.add = function(x) { - this._length = this._items[x] ? this._length : this._length + 1; - this._items[x] = this._items[x] ? this._items[x] : this._length; + if (this.has(x)) return this; + this._length++; + if (typeof x === 'string') this._items[x] = this._length; + else this._nums[x] = this._length; return this; }; StringSet.prototype.delete = function(x) { - this._length = this._items[x] ? this._length - 1 : this._length; + if (this.has(x) === false) return this; + this._length--; delete this._items[x]; + delete this._nums[x]; return this; }; StringSet.prototype.has = function(x) { - return this._items[x] !== undefined; + if (typeof x !== 'string' && typeof x !== 'number') return false; + return this._items[x] !== undefined || this._nums[x] !== undefined; }; StringSet.prototype.values = function() { - const orderedKeys = Object.keys(this._items).sort((a, b) => this._items[a] - this._items[b]); - return orderedKeys; + const values = []; + Object.keys(this._items).forEach(k => { + values.push({ k: k, v: this._items[k] }); + }); + Object.keys(this._nums).forEach(k => { + values.push({ k: JSON.parse(k), v: this._nums[k] }); + }); + + return values.sort((a, b) => a.v - b.v).map(a => a.k); }; StringSet.prototype.clear = function() { this._length = 0; this._items = {}; + this._nums = {}; return this; }; diff --git a/test/string_set.test.js b/test/string_set.test.js index 5c9b3360b..c93b00fb3 100644 --- a/test/string_set.test.js +++ b/test/string_set.test.js @@ -13,8 +13,8 @@ test('StringSet constructor and API', t => { t.equal(typeof StringSet.prototype.clear, 'function', 'exposes set.clear'); t.equal(Object.keys(StringSet.prototype).filter(k => k[0] !== '_').length, 5, 'no unexpected methods'); - const populatedSet = new StringSet(['a', 'b']); - t.deepEqual(populatedSet.values(), ['a', 'b'], 'populated by constructor arg'); + const populatedSet = new StringSet(['a', 4, 'b']); + t.deepEqual(populatedSet.values(), ['a', 4, 'b'], 'populated by constructor arg'); t.end(); }); @@ -28,29 +28,35 @@ test('StringSet#add', t => { t.deepEqual(set.values(), ['a', 'b']); set.add('a'); t.deepEqual(set.values(), ['a', 'b']); + set.add(3); + t.deepEqual(set.values(), ['a', 'b', 3]); t.end(); }); test('StringSet#delete', t => { - const subject = ['a', 'b']; + const subject = ['a', 'b', 2]; const set = new StringSet(subject); set.delete('a'); - t.deepEqual(set.values(), ['b']); + t.deepEqual(set.values(), ['b', 2]); set.delete('a'); - t.deepEqual(set.values(), ['b']); + t.deepEqual(set.values(), ['b', 2]); set.delete(); - t.deepEqual(set.values(), ['b']); + t.deepEqual(set.values(), ['b', 2]); set.delete('b'); + t.deepEqual(set.values(), [2]); + set.delete(2); t.deepEqual(set.values(), []); - t.deepEqual(subject, ['a', 'b'], 'source array not mutated'); + t.deepEqual(subject, ['a', 'b', 2], 'source array not mutated'); t.end(); }); test('StringSet#has', t => { - const set = new StringSet(['a', 'b']); + const set = new StringSet(['a', 'b', 2]); t.equal(set.has('a'), true); t.equal(set.has('b'), true); + t.equal(set.has(2), true); t.equal(set.has('c'), false); + t.equal(set.has(4), false); t.end(); });