[IMP] web: utils: add groupBy function
This commit introduces a new utility function: 'groupBy'. It is similar in API and result as the underscore method of the same name. X-original-commit: 834a1967161dc537cb2d5543314a062dbac08f9c
This commit is contained in:
committed by
Aaron Bohy
parent
669bba4694
commit
a6cd6d5a89
@@ -205,6 +205,28 @@ var diacriticsMap = {
|
||||
|
||||
const patchMap = new WeakMap();
|
||||
|
||||
/**
|
||||
* Helper function returning an extraction handler to use on array elements to
|
||||
* return a certain attribute or mutated form of the element.
|
||||
*
|
||||
* @private
|
||||
* @param {string | function} criterion
|
||||
* @returns {(element: any) => any}
|
||||
*/
|
||||
function _getExtractorFrom(criterion) {
|
||||
if (criterion) {
|
||||
switch (typeof criterion) {
|
||||
case 'string': return element => element[criterion];
|
||||
case 'function': return criterion;
|
||||
default: throw new Error(
|
||||
`Expected criterion of type 'string' or 'function' and got '${typeof criterion}'`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return element => element;
|
||||
}
|
||||
}
|
||||
|
||||
var utils = {
|
||||
|
||||
/**
|
||||
@@ -303,6 +325,31 @@ var utils = {
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Returns an object holding different groups defined by a given criterion
|
||||
* or a default one. Each group is a subset of the original given list.
|
||||
* The given criterion can either be:
|
||||
* - a string: a property name on the list elements which value will be the
|
||||
* group name,
|
||||
* - a function: a handler that will return the group name from a given
|
||||
* element.
|
||||
*
|
||||
* @param {any[]} list
|
||||
* @param {string | function} [criterion]
|
||||
* @returns {Object}
|
||||
*/
|
||||
groupBy: function (list, criterion) {
|
||||
const extract = _getExtractorFrom(criterion);
|
||||
const groups = {};
|
||||
for (const element of list) {
|
||||
const group = String(extract(element));
|
||||
if (!(group in groups)) {
|
||||
groups[group] = [];
|
||||
}
|
||||
groups[group].push(element);
|
||||
}
|
||||
return groups;
|
||||
},
|
||||
/**
|
||||
* Returns a human readable number (e.g. 34000 -> 34k).
|
||||
*
|
||||
@@ -656,22 +703,13 @@ var utils = {
|
||||
* - a function: a handler that will return the sortable primitive from a given element.
|
||||
*
|
||||
* @param {any[]} array
|
||||
* @param {(string|function)} [criterion]
|
||||
* @param {string | function} [criterion]
|
||||
*/
|
||||
sortBy: function (array, criterion) {
|
||||
let getter;
|
||||
if (criterion) {
|
||||
switch (typeof criterion) {
|
||||
case 'string': getter = element => element[criterion]; break;
|
||||
case 'function': getter = criterion; break;
|
||||
default: throw new Error(`Expected criterion of type 'string' or 'function' and got '${typeof criterion}'`);
|
||||
}
|
||||
} else {
|
||||
getter = element => element;
|
||||
}
|
||||
const extract = _getExtractorFrom(criterion);
|
||||
return array.slice().sort((elA, elB) => {
|
||||
const a = getter(elA);
|
||||
const b = getter(elB);
|
||||
const a = extract(elA);
|
||||
const b = extract(elB);
|
||||
if (isNaN(a) && isNaN(b)) {
|
||||
return a > b ? 1 : a < b ? -1 : 0;
|
||||
} else {
|
||||
|
||||
@@ -7,6 +7,59 @@ QUnit.module('core', {}, function () {
|
||||
|
||||
QUnit.module('utils');
|
||||
|
||||
QUnit.test('groupBy', function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
const { groupBy } = utils;
|
||||
|
||||
// Invalid
|
||||
assert.throws(
|
||||
() => groupBy({}),
|
||||
new TypeError(`list is not iterable`)
|
||||
);
|
||||
assert.throws(
|
||||
() => groupBy([], true),
|
||||
new Error(`Expected criterion of type 'string' or 'function' and got 'boolean'`)
|
||||
);
|
||||
assert.throws(
|
||||
() => groupBy([], 3),
|
||||
new Error(`Expected criterion of type 'string' or 'function' and got 'number'`)
|
||||
);
|
||||
assert.throws(
|
||||
() => groupBy([], {}),
|
||||
new Error(`Expected criterion of type 'string' or 'function' and got 'object'`)
|
||||
);
|
||||
|
||||
// criterion = default
|
||||
assert.deepEqual(
|
||||
groupBy(["a", "b", 1, true]),
|
||||
{
|
||||
1: [1],
|
||||
a: ["a"],
|
||||
b: ["b"],
|
||||
true: [true],
|
||||
}
|
||||
);
|
||||
// criterion = string
|
||||
assert.deepEqual(
|
||||
groupBy([{ x: "a" }, { x: "a" }, { x: "b" }], "x"),
|
||||
{
|
||||
a: [{ x: "a" }, { x: "a" }],
|
||||
b: [{ x: "b" }],
|
||||
}
|
||||
);
|
||||
// criterion = function
|
||||
assert.deepEqual(
|
||||
groupBy(["a", "b", 1, true], (x) => `el${x}`),
|
||||
{
|
||||
ela: ["a"],
|
||||
elb: ["b"],
|
||||
el1: [1],
|
||||
eltrue: [true],
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test('intersperse', function (assert) {
|
||||
assert.expect(27);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user