Last active
April 23, 2017 13:24
-
-
Save softwarespot/dbd41ecea6e160f63abd028eb476e122 to your computer and use it in GitHub Desktop.
Map & Set implementations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The following is a proof of concept for creating Map and Set data structures found in ES2015 | |
// URL: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map | |
// URL: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set | |
function Map(iterable) { | |
if (!(this instanceof Map)) { | |
throw new TypeError('"Map" cannot be called as a function. Use the "new" keyword to construct a new "Map" object'); | |
} | |
this._keys = []; | |
this._values = []; | |
if (!_isArrayLike(iterable)) { | |
return; | |
} | |
_arrayForEach(_arrayFrom(iterable), function (values) { | |
if (values && values.length === 2) { | |
this.set(values[0], values[1]); | |
} | |
}, this); | |
} | |
Map.prototype = { | |
clear: function () { | |
this._keys.length = 0; | |
this._values.length = 0; | |
}, | |
delete: function (key) { | |
var index = _indexOf(this._keys, key); | |
if (index >= 0) { | |
this._values.splice(index, 1); | |
this._keys.splice(index, 1); | |
return true; | |
} | |
return false; | |
}, | |
entries: function () { | |
return _arrayMap(this._keys, function (key, index) { | |
return [key, this._values[index]]; | |
}, this); | |
}, | |
forEach: function (fn, thisArg) { | |
return _arrayForEach(this._keys, function (key, index) { | |
fn.call(thisArg, this._values[index], key, this); | |
}, this); | |
}, | |
get: function (key) { | |
return this._values[_indexOf(this._keys, key)]; | |
}, | |
has: function (key) { | |
return _indexOf(this._keys, key) >= 0; | |
}, | |
keys: function () { | |
// Create a copy of the internal array | |
return _arrayFrom(this._keys); | |
}, | |
set: function (key, value) { | |
var index = _indexOf(this._keys, key); | |
if (index === -1) { | |
this._keys.push(key); | |
this._values.push(value); | |
} else { | |
this._values[index] = value; | |
} | |
return this; | |
}, | |
values: function () { | |
// Create a copy of the internal array | |
return _arrayFrom(this._values); | |
} | |
}; | |
// Define a read-only "size" property | |
Object.defineProperty(Map.prototype, 'size', { | |
get: function () { | |
return this._keys.length; | |
} | |
}); | |
// Map Example(s) | |
var map = new Map([ | |
['A', '1'], | |
['B', '2'], | |
['C', '3'], | |
]); | |
map.set('D', '4'); | |
map.set('E', NaN); | |
map.set(NaN, NaN); | |
map.set(NaN, 'NaN'); | |
console.log('get::', map.get(NaN)); | |
console.log('has::', map.has(NaN)); | |
console.log('keys::', map.keys()); | |
console.log('values::', map.values()); | |
console.log('entries::', map.entries()); | |
console.log('size::', map.size); | |
map.forEach(function (value, key, map) { | |
console.log('forEach::', value, key, map); | |
}); | |
map.clear(); | |
console.log('keys::', map.keys()); | |
console.log('size::', map.size); | |
function Set(iterable) { | |
if (!(this instanceof Set)) { | |
throw new TypeError('"Set" cannot be called as a function. Use the "new" keyword to construct a new "Set" object'); | |
} | |
this._values = []; | |
if (!_isArrayLike(iterable)) { | |
return; | |
} | |
_arrayForEach(_arrayFrom(iterable), function (value) { | |
this.add(value); | |
}, this); | |
} | |
Set.prototype = { | |
add: function (value) { | |
if (!this.has(value)) { | |
this._values.push(value); | |
} | |
return this; | |
}, | |
clear: function () { | |
this._values.length = 0; | |
}, | |
delete: function (value) { | |
var index = _indexOf(this._values, value) | |
if (index >= 0) { | |
this._values.splice(index, 1); | |
return true; | |
} | |
return false; | |
}, | |
entries: function () { | |
return _arrayMap(this._values, function (value) { | |
return [value, value]; | |
}, this); | |
}, | |
forEach: function (fn, thisArg) { | |
return _arrayForEach(this._values, function (value) { | |
fn.call(thisArg, value, value, this); | |
}, this); | |
}, | |
has: function (value) { | |
return _indexOf(this._values, value) >= 0; | |
}, | |
values: function () { | |
// Create a copy of the internal array | |
return _arrayFrom(this._values); | |
} | |
}; | |
// Define a read-only "size" property | |
Object.defineProperty(Set.prototype, 'size', { | |
get: function () { | |
return this._values.length; | |
} | |
}); | |
// Set Example(s) | |
var queue = new Set([ | |
'A', | |
'B', | |
'C' | |
]); | |
queue.add('D'); | |
queue.add('E'); | |
queue.add(NaN); | |
queue.add(NaN); | |
console.log('has::', queue.has(NaN)); | |
console.log('values::', queue.values()); | |
console.log('entries::', queue.entries()); | |
console.log('size::', queue.size); | |
queue.forEach(function (value, key, set) { | |
console.log('forEach::', value, key, set); | |
}); | |
queue.clear(); | |
console.log('size::', queue.size); | |
// Helper functions | |
// Wrapper for Array.from() | |
function _arrayFrom(array) { | |
return _arrayMap(array, function (value) { | |
return value; | |
}); | |
} | |
// Wrapper for Array.prototype.forEach() | |
function _arrayForEach(array, fn, context) { | |
return array.forEach(fn.bind(context)); | |
} | |
// Wrapper for Array.prototype.map() | |
function _arrayMap(array, fn, context) { | |
return array.map(fn.bind(context)); | |
} | |
// Array.prototype.indexOf() polyfill which supports NaN aka just like Array.prototype.includes() does | |
function _indexOf(array, search) { | |
var includeNaN = search !== search; | |
for (var i = 0; i < array.length; i++) { | |
// Similar to Object.is() and its comparison checks, since -0 !== 0 | |
if (search === array[i] ? | |
search !== 0 || (1 / search) === (1 / array[i]) : | |
includeNaN && array[i] !== array[i]) { | |
return i; | |
} | |
} | |
return -1; | |
} | |
// Check if an array-like object is array-like | |
function _isArrayLike(arrayLike) { | |
return arrayLike && | |
typeof arrayLike.length === 'number' && | |
arrayLike.length >= 0 && | |
arrayLike.length <= (Math.pow(2, 53) - 1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment