Extending the Map
A map is an abstract data type composed of key-value pairs. JavaScript comes with the
built-in Map
object that allows creating a map easily.
const myMap = new Map();map.set("foo", "bar");console.log(map.get("foo"));
Pretty easy, isn't it?
However, the methods available on a Map are very limited, namely:
Map.prototype.clear()
Map.prototype.delete()
Map.prototype.entries()
Map.prototype.forEach()
Map.prototype.get()
Map.prototype.has()
Map.prototype.keys()
Map.prototype.set()
Map.prototype.values()
In this article, you'll be guided to extend the Map
object and add more methods
for ease of use. The following are the list of methods we will be implementing:
find()
filter()
map()
reduce()
Now let's move on to implementing them.
Extending a Class
Classes in JavaScript can be extended using the extends
keyword.
class ExtendedMap extends Map { constructor() {}}
Since we are extending an existing class, we should follow up with a super()
call.
class ExtendedMap extends Map { constructor() { super(); }}
Cool. Next comes adding custom methods.
Implementing Methods
class ExtendedMap extends Map { constructor() { super(); } foo() { console.log("bar") }}
You should now be able to access the extended map.
const myMap = new ExtendedMap();myMap.foo();
bar
Map.find()
The find()
method performs a linear search on the collection and returns the
first element that passes our condition.
For this, we first convert our Map into an iterator. To do so, we use Map.prototype.entries()
.
We can then iterate over the result using a for..of
loop.
find() { for (const [k, v] of this.entries()) { }}
Our find()
method needs one parameter, the test function. The function should return a truthy
value in order to return an element.
find(fn) { for (const [k, v] of this.entries()) { }}
Next is of course, running our test function on each entry in the map.
find(fn) { for (const [k, v] of this.entries()) { if(fn(v, k)) return v; }}
There we have, our simple find()
method. However, this method will
return NOTHING if it didn't find a match. In that case, we can return a null
or undefined
.
find(fn) { for (const [k, v] of this.entries()) { if(fn(v, k)) return v; } return undefined;}
Map.filter()
The filter()
method performs a linear search on the collection and returns the
all elements that pass our condition.
The working is the same as our find()
method.
filter(fn) { for (const [k, v] of this.entries()) { }}
The only change is that we are returning an array of elements now.
filter(fn) { const res = []; for (const [k, v] of this.entries()) { if(fn(v, k)) res.push(v); } return res;}
Unlike find()
, method will return an empty array even if it didn't find a match.
We don't need to explicitly return a fallback.
Map.map()
map()
is a bit different. It is a method which runs a function on every element
in the collection and returns an array of results.
Starting from where we left our filter
,
map(fn) { const res = []; for (const [k, v] of this.entries()) { if(fn(v, k)) res.push(v); } return res;}
All we need to do is, remove the condition and push the function result instead.
map(fn) { const res = []; for (const [k, v] of this.entries()) { res.push(fn(v, k)); } return res;}
Easy, is it not? Now comes the final part of this guide.
Map.reduce()
The reduce()
method accepts a reducer callback as a parameter
and executes the callback on each element, with the previous iteration's
result as a parameter. Like a chain.
Let's continue from where we left our map()
.
reduce(fn) { const res = []; for (const [k, v] of this.entries()) { res.push(fn(v, k)); } return res;}
We will need two parameters, one being the callback and the other being the initial value of our result.
reduce(fn, first) { const res = first; for (const [k, v] of this.entries()) { res = fn(res, [k, v]); } return res;}
And that's it! We have implemented reduce()
for our extended Map
.
Our result
Check out the full source code with even more methods at retraigo/bettermap
class ExtendedMap extends Map { constructor() { super(); } find(fn) { for (const [k, v] of this.entries()) { if(fn(v, k)) return v; } return undefined; } filter(fn) { const res = []; for (const [k, v] of this.entries()) { if(fn(v, k)) res.push(v); } return res; } map(fn) { const res = []; for (const [k, v] of this.entries()) { res.push(fn(v, k)); } return res; } reduce(fn, first) { const res = first; for (const [k, v] of this.entries()) { res = fn(res, [k, v]); } return res; }}