Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
filterMap utility function using JavaScript iteration protocols
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Filter Map</title>
</head>
<body>
<script>
// In response to
// https://gomakethings.com/revisiting-array.reduce/#transforming-and-filtering-an-array
//
let wizards = [
{ name: 'Harry Potter', house: 'Gryfindor' },
{ name: 'Cedric Diggory', house: 'Hufflepuff' },
{ name: 'Tonks', house: 'Hufflepuff' },
{ name: 'Ronald Weasley', house: 'Gryfindor' },
{ name: 'Hermione Granger', house: 'Gryfindor' },
];
// if `filter` & `map` is easier to read then maybe it's a naming problem
let hufflepuffNames = wizards.reduce(collectNamesInHouse('Hufflepuff'), []);
console.log('hufflepuffNames', hufflepuffNames); // hufflepuffNames ▶(2) ["Cedric Diggory", "Tonks"]
// Using iteration protocol based utility function
// returns:
// - `undefined` for "None"
// - "Some" `name` otherwise
const maybeHufflepuffName = function ({ name, house }) {
return house.toLowerCase() === 'hufflepuff' ? name : undefined;
};
let hufflepuff = Array.from(filterMap(maybeHufflepuffName, wizards));
console.log('hufflepuff', hufflepuff); // hufflepuff ▶(2) ["Cedric Diggory", "Tonks"]
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
// key-value Map; name to house
let wizardMap = new Map(
wizards.map(function ({ name, house }) {
return [name, house];
})
);
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
const f = function ([name, house]) {
return house.toLowerCase() === 'hufflepuff' ? name : null;
};
// Parameters ordered to be most useful for binding (fake "currying")
const keepHufflepuffNames = filterMap.bind(null, f);
// Takes an iterable and supplies an iterable
let hufflepuffSet = new Set(keepHufflepuffNames(wizardMap));
console.log('hufflepuffSet', hufflepuffSet); // hufflepuffSet ▶Set(2) {"Cedric Diggory", "Tonks"}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
// ---
// ---
function collectNamesInHouse(inHouse) {
const houseName = inHouse.toLowerCase();
return function (array, { house, name }) {
if (house.toLowerCase() === houseName) array.push(name);
return array;
};
}
// Iteration protocols
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
function filterMap(f, iterable) {
let wrapped = iterable[Symbol.iterator]();
let done = false;
let iterator = {
next() {
if (done) return { done };
let value, result;
do {
({ done, value: result } = wrapped.next());
if (done) break;
value = f(result);
} while (value === undefined);
return done ? { done } : { done, value };
},
[Symbol.iterator]() {
return this;
},
};
return iterator;
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>groupBy</title>
</head>
<body>
<script>
// In response to
// https://gomakethings.com/revisiting-array.reduce/#recreating-the-lodash-groupby-method-with-vanilla-js
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#Objects_vs._Maps
// "The keys of an Object must be either a String or a Symbol."
//
const numbers = [6.1, 4.2, 6.3];
const numbersGroupObj = groupByObj(numbers, Math.floor);
console.log('groupBy floor function', numbersGroupObj);
console.log('key type', typeof Object.keys(numbersGroupObj)[0]);
// "key type string"
const words = ['one', 'two', 'three'];
console.log('groupBy length property', groupByObj(words, 'length'));
// "A Map's keys can be any value (including functions, objects, or any primitive)."
//
const numbersGroup = groupBy(numbers, Math.floor);
console.log('groupBy floor function', numbersGroup);
console.log('key type', typeof numbersGroup.keys().next().value);
// "key type number"
console.log('entries', Array.from(numbersGroup.entries()));
console.log('groupBy length property', groupBy(words, 'length'));
// ---
// ---
function groupByObj(arr, criteria) {
const extractKey =
typeof criteria === 'function'
? criteria
: function (i) {
return i[criteria];
};
const collectByKey = function (obj, item) {
const key = extractKey(item);
if (obj.hasOwnProperty(key)) {
obj[key].push(item);
} else {
obj[key] = [item];
}
return obj;
};
return arr.reduce(collectByKey, {});
}
function groupBy(arr, criteria) {
const extractKey =
typeof criteria === 'function'
? criteria
: function (i) {
return i[criteria];
};
const collectItemsByKey = function (map, item) {
const key = extractKey(item);
let value = map.get(key);
if (value !== undefined) {
value.push(item);
} else {
map.set(key, [item]);
}
return map;
};
return arr.reduce(collectItemsByKey, new Map());
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.