Skip to content

Instantly share code, notes, and snippets.

@softwarespot
Last active April 23, 2017 13:24
Show Gist options
  • Save softwarespot/dbd41ecea6e160f63abd028eb476e122 to your computer and use it in GitHub Desktop.
Save softwarespot/dbd41ecea6e160f63abd028eb476e122 to your computer and use it in GitHub Desktop.
Map & Set implementations
// 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