Skip to content

Instantly share code, notes, and snippets.

@gambitier
Forked from leofavre/deepGroupBy.js
Created January 6, 2023 06:11
Show Gist options
  • Select an option

  • Save gambitier/f94e00c388f9bdb72d28b79dcec92e5b to your computer and use it in GitHub Desktop.

Select an option

Save gambitier/f94e00c388f9bdb72d28b79dcec92e5b to your computer and use it in GitHub Desktop.

Revisions

  1. @leofavre leofavre revised this gist Jun 15, 2017. 1 changed file with 9 additions and 8 deletions.
    17 changes: 9 additions & 8 deletions deepGroupBy.js
    Original file line number Diff line number Diff line change
    @@ -7,16 +7,17 @@
    * strings for iteratees.
    */
    const deepGroupBy = (collection, ...iteratees) => {
    let paths = collection.map(value => iteratees.map(iteratee => iteratee(value))),
    result = {};
    let paths = collection.map(value => iteratees.map(iteratee => iteratee(value))),
    result = {};

    paths.forEach((path, index) => {
    let currentValue = _simpleAt(result, path);
    let newValue = (typeof currentValue === "undefined") ? [collection[index]] : currentValue.concat([collection[index]]);
    _simpleSet(result, path, newValue);
    });
    paths.forEach((path, index) => {
    let currentValue = _simpleAt(result, path) || [],
    newValue = currentValue.concat([collection[index]]);

    return result;
    _simpleSet(result, path, newValue);
    });

    return result;
    };

    const _isPlainObject = arg =>
  2. @leofavre leofavre revised this gist Jun 15, 2017. No changes.
  3. @leofavre leofavre revised this gist Jun 15, 2017. 1 changed file with 31 additions and 93 deletions.
    124 changes: 31 additions & 93 deletions deepGroupBy.js
    Original file line number Diff line number Diff line change
    @@ -1,106 +1,44 @@
    import groupBy from "lodash-es/groupBy";
    import set from "lodash-es/set";
    import at from "lodash-es/at";

    /**
    * Part of [Canivete](http://canivete.leofavre.com/)
    *
    * A recursive implementation of LoDash [`groupBy()`](https://lodash.com/docs/4.17.4#groupBy)
    * that can take one or more iteratees to create nested groups.
    *
    * @category Collection
    *
    * @param {Array.<Object>} collection The array of objects.
    * @param {...Function} [...iteratees] The functions used to group the array of objects by their results.
    * @return {Object} The resulting object.
    *
    * @example
    *
    * const getLength = str => str.length;
    * const getFirstLetter = str => str.slice(0, 1);
    *
    * deepGroupBy(["one", "two", "three"], getLength, getFirstLetter);
    * // => {
    * // => "3": {"o": ["one"], "t": ["two"]},
    * // => "5": {"t": ["three"]}
    * // => }
    *
    * @example
    *
    * const getLength = str => str.length;
    * const getFirstLetter = str => str.slice(0, 1);
    *
    * deepGroupBy(["one", "two", "three"], getFirstLetter, getLength);
    * // => {
    * // => "o": {"3": ["one"]},
    * // => "t": {"3": ["two"], "5": ["three"]}
    * // => }
    *
    * @example
    *
    * const stores = [{
    * "name": "Iguatemi",
    * "city": "Campinas",
    * "state": "SP"
    * }, {
    * "name": "Jardins",
    * "city": "São Paulo",
    * "state": "SP"
    * }, {
    * "name": "Iguatemi",
    * "city": "São Paulo",
    * "state": "SP"
    * }, {
    * "name": "Pedras",
    * "city": "Búzios",
    * "state": "RJ"
    * }, {
    * "name": "Ipanema",
    * "city": "Rio de Janeiro",
    * "state": "RJ"
    * }, {
    * "name": "Leblon",
    * "city": "Rio de Janeiro",
    * "state": "RJ"
    * }, {
    * "name": "ParkShopping",
    * "city": "Brasília",
    * "state": "DF"
    * }];
    * Part of [Canivete](http://canivete.leofavre.com/#deepgroupby)
    *
    * const getStateName = item => item.state;
    * const getCityName = item => item.city;
    *
    * deepGroupBy(stores, getStateName, getCityName);
    * // => {
    * // => "SP": { "Campinas": [...], "São Paulo": [...] },
    * // => "RJ": { "Búzios": [...], "Rio de Janeiro": [...] },
    * // => "DF": { "Brasília": [...] }
    * // => }
    * Groups the contents of an array by one or more iteratees.
    * Unlike Lodash [`groupBy()`](https://lodash.com/docs/4.17.4#groupBy),
    * this function can create nested groups, but cannot receive
    * strings for iteratees.
    */
    const deepGroupBy = (collection, ...iteratees) => {
    let paths = collection.map(value => iteratees.map(iteratee => iteratee(value))),
    result = {};

    const groupBranch = (collection, iteratee, keys = []) => (keys.length === 0) ? groupBy(collection, iteratee) : set(collection, formatPath(keys), groupBy(simpleAt(collection, keys), iteratee));
    const getKeysAt = (collection, keys = []) => (keys.length === 0) ? Object.keys(collection) : Object.keys(simpleAt(collection, keys));
    paths.forEach((path, index) => {
    let currentValue = _simpleAt(result, path);
    let newValue = (typeof currentValue === "undefined") ? [collection[index]] : currentValue.concat([collection[index]]);
    _simpleSet(result, path, newValue);
    });

    const simpleAt = (collection, keys) => at(collection, formatPath(keys))[0];
    const formatPath = (keys = []) => keys.join(".") || undefined;
    return result;
    };

    const doGroupByRecursive = (collection, iteratees = [], keys = []) => {
    if (iteratees.length > 0) {
    let result = groupBranch(collection, iteratees[0], keys);
    const _isPlainObject = arg =>
    arg != null && typeof arg == "object" && arg.constructor == Object;

    getKeysAt(result, keys).forEach(key => {
    doGroupByRecursive(result, iteratees.slice(1), keys.concat([key]));
    });
    const _parsePath = path =>
    Array.isArray(path) ? path : `${path}`.split(".");

    return result;
    }
    const _simpleAt = (obj, path) =>
    _parsePath(path).reduce((obj, key) => {
    return (obj != null && obj.hasOwnProperty(key)) ? obj[key] : undefined;
    }, obj);

    return collection;
    };
    const _simpleSet = (obj, path, value) =>
    _parsePath(path).reduce((obj, key, index, arr) => {
    let isLast = (index === arr.length - 1);

    return doGroupByRecursive(collection, iteratees);
    };
    if (!obj.hasOwnProperty(key) || (!isLast && !_isPlainObject(obj[key]))) {
    obj[key] = {};
    }

    return (!isLast) ? obj[key] : obj[key] = value;
    }, obj);

    export default deepGroupBy;
  4. @leofavre leofavre renamed this gist Jun 14, 2017. 1 changed file with 5 additions and 7 deletions.
    12 changes: 5 additions & 7 deletions recursiveGroupBy.js → deepGroupBy.js
    Original file line number Diff line number Diff line change
    @@ -8,8 +8,6 @@ import at from "lodash-es/at";
    * A recursive implementation of LoDash [`groupBy()`](https://lodash.com/docs/4.17.4#groupBy)
    * that can take one or more iteratees to create nested groups.
    *
    * @todo Refactor and separate functions in their own files.
    *
    * @category Collection
    *
    * @param {Array.<Object>} collection The array of objects.
    @@ -21,7 +19,7 @@ import at from "lodash-es/at";
    * const getLength = str => str.length;
    * const getFirstLetter = str => str.slice(0, 1);
    *
    * recursiveGroupBy(["one", "two", "three"], getLength, getFirstLetter);
    * deepGroupBy(["one", "two", "three"], getLength, getFirstLetter);
    * // => {
    * // => "3": {"o": ["one"], "t": ["two"]},
    * // => "5": {"t": ["three"]}
    @@ -32,7 +30,7 @@ import at from "lodash-es/at";
    * const getLength = str => str.length;
    * const getFirstLetter = str => str.slice(0, 1);
    *
    * recursiveGroupBy(["one", "two", "three"], getFirstLetter, getLength);
    * deepGroupBy(["one", "two", "three"], getFirstLetter, getLength);
    * // => {
    * // => "o": {"3": ["one"]},
    * // => "t": {"3": ["two"], "5": ["three"]}
    @@ -73,14 +71,14 @@ import at from "lodash-es/at";
    * const getStateName = item => item.state;
    * const getCityName = item => item.city;
    *
    * recursiveGroupBy(stores, getStateName, getCityName);
    * deepGroupBy(stores, getStateName, getCityName);
    * // => {
    * // => "SP": { "Campinas": [...], "São Paulo": [...] },
    * // => "RJ": { "Búzios": [...], "Rio de Janeiro": [...] },
    * // => "DF": { "Brasília": [...] }
    * // => }
    */
    const recursiveGroupBy = (collection, ...iteratees) => {
    const deepGroupBy = (collection, ...iteratees) => {

    const groupBranch = (collection, iteratee, keys = []) => (keys.length === 0) ? groupBy(collection, iteratee) : set(collection, formatPath(keys), groupBy(simpleAt(collection, keys), iteratee));
    const getKeysAt = (collection, keys = []) => (keys.length === 0) ? Object.keys(collection) : Object.keys(simpleAt(collection, keys));
    @@ -105,4 +103,4 @@ const recursiveGroupBy = (collection, ...iteratees) => {
    return doGroupByRecursive(collection, iteratees);
    };

    export default recursiveGroupBy;
    export default deepGroupBy;
  5. @leofavre leofavre revised this gist May 21, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion recursiveGroupBy.js
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@ import set from "lodash-es/set";
    import at from "lodash-es/at";

    /**
    * Part of [Canivete](https://leofavre.github.io/canivete/)
    * Part of [Canivete](http://canivete.leofavre.com/)
    *
    * A recursive implementation of LoDash [`groupBy()`](https://lodash.com/docs/4.17.4#groupBy)
    * that can take one or more iteratees to create nested groups.
  6. @leofavre leofavre revised this gist May 17, 2017. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion recursiveGroupBy.js
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@ import set from "lodash-es/set";
    import at from "lodash-es/at";

    /**
    * Part of [Canivete](http://leofavre.github.io/canivete/)
    * Part of [Canivete](https://leofavre.github.io/canivete/)
    *
    * A recursive implementation of LoDash [`groupBy()`](https://lodash.com/docs/4.17.4#groupBy)
    * that can take one or more iteratees to create nested groups.
    @@ -39,6 +39,7 @@ import at from "lodash-es/at";
    * // => }
    *
    * @example
    *
    * const stores = [{
    * "name": "Iguatemi",
    * "city": "Campinas",
  7. @leofavre leofavre revised this gist May 17, 2017. 2 changed files with 107 additions and 80 deletions.
    80 changes: 0 additions & 80 deletions groupByRecursive.js
    Original file line number Diff line number Diff line change
    @@ -1,80 +0,0 @@
    import groupBy from "lodash-es/groupBy";
    import set from "lodash-es/set";
    import at from "lodash-es/at";

    /**
    * A recursive implementation of LoDash [`groupBy()`](https://lodash.com/docs/4.17.4#groupBy)
    * that can take one or many iteratees to create nested groups.
    *
    * @category Collection
    *
    * @param {Array.<Object>} collection The array of objects.
    * @param {...Function} [...iteratees] The functions used to group the array of objects by their results.
    * @return {Object} The resulting object.
    *
    * @example
    * const stores = [{
    * name: "Iguatemi",
    * city: "Campinas",
    * state: "SP"
    * }, {
    * name: "Jardins",
    * city: "São Paulo",
    * state: "SP"
    * }, {
    * name: "Iguatemi",
    * city: "São Paulo",
    * state: "SP"
    * }, {
    * name: "Pedras",
    * city: "Búzios",
    * state: "RJ"
    * }, {
    * name: "Ipanema",
    * city: "Rio de Janeiro",
    * state: "RJ"
    * }, {
    * name: "Leblon",
    * city: "Rio de Janeiro",
    * state: "RJ"
    * }, {
    * name: "ParkShopping",
    * city: "Brasília",
    * state: "DF"
    * }];
    *
    * const getStateName = item => item.state;
    * const getCityName = item => item.city;
    *
    * groupByRecursive(stores, getStateName, getCityName);
    * // => {
    * // => "SP": { "Campinas": [...], "São Paulo": [...] },
    * // => "RJ": { "Búzios": [...], "Rio de Janeiro": [...] },
    * // => "DF": { "Brasília": [...] }
    * // => }
    */
    const groupByRecursive = (collection, ...iteratees) => {
    const doGroup = (collection, iteratee, keys = []) => (keys.length === 0) ? groupBy(collection, iteratee) : set(collection, formatPath(keys), groupBy(simpleAt(collection, keys), iteratee));
    const getKeysAt = (collection, keys = []) => (keys.length === 0) ? Object.keys(collection) : Object.keys(simpleAt(collection, keys));

    const simpleAt = (collection, keys) => at(collection, formatPath(keys))[0];
    const formatPath = (keys = []) => (keys.length === 0) ? undefined : keys.join(".");

    const doGroupByRecursive = (collection, iteratees = [], keys = []) => {
    if (iteratees.length > 0) {
    let result = doGroup(collection, iteratees[0], keys);

    getKeysAt(result, keys).forEach(key => {
    doGroupByRecursive(result, iteratees.slice(1), keys.concat([key]));
    });

    return result;
    }

    return collection;
    };

    return doGroupByRecursive(collection, iteratees);
    };

    export default groupByRecursive;
    107 changes: 107 additions & 0 deletions recursiveGroupBy.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,107 @@
    import groupBy from "lodash-es/groupBy";
    import set from "lodash-es/set";
    import at from "lodash-es/at";

    /**
    * Part of [Canivete](http://leofavre.github.io/canivete/)
    *
    * A recursive implementation of LoDash [`groupBy()`](https://lodash.com/docs/4.17.4#groupBy)
    * that can take one or more iteratees to create nested groups.
    *
    * @todo Refactor and separate functions in their own files.
    *
    * @category Collection
    *
    * @param {Array.<Object>} collection The array of objects.
    * @param {...Function} [...iteratees] The functions used to group the array of objects by their results.
    * @return {Object} The resulting object.
    *
    * @example
    *
    * const getLength = str => str.length;
    * const getFirstLetter = str => str.slice(0, 1);
    *
    * recursiveGroupBy(["one", "two", "three"], getLength, getFirstLetter);
    * // => {
    * // => "3": {"o": ["one"], "t": ["two"]},
    * // => "5": {"t": ["three"]}
    * // => }
    *
    * @example
    *
    * const getLength = str => str.length;
    * const getFirstLetter = str => str.slice(0, 1);
    *
    * recursiveGroupBy(["one", "two", "three"], getFirstLetter, getLength);
    * // => {
    * // => "o": {"3": ["one"]},
    * // => "t": {"3": ["two"], "5": ["three"]}
    * // => }
    *
    * @example
    * const stores = [{
    * "name": "Iguatemi",
    * "city": "Campinas",
    * "state": "SP"
    * }, {
    * "name": "Jardins",
    * "city": "São Paulo",
    * "state": "SP"
    * }, {
    * "name": "Iguatemi",
    * "city": "São Paulo",
    * "state": "SP"
    * }, {
    * "name": "Pedras",
    * "city": "Búzios",
    * "state": "RJ"
    * }, {
    * "name": "Ipanema",
    * "city": "Rio de Janeiro",
    * "state": "RJ"
    * }, {
    * "name": "Leblon",
    * "city": "Rio de Janeiro",
    * "state": "RJ"
    * }, {
    * "name": "ParkShopping",
    * "city": "Brasília",
    * "state": "DF"
    * }];
    *
    * const getStateName = item => item.state;
    * const getCityName = item => item.city;
    *
    * recursiveGroupBy(stores, getStateName, getCityName);
    * // => {
    * // => "SP": { "Campinas": [...], "São Paulo": [...] },
    * // => "RJ": { "Búzios": [...], "Rio de Janeiro": [...] },
    * // => "DF": { "Brasília": [...] }
    * // => }
    */
    const recursiveGroupBy = (collection, ...iteratees) => {

    const groupBranch = (collection, iteratee, keys = []) => (keys.length === 0) ? groupBy(collection, iteratee) : set(collection, formatPath(keys), groupBy(simpleAt(collection, keys), iteratee));
    const getKeysAt = (collection, keys = []) => (keys.length === 0) ? Object.keys(collection) : Object.keys(simpleAt(collection, keys));

    const simpleAt = (collection, keys) => at(collection, formatPath(keys))[0];
    const formatPath = (keys = []) => keys.join(".") || undefined;

    const doGroupByRecursive = (collection, iteratees = [], keys = []) => {
    if (iteratees.length > 0) {
    let result = groupBranch(collection, iteratees[0], keys);

    getKeysAt(result, keys).forEach(key => {
    doGroupByRecursive(result, iteratees.slice(1), keys.concat([key]));
    });

    return result;
    }

    return collection;
    };

    return doGroupByRecursive(collection, iteratees);
    };

    export default recursiveGroupBy;
  8. @leofavre leofavre revised this gist May 4, 2017. No changes.
  9. @leofavre leofavre revised this gist May 4, 2017. 1 changed file with 58 additions and 4 deletions.
    62 changes: 58 additions & 4 deletions groupByRecursive.js
    Original file line number Diff line number Diff line change
    @@ -2,25 +2,79 @@ import groupBy from "lodash-es/groupBy";
    import set from "lodash-es/set";
    import at from "lodash-es/at";

    function groupByRecursive(collection, ...iteratees) {
    /**
    * A recursive implementation of LoDash [`groupBy()`](https://lodash.com/docs/4.17.4#groupBy)
    * that can take one or many iteratees to create nested groups.
    *
    * @category Collection
    *
    * @param {Array.<Object>} collection The array of objects.
    * @param {...Function} [...iteratees] The functions used to group the array of objects by their results.
    * @return {Object} The resulting object.
    *
    * @example
    * const stores = [{
    * name: "Iguatemi",
    * city: "Campinas",
    * state: "SP"
    * }, {
    * name: "Jardins",
    * city: "São Paulo",
    * state: "SP"
    * }, {
    * name: "Iguatemi",
    * city: "São Paulo",
    * state: "SP"
    * }, {
    * name: "Pedras",
    * city: "Búzios",
    * state: "RJ"
    * }, {
    * name: "Ipanema",
    * city: "Rio de Janeiro",
    * state: "RJ"
    * }, {
    * name: "Leblon",
    * city: "Rio de Janeiro",
    * state: "RJ"
    * }, {
    * name: "ParkShopping",
    * city: "Brasília",
    * state: "DF"
    * }];
    *
    * const getStateName = item => item.state;
    * const getCityName = item => item.city;
    *
    * groupByRecursive(stores, getStateName, getCityName);
    * // => {
    * // => "SP": { "Campinas": [...], "São Paulo": [...] },
    * // => "RJ": { "Búzios": [...], "Rio de Janeiro": [...] },
    * // => "DF": { "Brasília": [...] }
    * // => }
    */
    const groupByRecursive = (collection, ...iteratees) => {
    const doGroup = (collection, iteratee, keys = []) => (keys.length === 0) ? groupBy(collection, iteratee) : set(collection, formatPath(keys), groupBy(simpleAt(collection, keys), iteratee));
    const getKeysAt = (collection, keys = []) => (keys.length === 0) ? Object.keys(collection) : Object.keys(simpleAt(collection, keys));

    const simpleAt = (collection, keys) => at(collection, formatPath(keys))[0];
    const formatPath = (keys = []) => (keys.length === 0) ? undefined : keys.join(".");

    function doGroupByRecursive(collection, iteratees = [], keys = []) {
    const doGroupByRecursive = (collection, iteratees = [], keys = []) => {
    if (iteratees.length > 0) {
    let result = doGroup(collection, iteratees[0], keys);

    getKeysAt(result, keys).forEach(key => {
    doGroupByRecursive(result, iteratees.slice(1), keys.concat([key]));
    });

    return result;
    }

    return collection;
    }
    };

    return doGroupByRecursive(collection, iteratees);
    }
    };

    export default groupByRecursive;
  10. @leofavre leofavre revised this gist Apr 4, 2017. No changes.
  11. @leofavre leofavre revised this gist Apr 4, 2017. No changes.
  12. @leofavre leofavre created this gist Apr 4, 2017.
    26 changes: 26 additions & 0 deletions groupByRecursive.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,26 @@
    import groupBy from "lodash-es/groupBy";
    import set from "lodash-es/set";
    import at from "lodash-es/at";

    function groupByRecursive(collection, ...iteratees) {
    const doGroup = (collection, iteratee, keys = []) => (keys.length === 0) ? groupBy(collection, iteratee) : set(collection, formatPath(keys), groupBy(simpleAt(collection, keys), iteratee));
    const getKeysAt = (collection, keys = []) => (keys.length === 0) ? Object.keys(collection) : Object.keys(simpleAt(collection, keys));
    const simpleAt = (collection, keys) => at(collection, formatPath(keys))[0];
    const formatPath = (keys = []) => (keys.length === 0) ? undefined : keys.join(".");

    function doGroupByRecursive(collection, iteratees = [], keys = []) {
    if (iteratees.length > 0) {
    let result = doGroup(collection, iteratees[0], keys);
    getKeysAt(result, keys).forEach(key => {
    doGroupByRecursive(result, iteratees.slice(1), keys.concat([key]));
    });
    return result;
    }

    return collection;
    }

    return doGroupByRecursive(collection, iteratees);
    }

    export default groupByRecursive;