summaryrefslogtreecommitdiff
path: root/addons/web
diff options
context:
space:
mode:
authorJulien Mougenot <jum@odoo.com>2020-06-19 07:06:28 +0000
committerAaron Bohy <aab@odoo.com>2020-06-22 13:04:00 +0000
commita6cd6d5a892f5a0501c0e81fe583bd64befff5d0 (patch)
tree428d4f7b10bcda2e08f3b15ae78f53e7f91ab92b /addons/web
parent669bba4694896e29c088d6ce69440d3bef8e9fe5 (diff)
[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
Diffstat (limited to 'addons/web')
-rw-r--r--addons/web/static/src/js/core/utils.js64
-rw-r--r--addons/web/static/tests/core/util_tests.js53
2 files changed, 104 insertions, 13 deletions
diff --git a/addons/web/static/src/js/core/utils.js b/addons/web/static/src/js/core/utils.js
index 560f3a94466..17ce0e080b2 100644
--- a/addons/web/static/src/js/core/utils.js
+++ b/addons/web/static/src/js/core/utils.js
@@ -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 = {
/**
@@ -304,6 +326,31 @@ var utils = {
});
},
/**
+ * 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).
*
* @param {number} number
@@ -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 {
diff --git a/addons/web/static/tests/core/util_tests.js b/addons/web/static/tests/core/util_tests.js
index da6ac0d7737..6115833e4f2 100644
--- a/addons/web/static/tests/core/util_tests.js
+++ b/addons/web/static/tests/core/util_tests.js
@@ -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);