├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .npmrc ├── .travis.yml ├── .verb.md ├── LICENSE ├── README.md ├── examples ├── geojson.js ├── posts.js ├── tags-function.js └── tags-property.js ├── index.js ├── package.json └── test ├── expected ├── categories-tags.js ├── deeply-nested.js ├── insanely-nested.js ├── issue-10.js ├── nested.js └── tags-array.js ├── fixtures ├── categories-tags.js ├── date-function.js ├── deeply-nested.js ├── insanely-nested.js ├── issue-10.js ├── nested.js └── tags-array.js └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | indent_size = 2 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [{**/{actual,fixtures,expected,templates}/**,*.md}] 12 | trim_trailing_whitespace = false 13 | insert_final_newline = false -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "es6": true, 5 | "node": true, 6 | "mocha": true 7 | }, 8 | 9 | "globals": { 10 | "document": false, 11 | "navigator": false, 12 | "window": false 13 | }, 14 | 15 | "rules": { 16 | "accessor-pairs": 2, 17 | "arrow-spacing": [2, { "before": true, "after": true }], 18 | "block-spacing": [2, "always"], 19 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 20 | "comma-dangle": [2, "never"], 21 | "comma-spacing": [2, { "before": false, "after": true }], 22 | "comma-style": [2, "last"], 23 | "constructor-super": 2, 24 | "curly": [2, "multi-line"], 25 | "dot-location": [2, "property"], 26 | "eol-last": 2, 27 | "eqeqeq": [2, "allow-null"], 28 | "generator-star-spacing": [2, { "before": true, "after": true }], 29 | "handle-callback-err": [2, "^(err|error)$" ], 30 | "indent": [2, 2, { "SwitchCase": 1 }], 31 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 32 | "keyword-spacing": [2, { "before": true, "after": true }], 33 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 34 | "new-parens": 2, 35 | "no-array-constructor": 2, 36 | "no-caller": 2, 37 | "no-class-assign": 2, 38 | "no-cond-assign": 2, 39 | "no-const-assign": 2, 40 | "no-control-regex": 2, 41 | "no-debugger": 2, 42 | "no-delete-var": 2, 43 | "no-dupe-args": 2, 44 | "no-dupe-class-members": 2, 45 | "no-dupe-keys": 2, 46 | "no-duplicate-case": 2, 47 | "no-empty-character-class": 2, 48 | "no-eval": 2, 49 | "no-ex-assign": 2, 50 | "no-extend-native": 2, 51 | "no-extra-bind": 2, 52 | "no-extra-boolean-cast": 2, 53 | "no-extra-parens": [2, "functions"], 54 | "no-fallthrough": 2, 55 | "no-floating-decimal": 2, 56 | "no-func-assign": 2, 57 | "no-implied-eval": 2, 58 | "no-inner-declarations": [2, "functions"], 59 | "no-invalid-regexp": 2, 60 | "no-irregular-whitespace": 2, 61 | "no-iterator": 2, 62 | "no-label-var": 2, 63 | "no-labels": 2, 64 | "no-lone-blocks": 2, 65 | "no-mixed-spaces-and-tabs": 2, 66 | "no-multi-spaces": 2, 67 | "no-multi-str": 2, 68 | "no-multiple-empty-lines": [2, { "max": 1 }], 69 | "no-native-reassign": 0, 70 | "no-negated-in-lhs": 2, 71 | "no-new": 2, 72 | "no-new-func": 2, 73 | "no-new-object": 2, 74 | "no-new-require": 2, 75 | "no-new-wrappers": 2, 76 | "no-obj-calls": 2, 77 | "no-octal": 2, 78 | "no-octal-escape": 2, 79 | "no-proto": 0, 80 | "no-redeclare": 2, 81 | "no-regex-spaces": 2, 82 | "no-return-assign": 2, 83 | "no-self-compare": 2, 84 | "no-sequences": 2, 85 | "no-shadow-restricted-names": 2, 86 | "no-spaced-func": 2, 87 | "no-sparse-arrays": 2, 88 | "no-this-before-super": 2, 89 | "no-throw-literal": 2, 90 | "no-trailing-spaces": 0, 91 | "no-undef": 2, 92 | "no-undef-init": 2, 93 | "no-unexpected-multiline": 2, 94 | "no-unneeded-ternary": [2, { "defaultAssignment": false }], 95 | "no-unreachable": 2, 96 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 97 | "no-useless-call": 0, 98 | "no-with": 2, 99 | "one-var": [0, { "initialized": "never" }], 100 | "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], 101 | "padded-blocks": [0, "never"], 102 | "quotes": [2, "single", "avoid-escape"], 103 | "radix": 2, 104 | "semi": [2, "always"], 105 | "semi-spacing": [2, { "before": false, "after": true }], 106 | "space-before-blocks": [2, "always"], 107 | "space-before-function-paren": [2, "never"], 108 | "space-in-parens": [2, "never"], 109 | "space-infix-ops": 2, 110 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 111 | "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], 112 | "use-isnan": 2, 113 | "valid-typeof": 2, 114 | "wrap-iife": [2, "any"], 115 | "yoda": [2, "never"] 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | * text eol=lf 3 | 4 | # binaries 5 | *.ai binary 6 | *.psd binary 7 | *.jpg binary 8 | *.gif binary 9 | *.png binary 10 | *.jpeg binary -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # always ignore files 2 | *.DS_Store 3 | *.sublime-* 4 | 5 | # test related, or directories generated by tests 6 | test/actual 7 | actual 8 | coverage 9 | .nyc* 10 | 11 | # npm 12 | node_modules 13 | npm-debug.log 14 | 15 | # yarn 16 | yarn.lock 17 | yarn-error.log 18 | 19 | # misc 20 | _gh_pages 21 | _draft 22 | _drafts 23 | bower_components 24 | vendor 25 | temp 26 | tmp 27 | TODO.md 28 | 29 | examples/*/dist 30 | examples/*/site 31 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: 3 | - linux 4 | - osx 5 | language: node_js 6 | node_js: 7 | - node 8 | - '8' 9 | - '12' 10 | - '10' 11 | -------------------------------------------------------------------------------- /.verb.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ```js 4 | var groupArray = require('{%= name %}'); 5 | ``` 6 | 7 | ## Examples 8 | 9 | ```js 10 | var arr = [ 11 | {tag: 'one', content: 'A'}, 12 | {tag: 'one', content: 'B'}, 13 | {tag: 'two', content: 'C'}, 14 | {tag: 'two', content: 'D'}, 15 | {tag: 'three', content: 'E'}, 16 | {tag: 'three', content: 'F'} 17 | ]; 18 | 19 | // group by the `tag` property 20 | groupArray(arr, 'tag'); 21 | ``` 22 | 23 | **results in:** 24 | 25 | ```js 26 | { 27 | one: [ 28 | {tag: 'one', content: 'A'}, 29 | {tag: 'one', content: 'B'} 30 | ], 31 | two: [ 32 | {tag: 'two', content: 'C'}, 33 | {tag: 'two', content: 'D'} 34 | ], 35 | three: [ 36 | {tag: 'three', content: 'E'}, 37 | {tag: 'three', content: 'F'} 38 | ] 39 | } 40 | ``` 41 | 42 | **Group by multiple, deeply nested properties** 43 | 44 | 45 | ```js 46 | // given an array of object, like blog posts... 47 | var arr = [ 48 | { data: { year: '2016', tag: 'one', month: 'jan', day: '01'}, content: '...'}, 49 | { data: { year: '2016', tag: 'one', month: 'jan', day: '01'}, content: '...'}, 50 | { data: { year: '2016', tag: 'one', month: 'jan', day: '02'}, content: '...'}, 51 | { data: { year: '2016', tag: 'one', month: 'jan', day: '02'}, content: '...'}, 52 | { data: { year: '2016', tag: 'one', month: 'feb', day: '10'}, content: '...'}, 53 | { data: { year: '2016', tag: 'one', month: 'feb', day: '10'}, content: '...'}, 54 | { data: { year: '2016', tag: 'one', month: 'feb', day: '12'}, content: '...'}, 55 | { data: { year: '2016', tag: 'one', month: 'feb', day: '12'}, content: '...'}, 56 | { data: { year: '2016', tag: 'two', month: 'jan', day: '14'}, content: '...'}, 57 | { data: { year: '2016', tag: 'two', month: 'jan', day: '14'}, content: '...'}, 58 | { data: { year: '2016', tag: 'two', month: 'jan', day: '16'}, content: '...'}, 59 | { data: { year: '2016', tag: 'two', month: 'jan', day: '16'}, content: '...'}, 60 | { data: { year: '2016', tag: 'two', month: 'feb', day: '18'}, content: '...'}, 61 | { data: { year: '2017', tag: 'two', month: 'feb', day: '18'}, content: '...'}, 62 | { data: { year: '2017', tag: 'two', month: 'feb', day: '10'}, content: '...'}, 63 | { data: { year: '2017', tag: 'two', month: 'feb', day: '10'}, content: '...'}, 64 | { data: { year: '2017', tag: 'three', month: 'jan', day: '01'}, content: '...'}, 65 | { data: { year: '2017', tag: 'three', month: 'jan', day: '01'}, content: '...'}, 66 | { data: { year: '2017', tag: 'three', month: 'jan', day: '02'}, content: '...'}, 67 | { data: { year: '2017', tag: 'three', month: 'jan', day: '02'}, content: '...'}, 68 | { data: { year: '2017', tag: 'three', month: 'feb', day: '01'}, content: '...'}, 69 | { data: { year: '2017', tag: 'three', month: 'feb', day: '01'}, content: '...'}, 70 | { data: { year: '2017', tag: 'three', month: 'feb', day: '02'}, content: '...'}, 71 | { data: { year: '2017', tag: 'three', month: 'feb', day: '02'}, content: '...'} 72 | ] 73 | ``` 74 | 75 | Pass a list or array of properties: 76 | 77 | ```js 78 | groupArray(arr, 'data.year', 'data.tag', 'data.month', 'data.day'); 79 | ``` 80 | 81 | **Results in something like this: (abbreviated)** 82 | 83 | ```js 84 | { '2016': 85 | { one: 86 | { jan: 87 | { '01': 88 | [ { data: { year: '2016', tag: 'one', month: 'jan', day: '01' }, 89 | content: '...' }, 90 | { data: { year: '2016', tag: 'one', month: 'jan', day: '01' }, 91 | content: '...' } ], 92 | '02': 93 | [ { data: { year: '2016', tag: 'one', month: 'jan', day: '02' }, 94 | content: '...' }, 95 | { data: { year: '2016', tag: 'one', month: 'jan', day: '02' }, 96 | content: '...' } ] }, 97 | feb: 98 | { '10': 99 | [ { data: { year: '2016', tag: 'one', month: 'feb', day: '10' }, 100 | content: '...' }, 101 | { data: { year: '2016', tag: 'one', month: 'feb', day: '10' }, 102 | content: '...' } ], 103 | '12': 104 | [ { data: { year: '2016', tag: 'one', month: 'feb', day: '12' }, 105 | content: '...' }, 106 | { data: { year: '2016', tag: 'one', month: 'feb', day: '12' }, 107 | content: '...' } ] } }, 108 | two: 109 | { jan: 110 | { '14': 111 | [ { data: { year: '2016', tag: 'two', month: 'jan', day: '14' }, 112 | content: '...' }, 113 | { data: { year: '2016', tag: 'two', month: 'jan', day: '14' }, 114 | content: '...' } ], 115 | '16': 116 | [ { data: { year: '2016', tag: 'two', month: 'jan', day: '16' }, 117 | content: '...' }, 118 | { data: { year: '2016', tag: 'two', month: 'jan', day: '16' }, 119 | content: '...' } ] }, 120 | feb: 121 | { '18': 122 | [ { data: { year: '2016', tag: 'two', month: 'feb', day: '18' }, 123 | content: '...' } ] } } }, 124 | '2017': 125 | { two: 126 | { feb: 127 | { '10': 128 | [ { data: { year: '2017', tag: 'two', month: 'feb', day: '10' }, 129 | content: '...' }, 130 | { data: { year: '2017', tag: 'two', month: 'feb', day: '10' }, 131 | content: '...' } ], 132 | '18': 133 | [ { data: { year: '2017', tag: 'two', month: 'feb', day: '18' }, 134 | content: '...' } ] } }, 135 | three: 136 | { jan: 137 | { '01': 138 | [ { data: { year: '2017', tag: 'three', month: 'jan', day: '01' }, 139 | content: '...' }, 140 | { data: { year: '2017', tag: 'three', month: 'jan', day: '01' }, 141 | content: '...' } ], 142 | '02': 143 | [ { data: { year: '2017', tag: 'three', month: 'jan', day: '02' }, 144 | content: '...' }, 145 | { data: { year: '2017', tag: 'three', month: 'jan', day: '02' }, 146 | content: '...' } ] }, 147 | feb: 148 | { '01': 149 | [ { data: { year: '2017', tag: 'three', month: 'feb', day: '01' }, 150 | content: '...' }, 151 | { data: { year: '2017', tag: 'three', month: 'feb', day: '01' }, 152 | content: '...' } ], 153 | '02': 154 | [ { data: { year: '2017', tag: 'three', month: 'feb', day: '02' }, 155 | content: '...' }, 156 | { data: { year: '2017', tag: 'three', month: 'feb', day: '02' }, 157 | content: '...' } ] } } } } 158 | ``` 159 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 - present, Brian Woodward. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # group-array [![NPM version](https://img.shields.io/npm/v/group-array.svg?style=flat)](https://www.npmjs.com/package/group-array) [![NPM monthly downloads](https://img.shields.io/npm/dm/group-array.svg?style=flat)](https://npmjs.org/package/group-array) [![NPM total downloads](https://img.shields.io/npm/dt/group-array.svg?style=flat)](https://npmjs.org/package/group-array) [![Linux Build Status](https://img.shields.io/travis/doowb/group-array.svg?style=flat&label=Travis)](https://travis-ci.org/doowb/group-array) 2 | 3 | > Group array of objects into lists. 4 | 5 | Please consider following this project's author, [Brian Woodward](https://github.com/doowb), and consider starring the project to show your :heart: and support. 6 | 7 | - [Install](#install) 8 | - [Usage](#usage) 9 | - [Examples](#examples) 10 | - [About](#about) 11 | 12 | _(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_ 13 | 14 | ## Install 15 | 16 | Install with [npm](https://www.npmjs.com/): 17 | 18 | ```sh 19 | $ npm install --save group-array 20 | ``` 21 | 22 | ## Usage 23 | 24 | ```js 25 | var groupArray = require('group-array'); 26 | ``` 27 | 28 | ## Examples 29 | 30 | ```js 31 | var arr = [ 32 | {tag: 'one', content: 'A'}, 33 | {tag: 'one', content: 'B'}, 34 | {tag: 'two', content: 'C'}, 35 | {tag: 'two', content: 'D'}, 36 | {tag: 'three', content: 'E'}, 37 | {tag: 'three', content: 'F'} 38 | ]; 39 | 40 | // group by the `tag` property 41 | groupArray(arr, 'tag'); 42 | ``` 43 | 44 | **results in:** 45 | 46 | ```js 47 | { 48 | one: [ 49 | {tag: 'one', content: 'A'}, 50 | {tag: 'one', content: 'B'} 51 | ], 52 | two: [ 53 | {tag: 'two', content: 'C'}, 54 | {tag: 'two', content: 'D'} 55 | ], 56 | three: [ 57 | {tag: 'three', content: 'E'}, 58 | {tag: 'three', content: 'F'} 59 | ] 60 | } 61 | ``` 62 | 63 | **Group by multiple, deeply nested properties** 64 | 65 | ```js 66 | // given an array of object, like blog posts... 67 | var arr = [ 68 | { data: { year: '2016', tag: 'one', month: 'jan', day: '01'}, content: '...'}, 69 | { data: { year: '2016', tag: 'one', month: 'jan', day: '01'}, content: '...'}, 70 | { data: { year: '2016', tag: 'one', month: 'jan', day: '02'}, content: '...'}, 71 | { data: { year: '2016', tag: 'one', month: 'jan', day: '02'}, content: '...'}, 72 | { data: { year: '2016', tag: 'one', month: 'feb', day: '10'}, content: '...'}, 73 | { data: { year: '2016', tag: 'one', month: 'feb', day: '10'}, content: '...'}, 74 | { data: { year: '2016', tag: 'one', month: 'feb', day: '12'}, content: '...'}, 75 | { data: { year: '2016', tag: 'one', month: 'feb', day: '12'}, content: '...'}, 76 | { data: { year: '2016', tag: 'two', month: 'jan', day: '14'}, content: '...'}, 77 | { data: { year: '2016', tag: 'two', month: 'jan', day: '14'}, content: '...'}, 78 | { data: { year: '2016', tag: 'two', month: 'jan', day: '16'}, content: '...'}, 79 | { data: { year: '2016', tag: 'two', month: 'jan', day: '16'}, content: '...'}, 80 | { data: { year: '2016', tag: 'two', month: 'feb', day: '18'}, content: '...'}, 81 | { data: { year: '2017', tag: 'two', month: 'feb', day: '18'}, content: '...'}, 82 | { data: { year: '2017', tag: 'two', month: 'feb', day: '10'}, content: '...'}, 83 | { data: { year: '2017', tag: 'two', month: 'feb', day: '10'}, content: '...'}, 84 | { data: { year: '2017', tag: 'three', month: 'jan', day: '01'}, content: '...'}, 85 | { data: { year: '2017', tag: 'three', month: 'jan', day: '01'}, content: '...'}, 86 | { data: { year: '2017', tag: 'three', month: 'jan', day: '02'}, content: '...'}, 87 | { data: { year: '2017', tag: 'three', month: 'jan', day: '02'}, content: '...'}, 88 | { data: { year: '2017', tag: 'three', month: 'feb', day: '01'}, content: '...'}, 89 | { data: { year: '2017', tag: 'three', month: 'feb', day: '01'}, content: '...'}, 90 | { data: { year: '2017', tag: 'three', month: 'feb', day: '02'}, content: '...'}, 91 | { data: { year: '2017', tag: 'three', month: 'feb', day: '02'}, content: '...'} 92 | ] 93 | ``` 94 | 95 | Pass a list or array of properties: 96 | 97 | ```js 98 | groupArray(arr, 'data.year', 'data.tag', 'data.month', 'data.day'); 99 | ``` 100 | 101 | **Results in something like this: (abbreviated)** 102 | 103 | ```js 104 | { '2016': 105 | { one: 106 | { jan: 107 | { '01': 108 | [ { data: { year: '2016', tag: 'one', month: 'jan', day: '01' }, 109 | content: '...' }, 110 | { data: { year: '2016', tag: 'one', month: 'jan', day: '01' }, 111 | content: '...' } ], 112 | '02': 113 | [ { data: { year: '2016', tag: 'one', month: 'jan', day: '02' }, 114 | content: '...' }, 115 | { data: { year: '2016', tag: 'one', month: 'jan', day: '02' }, 116 | content: '...' } ] }, 117 | feb: 118 | { '10': 119 | [ { data: { year: '2016', tag: 'one', month: 'feb', day: '10' }, 120 | content: '...' }, 121 | { data: { year: '2016', tag: 'one', month: 'feb', day: '10' }, 122 | content: '...' } ], 123 | '12': 124 | [ { data: { year: '2016', tag: 'one', month: 'feb', day: '12' }, 125 | content: '...' }, 126 | { data: { year: '2016', tag: 'one', month: 'feb', day: '12' }, 127 | content: '...' } ] } }, 128 | two: 129 | { jan: 130 | { '14': 131 | [ { data: { year: '2016', tag: 'two', month: 'jan', day: '14' }, 132 | content: '...' }, 133 | { data: { year: '2016', tag: 'two', month: 'jan', day: '14' }, 134 | content: '...' } ], 135 | '16': 136 | [ { data: { year: '2016', tag: 'two', month: 'jan', day: '16' }, 137 | content: '...' }, 138 | { data: { year: '2016', tag: 'two', month: 'jan', day: '16' }, 139 | content: '...' } ] }, 140 | feb: 141 | { '18': 142 | [ { data: { year: '2016', tag: 'two', month: 'feb', day: '18' }, 143 | content: '...' } ] } } }, 144 | '2017': 145 | { two: 146 | { feb: 147 | { '10': 148 | [ { data: { year: '2017', tag: 'two', month: 'feb', day: '10' }, 149 | content: '...' }, 150 | { data: { year: '2017', tag: 'two', month: 'feb', day: '10' }, 151 | content: '...' } ], 152 | '18': 153 | [ { data: { year: '2017', tag: 'two', month: 'feb', day: '18' }, 154 | content: '...' } ] } }, 155 | three: 156 | { jan: 157 | { '01': 158 | [ { data: { year: '2017', tag: 'three', month: 'jan', day: '01' }, 159 | content: '...' }, 160 | { data: { year: '2017', tag: 'three', month: 'jan', day: '01' }, 161 | content: '...' } ], 162 | '02': 163 | [ { data: { year: '2017', tag: 'three', month: 'jan', day: '02' }, 164 | content: '...' }, 165 | { data: { year: '2017', tag: 'three', month: 'jan', day: '02' }, 166 | content: '...' } ] }, 167 | feb: 168 | { '01': 169 | [ { data: { year: '2017', tag: 'three', month: 'feb', day: '01' }, 170 | content: '...' }, 171 | { data: { year: '2017', tag: 'three', month: 'feb', day: '01' }, 172 | content: '...' } ], 173 | '02': 174 | [ { data: { year: '2017', tag: 'three', month: 'feb', day: '02' }, 175 | content: '...' }, 176 | { data: { year: '2017', tag: 'three', month: 'feb', day: '02' }, 177 | content: '...' } ] } } } } 178 | ``` 179 | 180 | ## About 181 | 182 |
183 | Contributing 184 | 185 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). 186 | 187 |
188 | 189 |
190 | Running Tests 191 | 192 | Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: 193 | 194 | ```sh 195 | $ npm install && npm test 196 | ``` 197 | 198 |
199 | 200 |
201 | Building docs 202 | 203 | _(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ 204 | 205 | To generate the readme, run the following command: 206 | 207 | ```sh 208 | $ npm install -g verbose/verb#dev verb-generate-readme && verb 209 | ``` 210 | 211 |
212 | 213 | ### Related projects 214 | 215 | You might also be interested in these projects: 216 | 217 | * [arr-flatten](https://www.npmjs.com/package/arr-flatten): Recursively flatten an array or arrays. | [homepage](https://github.com/jonschlinkert/arr-flatten "Recursively flatten an array or arrays.") 218 | * [get-value](https://www.npmjs.com/package/get-value): Use property paths like 'a.b.c' to get a nested value from an object. Even works… [more](https://github.com/jonschlinkert/get-value) | [homepage](https://github.com/jonschlinkert/get-value "Use property paths like 'a.b.c' to get a nested value from an object. Even works when keys have dots in them (no other dot-prop library can do this!).") 219 | * [group-object](https://www.npmjs.com/package/group-object): Group object keys and values into lists. | [homepage](https://github.com/doowb/group-object "Group object keys and values into lists.") 220 | * [union-value](https://www.npmjs.com/package/union-value): Set an array of unique values as the property of an object. Supports setting deeply… [more](https://github.com/jonschlinkert/union-value) | [homepage](https://github.com/jonschlinkert/union-value "Set an array of unique values as the property of an object. Supports setting deeply nested properties using using object-paths/dot notation.") 221 | 222 | ### Contributors 223 | 224 | | **Commits** | **Contributor** | 225 | | --- | --- | 226 | | 32 | [doowb](https://github.com/doowb) | 227 | | 13 | [jonschlinkert](https://github.com/jonschlinkert) | 228 | | 2 | [johlym](https://github.com/johlym) | 229 | | 1 | [cperryk](https://github.com/cperryk) | 230 | 231 | ### Author 232 | 233 | **Brian Woodward** 234 | 235 | * [GitHub Profile](https://github.com/doowb) 236 | * [Twitter Profile](https://twitter.com/doowb) 237 | * [LinkedIn Profile](https://linkedin.com/in/woodwardbrian) 238 | 239 | ### License 240 | 241 | Copyright © 2019, [Brian Woodward](https://github.com/doowb). 242 | Released under the [MIT License](LICENSE). 243 | 244 | *** 245 | 246 | _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on July 22, 2019._ -------------------------------------------------------------------------------- /examples/geojson.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // example from https://github.com/doowb/group-array/issues/3 4 | // using geojson data to group into intervals 5 | 6 | const util = require('util'); 7 | const groupArray = require('..'); 8 | 9 | // create a function that will use the property provided to group on 10 | function interval(prop) { 11 | 12 | // create a function that will use the intervals to check the group by property 13 | return intervals => { 14 | 15 | // create custom labels to use for the resulting object keys 16 | let labels = intervals.reduce((acc, val, i) => { 17 | let min = val; 18 | let max = (intervals[i + 1] && intervals[i + 1] - 1) || '*'; 19 | acc[val] = min + ' - ' + max; 20 | return acc; 21 | }, {}); 22 | 23 | // create a function that does the grouping for each item 24 | return item => { 25 | 26 | // value to group by 27 | let val = item[prop]; 28 | 29 | // if the value falls between the interval range, return it 30 | // as an array to make the interval value the key being grouped by 31 | return intervals.filter((int, i) => { 32 | let min = int; 33 | let max = intervals[i+1] || Infinity; 34 | return min <= val && val < max; 35 | }).map(function(int) { 36 | return labels[int]; 37 | }); 38 | }; 39 | }; 40 | } 41 | 42 | // example data to group 43 | let countries = [{ 44 | name: 'a', 45 | population: 23000, 46 | climate: 'cold' 47 | }, { 48 | name: 'b', 49 | population: 34000, 50 | climate: 'hot' 51 | }, { 52 | name: 'c', 53 | population: 55000, 54 | climate: 'cold' 55 | }, { 56 | name: 'd', 57 | population: 66000, 58 | climate: 'pleasant' 59 | }, { 60 | name: 'e', 61 | population: 70000, 62 | climate: 'cold' 63 | }]; 64 | 65 | // example intervals to group on 66 | let intervals = [20000, 50000, 100000]; 67 | 68 | // use population to group by 69 | let population = interval('population'); 70 | 71 | // group on population intervals only 72 | let res1 = groupArray(countries, population(intervals)); 73 | 74 | // group on climates within population intervals 75 | let res2 = groupArray(countries, population(intervals), 'climate'); 76 | 77 | console.log(util.inspect(res1, null, 10)); 78 | console.log(); 79 | console.log(util.inspect(res2, null, 10)); 80 | console.log(); 81 | -------------------------------------------------------------------------------- /examples/posts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const groupBy = require('..'); 5 | 6 | let arr = [ 7 | { data: { year: '2016', tag: 'one', month: 'jan', day: '01' }, content: '...' }, 8 | { data: { year: '2016', tag: 'one', month: 'jan', day: '01' }, content: '...' }, 9 | { data: { year: '2016', tag: 'one', month: 'jan', day: '02' }, content: '...' }, 10 | { data: { year: '2016', tag: 'one', month: 'jan', day: '02' }, content: '...' }, 11 | { data: { year: '2016', tag: 'one', month: 'feb', day: '10' }, content: '...' }, 12 | { data: { year: '2016', tag: 'one', month: 'feb', day: '10' }, content: '...' }, 13 | { data: { year: '2016', tag: 'one', month: 'feb', day: '12' }, content: '...' }, 14 | { data: { year: '2016', tag: 'one', month: 'feb', day: '12' }, content: '...' }, 15 | { data: { year: '2016', tag: 'two', month: 'jan', day: '14' }, content: '...' }, 16 | { data: { year: '2016', tag: 'two', month: 'jan', day: '14' }, content: '...' }, 17 | { data: { year: '2016', tag: 'two', month: 'jan', day: '16' }, content: '...' }, 18 | { data: { year: '2016', tag: 'two', month: 'jan', day: '16' }, content: '...' }, 19 | { data: { year: '2016', tag: 'two', month: 'feb', day: '18' }, content: '...' }, 20 | { data: { year: '2017', tag: 'two', month: 'feb', day: '18' }, content: '...' }, 21 | { data: { year: '2017', tag: 'two', month: 'feb', day: '10' }, content: '...' }, 22 | { data: { year: '2017', tag: 'two', month: 'feb', day: '10' }, content: '...' }, 23 | { data: { year: '2017', tag: 'three', month: 'jan', day: '01' }, content: '...' }, 24 | { data: { year: '2017', tag: 'three', month: 'jan', day: '01' }, content: '...' }, 25 | { data: { year: '2017', tag: 'three', month: 'jan', day: '02' }, content: '...' }, 26 | { data: { year: '2017', tag: 'three', month: 'jan', day: '02' }, content: '...' }, 27 | { data: { year: '2017', tag: 'three', month: 'feb', day: '01' }, content: '...' }, 28 | { data: { year: '2017', tag: 'three', month: 'feb', day: '01' }, content: '...' }, 29 | { data: { year: '2017', tag: 'three', month: 'feb', day: '02' }, content: '...' }, 30 | { data: { year: '2017', tag: 'three', month: 'feb', day: '02' }, content: '...' } 31 | ] 32 | 33 | let res = groupBy(arr, 'data.year', 'data.tag', 'data.month', 'data.day'); 34 | console.log(util.inspect(res, { depth: null })); 35 | -------------------------------------------------------------------------------- /examples/tags-function.js: -------------------------------------------------------------------------------- 1 | const util = require('util'); 2 | const groupArray = require('..'); 3 | let arr = [ 4 | { 5 | categories: { one: [ 'A' ] }, 6 | name: 'Post 1', 7 | content: 'Post 1', 8 | key: 'post-1.md' 9 | }, 10 | { 11 | categories: { one: [ 'A' ], two: [ 'B', 'C' ] }, 12 | name: 'Post 2', 13 | content: 'Post 2', 14 | key: 'post-2.md' 15 | }, 16 | { 17 | categories: { one: [ 'B' ], two: [ 'C', 'D' ] }, 18 | name: 'Post 3', 19 | content: 'Post 3', 20 | key: 'post-3.md' 21 | }, 22 | { 23 | categories: { three: [ 'B' ], four: [ 'E', 'F', 'G' ] }, 24 | name: 'Post 4', 25 | content: 'Post 4', 26 | key: 'post-4.md' 27 | }, 28 | { 29 | categories: { four: [ 'C', 'F' ] }, 30 | name: 'Post 5', 31 | content: 'Post 5', 32 | key: 'post-5.md' 33 | }, 34 | { 35 | categories: { four: [ 'F', 'G' ] }, 36 | name: 'Post 6', 37 | content: 'Post 6', 38 | key: 'post-6.md' 39 | } 40 | ]; 41 | 42 | let res = groupArray(arr, 'categories', obj => { 43 | obj.tags = []; 44 | for (let key in obj.categories) { 45 | if (obj.categories.hasOwnProperty(key)) { 46 | let tags = obj.categories[key]; 47 | tags.forEach(tag => { 48 | if (obj.tags.indexOf(tag) === -1) { 49 | obj.tags.push(tag); 50 | } 51 | }); 52 | } 53 | } 54 | return obj.tags; 55 | }); 56 | 57 | console.log(util.inspect(res, null, 10)); 58 | -------------------------------------------------------------------------------- /examples/tags-property.js: -------------------------------------------------------------------------------- 1 | const util = require('util'); 2 | const groupArray = require('..'); 3 | let arr = [ 4 | { 5 | categories: { one: ['A'] }, 6 | name: 'Post 1', 7 | content: 'Post 1', 8 | key: 'post-1.md' 9 | }, 10 | { 11 | categories: { one: ['A'], two: ['B', 'C'] }, 12 | name: 'Post 2', 13 | content: 'Post 2', 14 | key: 'post-2.md' 15 | }, 16 | { 17 | categories: { one: ['B'], two: ['C', 'D'] }, 18 | name: 'Post 3', 19 | content: 'Post 3', 20 | key: 'post-3.md' 21 | }, 22 | { 23 | categories: { three: ['B'], four: ['E', 'F', 'G'] }, 24 | name: 'Post 4', 25 | content: 'Post 4', 26 | key: 'post-4.md' 27 | }, 28 | { 29 | categories: { four: ['C', 'F'] }, 30 | name: 'Post 5', 31 | content: 'Post 5', 32 | key: 'post-5.md' 33 | }, 34 | { 35 | categories: { four: ['F', 'G'] }, 36 | name: 'Post 6', 37 | content: 'Post 6', 38 | key: 'post-6.md' 39 | } 40 | ]; 41 | 42 | function createTags(items) { 43 | items.forEach(obj => { 44 | obj.tags = []; 45 | for (let key in obj.categories) { 46 | if (obj.categories.hasOwnProperty(key)) { 47 | let tags = obj.categories[key]; 48 | tags.forEach(tag => { 49 | if (obj.tags.indexOf(tag) === -1) { 50 | obj.tags.push(tag); 51 | } 52 | }); 53 | } 54 | } 55 | }); 56 | return items; 57 | } 58 | 59 | let res = groupArray(createTags(arr), 'categories', 'tags'); 60 | console.log(util.inspect(res, null, 10)); 61 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const split = require('split-string'); 4 | const flatten = require('arr-flatten'); 5 | const union = require('union-value'); 6 | const forOwn = require('for-own'); 7 | const typeOf = require('kind-of'); 8 | const get = require('get-value'); 9 | 10 | function groupFn(arr, props) { 11 | if (arr == null) { 12 | return []; 13 | } 14 | 15 | if (!Array.isArray(arr)) { 16 | throw new TypeError('group-array expects an array.'); 17 | } 18 | 19 | if (arguments.length === 1) { 20 | return arr; 21 | } 22 | 23 | let args = flatten([].slice.call(arguments, 1)); 24 | let groups = groupBy(arr, args[0]); 25 | 26 | for (let i = 1; i < args.length; i++) { 27 | toGroup(groups, args[i]); 28 | } 29 | return groups; 30 | } 31 | 32 | function groupBy(arr, prop, key) { 33 | let groups = {}; 34 | 35 | for (let i = 0; i < arr.length; i++) { 36 | let obj = arr[i]; 37 | let val; 38 | 39 | // allow a function to modify the object 40 | // and/or return a val to use 41 | if (typeof prop === 'function') { 42 | val = prop.call(groups, obj, key); 43 | } else { 44 | val = get(obj, prop); 45 | } 46 | 47 | switch (typeOf(val)) { 48 | case 'undefined': 49 | break; 50 | case 'string': 51 | case 'number': 52 | case 'boolean': 53 | union(groups, escape(String(val)), obj); 54 | break; 55 | case 'object': 56 | case 'array': 57 | eachValue(groups, obj, val); 58 | break; 59 | case 'function': 60 | throw new Error('invalid argument type: ' + key); 61 | } 62 | } 63 | return groups; 64 | } 65 | 66 | function eachValue(groups, obj, val) { 67 | if (Array.isArray(val)) { 68 | val.forEach(key => { 69 | union(groups, escape(key), obj); 70 | }); 71 | } else { 72 | forOwn(val, (v, key) => { 73 | union(groups, escape(key), obj); 74 | }); 75 | } 76 | } 77 | 78 | function toGroup(groups, prop) { 79 | forOwn(groups, (val, key) => { 80 | if (!Array.isArray(val)) { 81 | groups[key] = toGroup(val, prop, key); 82 | } else { 83 | groups[key] = groupBy(val, prop, key); 84 | } 85 | }); 86 | return groups; 87 | } 88 | 89 | function escape(str) { 90 | var opts = { 91 | strict: false, 92 | keepEscaping: true, 93 | keepDoubleQuotes: true, 94 | keepSingleQuotes: true 95 | }; 96 | 97 | try { 98 | return split(str, opts).join('\\.'); 99 | } catch (err) { 100 | return str; 101 | } 102 | } 103 | 104 | /** 105 | * Expose `groupArray` 106 | */ 107 | 108 | module.exports = groupFn; 109 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "group-array", 3 | "description": "Group array of objects into lists.", 4 | "version": "1.0.0", 5 | "homepage": "https://github.com/doowb/group-array", 6 | "author": "Brian Woodward (https://github.com/doowb)", 7 | "contributors": [ 8 | "Brian Woodward (https://twitter.com/doowb)", 9 | "Chris Kirk (http://www.chrispkirk.com)", 10 | "Johnathan Lyman (https://johnathan.org)", 11 | "Jon Schlinkert (http://twitter.com/jonschlinkert)" 12 | ], 13 | "repository": "doowb/group-array", 14 | "bugs": { 15 | "url": "https://github.com/doowb/group-array/issues" 16 | }, 17 | "license": "MIT", 18 | "files": [ 19 | "index.js" 20 | ], 21 | "main": "index.js", 22 | "engines": { 23 | "node": ">=8" 24 | }, 25 | "scripts": { 26 | "test": "mocha" 27 | }, 28 | "dependencies": { 29 | "arr-flatten": "^1.1.0", 30 | "for-own": "^1.0.0", 31 | "get-value": "^3.0.1", 32 | "kind-of": "^6.0.2", 33 | "split-string": "^6.1.0", 34 | "union-value": "^2.0.1" 35 | }, 36 | "devDependencies": { 37 | "gulp-format-md": "^2.0.0", 38 | "mocha": "^6.2.0", 39 | "should": "^13.2.3" 40 | }, 41 | "keywords": [ 42 | "array", 43 | "group", 44 | "item", 45 | "list", 46 | "nested", 47 | "prop", 48 | "properties", 49 | "property" 50 | ], 51 | "verb": { 52 | "toc": true, 53 | "layout": "default", 54 | "tasks": [ 55 | "readme" 56 | ], 57 | "plugins": [ 58 | "gulp-format-md" 59 | ], 60 | "related": { 61 | "list": [ 62 | "arr-flatten", 63 | "get-value", 64 | "group-object", 65 | "union-value" 66 | ] 67 | }, 68 | "reflinks": [ 69 | "verb", 70 | "verb-generate-readme" 71 | ], 72 | "lint": { 73 | "reflinks": true 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/expected/categories-tags.js: -------------------------------------------------------------------------------- 1 | module.exports = { one: 2 | { A: 3 | [ { categories: { one: [ 'A' ] }, 4 | name: 'Post 1', 5 | content: 'Post 1', 6 | key: 'post-1.md' }, 7 | { categories: { one: [ 'A' ], two: [ 'B', 'C' ] }, 8 | name: 'Post 2', 9 | content: 'Post 2', 10 | key: 'post-2.md' } ], 11 | B: 12 | [ { categories: { one: [ 'B' ], two: [ 'C', 'D' ] }, 13 | name: 'Post 3', 14 | content: 'Post 3', 15 | key: 'post-3.md'} ] }, 16 | two: 17 | { B: 18 | [ { categories: { one: [ 'A' ], two: [ 'B', 'C' ] }, 19 | name: 'Post 2', 20 | content: 'Post 2', 21 | key: 'post-2.md' } ], 22 | C: 23 | [ { categories: { one: [ 'A' ], two: [ 'B', 'C' ] }, 24 | name: 'Post 2', 25 | content: 'Post 2', 26 | key: 'post-2.md' }, 27 | { categories: { one: [ 'B' ], two: [ 'C', 'D' ] }, 28 | name: 'Post 3', 29 | content: 'Post 3', 30 | key: 'post-3.md' } ], 31 | D: 32 | [ { categories: { one: [ 'B' ], two: [ 'C', 'D' ] }, 33 | name: 'Post 3', 34 | content: 'Post 3', 35 | key: 'post-3.md' } ] }, 36 | three: 37 | { B: 38 | [ { categories: { three: [ 'B' ], four: [ 'E', 'F', 'G' ] }, 39 | name: 'Post 4', 40 | content: 'Post 4', 41 | key: 'post-4.md' } ] }, 42 | four: 43 | { E: 44 | [ { categories: { three: [ 'B' ], four: [ 'E', 'F', 'G' ] }, 45 | name: 'Post 4', 46 | content: 'Post 4', 47 | key: 'post-4.md' } ], 48 | F: 49 | [ { categories: { three: [ 'B' ], four: [ 'E', 'F', 'G' ] }, 50 | name: 'Post 4', 51 | content: 'Post 4', 52 | key: 'post-4.md' }, 53 | { categories: { four: [ 'C', 'F' ] }, 54 | name: 'Post 5', 55 | content: 'Post 5', 56 | key: 'post-5.md' }, 57 | { categories: { four: [ 'F', 'G' ] }, 58 | name: 'Post 6', 59 | content: 'Post 6', 60 | key: 'post-6.md' } ], 61 | G: 62 | [ { categories: { three: [ 'B' ], four: [ 'E', 'F', 'G' ] }, 63 | name: 'Post 4', 64 | content: 'Post 4', 65 | key: 'post-4.md' }, 66 | { categories: { four: [ 'F', 'G' ] }, 67 | name: 'Post 6', 68 | content: 'Post 6', 69 | key: 'post-6.md' } ], 70 | C: 71 | [ { categories: { four: [ 'C', 'F' ] }, 72 | name: 'Post 5', 73 | content: 'Post 5', 74 | key: 'post-5.md' } ] } }; 75 | -------------------------------------------------------------------------------- /test/expected/deeply-nested.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | one: 3 | { jan: 4 | { '01': 5 | [ { data: { tag: 'one', month: 'jan', day: '01' }, content: '...' }, 6 | { data: { tag: 'one', month: 'jan', day: '01' }, content: '...' } ], 7 | '02': 8 | [ { data: { tag: 'one', month: 'jan', day: '02' }, content: '...' }, 9 | { data: { tag: 'one', month: 'jan', day: '02' }, content: '...' } ] }, 10 | feb: 11 | { '10': 12 | [ { data: { tag: 'one', month: 'feb', day: '10' }, content: '...' }, 13 | { data: { tag: 'one', month: 'feb', day: '10' }, content: '...' } ], 14 | '12': 15 | [ { data: { tag: 'one', month: 'feb', day: '12' }, content: '...' }, 16 | { data: { tag: 'one', month: 'feb', day: '12' }, content: '...' } ] } }, 17 | two: 18 | { jan: 19 | { '14': 20 | [ { data: { tag: 'two', month: 'jan', day: '14' }, content: '...' }, 21 | { data: { tag: 'two', month: 'jan', day: '14' }, content: '...' } ], 22 | '16': 23 | [ { data: { tag: 'two', month: 'jan', day: '16' }, content: '...' }, 24 | { data: { tag: 'two', month: 'jan', day: '16' }, content: '...' } ] }, 25 | feb: 26 | { '10': 27 | [ { data: { tag: 'two', month: 'feb', day: '10' }, content: '...' }, 28 | { data: { tag: 'two', month: 'feb', day: '10' }, content: '...' } ], 29 | '18': 30 | [ { data: { tag: 'two', month: 'feb', day: '18' }, content: '...' }, 31 | { data: { tag: 'two', month: 'feb', day: '18' }, content: '...' } ] } }, 32 | three: 33 | { jan: 34 | { '01': 35 | [ { data: { tag: 'three', month: 'jan', day: '01' }, content: '...' }, 36 | { data: { tag: 'three', month: 'jan', day: '01' }, content: '...' } ], 37 | '02': 38 | [ { data: { tag: 'three', month: 'jan', day: '02' }, content: '...' }, 39 | { data: { tag: 'three', month: 'jan', day: '02' }, content: '...' } ] }, 40 | feb: 41 | { '01': 42 | [ { data: { tag: 'three', month: 'feb', day: '01' }, content: '...' }, 43 | { data: { tag: 'three', month: 'feb', day: '01' }, content: '...' } ], 44 | '02': 45 | [ { data: { tag: 'three', month: 'feb', day: '02' }, content: '...' }, 46 | { data: { tag: 'three', month: 'feb', day: '02' }, content: '...' } ] } } 47 | }; 48 | -------------------------------------------------------------------------------- /test/expected/insanely-nested.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '2014': 3 | { one: 4 | { jan: 5 | { '01': 6 | [ { data: { year: '2014', tag: 'one', month: 'jan', day: '01' }, content: '...' }, 7 | { data: { year: '2014', tag: 'one', month: 'jan', day: '01' }, content: '...' } ], 8 | '02': 9 | [ { data: { year: '2014', tag: 'one', month: 'jan', day: '02' }, content: '...' }, 10 | { data: { year: '2014', tag: 'one', month: 'jan', day: '02' }, content: '...' } ] }, 11 | feb: 12 | { '10': 13 | [ { data: { year: '2014', tag: 'one', month: 'feb', day: '10' }, content: '...' }, 14 | { data: { year: '2014', tag: 'one', month: 'feb', day: '10' }, content: '...' } ], 15 | '12': 16 | [ { data: { year: '2014', tag: 'one', month: 'feb', day: '12' }, content: '...' }, 17 | { data: { year: '2014', tag: 'one', month: 'feb', day: '12' }, content: '...' } ] } }, 18 | two: 19 | { jan: 20 | { '14': 21 | [ { data: { year: '2014', tag: 'two', month: 'jan', day: '14' }, content: '...' }, 22 | { data: { year: '2014', tag: 'two', month: 'jan', day: '14' }, content: '...' } ], 23 | '16': 24 | [ { data: { year: '2014', tag: 'two', month: 'jan', day: '16' }, content: '...' }, 25 | { data: { year: '2014', tag: 'two', month: 'jan', day: '16' }, content: '...' } ] }, 26 | feb: 27 | { '18': 28 | [ { data: { year: '2014', tag: 'two', month: 'feb', day: '18' }, content: '...' } ] } } }, 29 | '2015': 30 | { two: 31 | { feb: 32 | { '10': 33 | [ { data: { year: '2015', tag: 'two', month: 'feb', day: '10' }, content: '...' }, 34 | { data: { year: '2015', tag: 'two', month: 'feb', day: '10' }, content: '...' } ], 35 | '18': 36 | [ { data: { year: '2015', tag: 'two', month: 'feb', day: '18' }, content: '...' } ] } }, 37 | three: 38 | { jan: 39 | { '01': 40 | [ { data: { year: '2015', tag: 'three', month: 'jan', day: '01' }, content: '...' }, 41 | { data: { year: '2015', tag: 'three', month: 'jan', day: '01' }, content: '...' } ], 42 | '02': 43 | [ { data: { year: '2015', tag: 'three', month: 'jan', day: '02' }, content: '...' }, 44 | { data: { year: '2015', tag: 'three', month: 'jan', day: '02' }, content: '...' } ] }, 45 | feb: 46 | { '01': 47 | [ { data: { year: '2015', tag: 'three', month: 'feb', day: '01' }, content: '...' }, 48 | { data: { year: '2015', tag: 'three', month: 'feb', day: '01' }, content: '...' } ], 49 | '02': 50 | [ { data: { year: '2015', tag: 'three', month: 'feb', day: '02' }, content: '...' }, 51 | { data: { year: '2015', tag: 'three', month: 'feb', day: '02' }, content: '...' } ] } } }}; 52 | -------------------------------------------------------------------------------- /test/expected/issue-10.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '/"jsb":s*[/gi/]/gi': [ 3 | { 4 | file: '', 5 | ext: 'js', 6 | startTag: /"js\b":\s*\[/gi, 7 | endTag: /\]/gi, 8 | tagKey: '/"js\\b":\\s*\\[/gi/\\]/gi' 9 | }, 10 | { 11 | file: '', 12 | ext: 'js', 13 | startTag: /"js\b":\s*\[/gi, 14 | endTag: /\]/gi, 15 | tagKey: '/"js\\b":\\s*\\[/gi/\\]/gi' 16 | } 17 | ], 18 | '/"htmlb":s*[/gi/]/gi': [ 19 | { 20 | file: '', 21 | ext: 'html', 22 | startTag: /"html\b":\s*\[/gi, 23 | endTag: /\]/gi, 24 | tagKey: '/"html\\b":\\s*\\[/gi/\\]/gi' 25 | } 26 | ], 27 | '/"cssb":s*[/gi/]/gi': [ 28 | { 29 | file: '', 30 | ext: 'css', 31 | startTag: /"css\b":\s*\[/gi, 32 | endTag: /\]/gi, 33 | tagKey: '/"css\\b":\\s*\\[/gi/\\]/gi' 34 | } 35 | ] 36 | }; -------------------------------------------------------------------------------- /test/expected/nested.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | one: { 3 | jan: [ 4 | {data: {month:'jan', tag: 'one'}, content: '...'}, 5 | {data: {month:'jan', tag: 'one'}, content: '...'} 6 | ], 7 | feb: [ 8 | {data: {month:'feb', tag: 'one'}, content: '...'}, 9 | {data: {month:'feb', tag: 'one'}, content: '...'} 10 | ] 11 | }, 12 | two: { 13 | jan: [ 14 | {data: {month:'jan', tag: 'two'}, content: '...'}, 15 | {data: {month:'jan', tag: 'two'}, content: '...'} 16 | ], 17 | feb: [ 18 | {data: {month:'feb', tag: 'two'}, content: '...'}, 19 | {data: {month:'feb', tag: 'two'}, content: '...'} 20 | ] 21 | }, 22 | three: { 23 | jan: [ 24 | {data: {month:'jan', tag: 'three'}, content: '...'}, 25 | {data: {month:'jan', tag: 'three'}, content: '...'} 26 | ], 27 | feb: [ 28 | {data: {month:'feb', tag: 'three'}, content: '...'}, 29 | {data: {month:'feb', tag: 'three'}, content: '...'} 30 | ] 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /test/expected/tags-array.js: -------------------------------------------------------------------------------- 1 | module.exports = { A: 2 | [ { categories: { one: [ 'A' ] }, 3 | name: 'Post 1', 4 | content: 'Post 1', 5 | key: 'post-1.md', 6 | tags: [ 'A' ] }, 7 | { categories: { one: [ 'A' ], two: [ 'B', 'C' ] }, 8 | name: 'Post 2', 9 | content: 'Post 2', 10 | key: 'post-2.md', 11 | tags: [ 'A', 'B', 'C' ] }, 12 | { categories: { one: [ 'A' ], two: [ 'B', 'C' ] }, 13 | name: 'Post 2', 14 | content: 'Post 2', 15 | key: 'post-2.md', 16 | tags: [ 'A', 'B', 'C' ] } ], 17 | B: 18 | [ { categories: { one: [ 'A' ], two: [ 'B', 'C' ] }, 19 | name: 'Post 2', 20 | content: 'Post 2', 21 | key: 'post-2.md', 22 | tags: [ 'A', 'B', 'C' ] }, 23 | { categories: { one: [ 'B' ], two: [ 'C', 'D' ] }, 24 | name: 'Post 3', 25 | content: 'Post 3', 26 | key: 'post-3.md', 27 | tags: [ 'B', 'C', 'D' ] }, 28 | { categories: { one: [ 'A' ], two: [ 'B', 'C' ] }, 29 | name: 'Post 2', 30 | content: 'Post 2', 31 | key: 'post-2.md', 32 | tags: [ 'A', 'B', 'C' ] }, 33 | { categories: { one: [ 'B' ], two: [ 'C', 'D' ] }, 34 | name: 'Post 3', 35 | content: 'Post 3', 36 | key: 'post-3.md', 37 | tags: [ 'B', 'C', 'D' ] }, 38 | { categories: { three: [ 'B' ], four: [ 'E', 'F', 'G' ] }, 39 | name: 'Post 4', 40 | content: 'Post 4', 41 | key: 'post-4.md', 42 | tags: [ 'B', 'E', 'F', 'G' ] }, 43 | { categories: { three: [ 'B' ], four: [ 'E', 'F', 'G' ] }, 44 | name: 'Post 4', 45 | content: 'Post 4', 46 | key: 'post-4.md', 47 | tags: [ 'B', 'E', 'F', 'G' ] } ], 48 | C: 49 | [ { categories: { one: [ 'A' ], two: [ 'B', 'C' ] }, 50 | name: 'Post 2', 51 | content: 'Post 2', 52 | key: 'post-2.md', 53 | tags: [ 'A', 'B', 'C' ] }, 54 | { categories: { one: [ 'B' ], two: [ 'C', 'D' ] }, 55 | name: 'Post 3', 56 | content: 'Post 3', 57 | key: 'post-3.md', 58 | tags: [ 'B', 'C', 'D' ] }, 59 | { categories: { one: [ 'A' ], two: [ 'B', 'C' ] }, 60 | name: 'Post 2', 61 | content: 'Post 2', 62 | key: 'post-2.md', 63 | tags: [ 'A', 'B', 'C' ] }, 64 | { categories: { one: [ 'B' ], two: [ 'C', 'D' ] }, 65 | name: 'Post 3', 66 | content: 'Post 3', 67 | key: 'post-3.md', 68 | tags: [ 'B', 'C', 'D' ] }, 69 | { categories: { four: [ 'C', 'F' ] }, 70 | name: 'Post 5', 71 | content: 'Post 5', 72 | key: 'post-5.md', 73 | tags: [ 'C', 'F' ] } ], 74 | D: 75 | [ { categories: { one: [ 'B' ], two: [ 'C', 'D' ] }, 76 | name: 'Post 3', 77 | content: 'Post 3', 78 | key: 'post-3.md', 79 | tags: [ 'B', 'C', 'D' ] }, 80 | { categories: { one: [ 'B' ], two: [ 'C', 'D' ] }, 81 | name: 'Post 3', 82 | content: 'Post 3', 83 | key: 'post-3.md', 84 | tags: [ 'B', 'C', 'D' ] } ], 85 | E: 86 | [ { categories: { three: [ 'B' ], four: [ 'E', 'F', 'G' ] }, 87 | name: 'Post 4', 88 | content: 'Post 4', 89 | key: 'post-4.md', 90 | tags: [ 'B', 'E', 'F', 'G' ] }, 91 | { categories: { three: [ 'B' ], four: [ 'E', 'F', 'G' ] }, 92 | name: 'Post 4', 93 | content: 'Post 4', 94 | key: 'post-4.md', 95 | tags: [ 'B', 'E', 'F', 'G' ] } ], 96 | F: 97 | [ { categories: { three: [ 'B' ], four: [ 'E', 'F', 'G' ] }, 98 | name: 'Post 4', 99 | content: 'Post 4', 100 | key: 'post-4.md', 101 | tags: [ 'B', 'E', 'F', 'G' ] }, 102 | { categories: { three: [ 'B' ], four: [ 'E', 'F', 'G' ] }, 103 | name: 'Post 4', 104 | content: 'Post 4', 105 | key: 'post-4.md', 106 | tags: [ 'B', 'E', 'F', 'G' ] }, 107 | { categories: { four: [ 'C', 'F' ] }, 108 | name: 'Post 5', 109 | content: 'Post 5', 110 | key: 'post-5.md', 111 | tags: [ 'C', 'F' ] }, 112 | { categories: { four: [ 'F', 'G' ] }, 113 | name: 'Post 6', 114 | content: 'Post 6', 115 | key: 'post-6.md', 116 | tags: [ 'F', 'G' ] } ], 117 | G: 118 | [ { categories: { three: [ 'B' ], four: [ 'E', 'F', 'G' ] }, 119 | name: 'Post 4', 120 | content: 'Post 4', 121 | key: 'post-4.md', 122 | tags: [ 'B', 'E', 'F', 'G' ] }, 123 | { categories: { three: [ 'B' ], four: [ 'E', 'F', 'G' ] }, 124 | name: 'Post 4', 125 | content: 'Post 4', 126 | key: 'post-4.md', 127 | tags: [ 'B', 'E', 'F', 'G' ] }, 128 | { categories: { four: [ 'F', 'G' ] }, 129 | name: 'Post 6', 130 | content: 'Post 6', 131 | key: 'post-6.md', 132 | tags: [ 'F', 'G' ] } ] } -------------------------------------------------------------------------------- /test/fixtures/categories-tags.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | categories: { 4 | one: ['A'] 5 | }, 6 | name: 'Post 1', 7 | content: 'Post 1', 8 | key: 'post-1.md' 9 | }, 10 | { 11 | categories: { 12 | one: ['A'], 13 | two: ['B', 'C'] 14 | }, 15 | name: 'Post 2', 16 | content: 'Post 2', 17 | key: 'post-2.md' 18 | }, 19 | { 20 | categories: { 21 | one: ['B'], 22 | two: ['C', 'D'] 23 | }, 24 | name: 'Post 3', 25 | content: 'Post 3', 26 | key: 'post-3.md' 27 | }, 28 | { 29 | categories: { 30 | three: ['B'], 31 | four: ['E', 'F', 'G'] 32 | }, 33 | name: 'Post 4', 34 | content: 'Post 4', 35 | key: 'post-4.md' 36 | }, 37 | { 38 | categories: { 39 | four: ['C', 'F'] 40 | }, 41 | name: 'Post 5', 42 | content: 'Post 5', 43 | key: 'post-5.md' 44 | }, 45 | { 46 | categories: { 47 | four: ['F', 'G'] 48 | }, 49 | name: 'Post 6', 50 | content: 'Post 6', 51 | key: 'post-6.md' 52 | }]; 53 | -------------------------------------------------------------------------------- /test/fixtures/date-function.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { data: { tag: 'one', date: 'jan-01'}, content: '...'}, 3 | { data: { tag: 'one', date: 'jan-01'}, content: '...'}, 4 | { data: { tag: 'one', date: 'jan-02'}, content: '...'}, 5 | { data: { tag: 'one', date: 'jan-02'}, content: '...'}, 6 | { data: { tag: 'one', date: 'feb-10'}, content: '...'}, 7 | { data: { tag: 'one', date: 'feb-10'}, content: '...'}, 8 | { data: { tag: 'one', date: 'feb-12'}, content: '...'}, 9 | { data: { tag: 'one', date: 'feb-12'}, content: '...'}, 10 | { data: { tag: 'two', date: 'jan-14'}, content: '...'}, 11 | { data: { tag: 'two', date: 'jan-14'}, content: '...'}, 12 | { data: { tag: 'two', date: 'jan-16'}, content: '...'}, 13 | { data: { tag: 'two', date: 'jan-16'}, content: '...'}, 14 | { data: { tag: 'two', date: 'feb-18'}, content: '...'}, 15 | { data: { tag: 'two', date: 'feb-18'}, content: '...'}, 16 | { data: { tag: 'two', date: 'feb-10'}, content: '...'}, 17 | { data: { tag: 'two', date: 'feb-10'}, content: '...'}, 18 | { data: { tag: 'three', date: 'jan-01'}, content: '...'}, 19 | { data: { tag: 'three', date: 'jan-01'}, content: '...'}, 20 | { data: { tag: 'three', date: 'jan-02'}, content: '...'}, 21 | { data: { tag: 'three', date: 'jan-02'}, content: '...'}, 22 | { data: { tag: 'three', date: 'feb-01'}, content: '...'}, 23 | { data: { tag: 'three', date: 'feb-01'}, content: '...'}, 24 | { data: { tag: 'three', date: 'feb-02'}, content: '...'}, 25 | { data: { tag: 'three', date: 'feb-02'}, content: '...'} 26 | ]; 27 | -------------------------------------------------------------------------------- /test/fixtures/deeply-nested.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { data: { tag: 'one', month: 'jan', day: '01'}, content: '...'}, 3 | { data: { tag: 'one', month: 'jan', day: '01'}, content: '...'}, 4 | { data: { tag: 'one', month: 'jan', day: '02'}, content: '...'}, 5 | { data: { tag: 'one', month: 'jan', day: '02'}, content: '...'}, 6 | { data: { tag: 'one', month: 'feb', day: '10'}, content: '...'}, 7 | { data: { tag: 'one', month: 'feb', day: '10'}, content: '...'}, 8 | { data: { tag: 'one', month: 'feb', day: '12'}, content: '...'}, 9 | { data: { tag: 'one', month: 'feb', day: '12'}, content: '...'}, 10 | { data: { tag: 'two', month: 'jan', day: '14'}, content: '...'}, 11 | { data: { tag: 'two', month: 'jan', day: '14'}, content: '...'}, 12 | { data: { tag: 'two', month: 'jan', day: '16'}, content: '...'}, 13 | { data: { tag: 'two', month: 'jan', day: '16'}, content: '...'}, 14 | { data: { tag: 'two', month: 'feb', day: '18'}, content: '...'}, 15 | { data: { tag: 'two', month: 'feb', day: '18'}, content: '...'}, 16 | { data: { tag: 'two', month: 'feb', day: '10'}, content: '...'}, 17 | { data: { tag: 'two', month: 'feb', day: '10'}, content: '...'}, 18 | { data: { tag: 'three', month: 'jan', day: '01'}, content: '...'}, 19 | { data: { tag: 'three', month: 'jan', day: '01'}, content: '...'}, 20 | { data: { tag: 'three', month: 'jan', day: '02'}, content: '...'}, 21 | { data: { tag: 'three', month: 'jan', day: '02'}, content: '...'}, 22 | { data: { tag: 'three', month: 'feb', day: '01'}, content: '...'}, 23 | { data: { tag: 'three', month: 'feb', day: '01'}, content: '...'}, 24 | { data: { tag: 'three', month: 'feb', day: '02'}, content: '...'}, 25 | { data: { tag: 'three', month: 'feb', day: '02'}, content: '...'} 26 | ]; 27 | -------------------------------------------------------------------------------- /test/fixtures/insanely-nested.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { data: { year: '2014', tag: 'one', month: 'jan', day: '01'}, content: '...'}, 3 | { data: { year: '2014', tag: 'one', month: 'jan', day: '01'}, content: '...'}, 4 | { data: { year: '2014', tag: 'one', month: 'jan', day: '02'}, content: '...'}, 5 | { data: { year: '2014', tag: 'one', month: 'jan', day: '02'}, content: '...'}, 6 | { data: { year: '2014', tag: 'one', month: 'feb', day: '10'}, content: '...'}, 7 | { data: { year: '2014', tag: 'one', month: 'feb', day: '10'}, content: '...'}, 8 | { data: { year: '2014', tag: 'one', month: 'feb', day: '12'}, content: '...'}, 9 | { data: { year: '2014', tag: 'one', month: 'feb', day: '12'}, content: '...'}, 10 | { data: { year: '2014', tag: 'two', month: 'jan', day: '14'}, content: '...'}, 11 | { data: { year: '2014', tag: 'two', month: 'jan', day: '14'}, content: '...'}, 12 | { data: { year: '2014', tag: 'two', month: 'jan', day: '16'}, content: '...'}, 13 | { data: { year: '2014', tag: 'two', month: 'jan', day: '16'}, content: '...'}, 14 | { data: { year: '2014', tag: 'two', month: 'feb', day: '18'}, content: '...'}, 15 | { data: { year: '2015', tag: 'two', month: 'feb', day: '18'}, content: '...'}, 16 | { data: { year: '2015', tag: 'two', month: 'feb', day: '10'}, content: '...'}, 17 | { data: { year: '2015', tag: 'two', month: 'feb', day: '10'}, content: '...'}, 18 | { data: { year: '2015', tag: 'three', month: 'jan', day: '01'}, content: '...'}, 19 | { data: { year: '2015', tag: 'three', month: 'jan', day: '01'}, content: '...'}, 20 | { data: { year: '2015', tag: 'three', month: 'jan', day: '02'}, content: '...'}, 21 | { data: { year: '2015', tag: 'three', month: 'jan', day: '02'}, content: '...'}, 22 | { data: { year: '2015', tag: 'three', month: 'feb', day: '01'}, content: '...'}, 23 | { data: { year: '2015', tag: 'three', month: 'feb', day: '01'}, content: '...'}, 24 | { data: { year: '2015', tag: 'three', month: 'feb', day: '02'}, content: '...'}, 25 | { data: { year: '2015', tag: 'three', month: 'feb', day: '02'}, content: '...'} 26 | ]; 27 | -------------------------------------------------------------------------------- /test/fixtures/issue-10.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | file: '', 4 | ext: 'js', 5 | startTag: /"js\b":\s*\[/gi, 6 | endTag: /\]/gi, 7 | tagKey: '/"js\\b":\\s*\\[/gi/\\]/gi' 8 | }, 9 | { 10 | file: '', 11 | ext: 'html', 12 | startTag: /"html\b":\s*\[/gi, 13 | endTag: /\]/gi, 14 | tagKey: '/"html\\b":\\s*\\[/gi/\\]/gi' 15 | }, 16 | { 17 | file: '', 18 | ext: 'js', 19 | startTag: /"js\b":\s*\[/gi, 20 | endTag: /\]/gi, 21 | tagKey: '/"js\\b":\\s*\\[/gi/\\]/gi' 22 | }, 23 | { 24 | file: '', 25 | ext: 'css', 26 | startTag: /"css\b":\s*\[/gi, 27 | endTag: /\]/gi, 28 | tagKey: '/"css\\b":\\s*\\[/gi/\\]/gi' 29 | } 30 | ]; -------------------------------------------------------------------------------- /test/fixtures/nested.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { data: { tag: 'one', month: 'jan'}, content: '...'}, 3 | { data: { tag: 'one', month: 'jan'}, content: '...'}, 4 | { data: { tag: 'one', month: 'feb'}, content: '...'}, 5 | { data: { tag: 'one', month: 'feb'}, content: '...'}, 6 | { data: { tag: 'two', month: 'jan'}, content: '...'}, 7 | { data: { tag: 'two', month: 'jan'}, content: '...'}, 8 | { data: { tag: 'two', month: 'feb'}, content: '...'}, 9 | { data: { tag: 'two', month: 'feb'}, content: '...'}, 10 | { data: { tag: 'three', month: 'jan'}, content: '...'}, 11 | { data: { tag: 'three', month: 'jan'}, content: '...'}, 12 | { data: { tag: 'three', month: 'feb'}, content: '...'}, 13 | { data: { tag: 'three', month: 'feb'}, content: '...'} 14 | ]; -------------------------------------------------------------------------------- /test/fixtures/tags-array.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | categories: { 4 | one: ['A'] 5 | }, 6 | name: 'Post 1', 7 | content: 'Post 1', 8 | key: 'post-1.md', 9 | tags: ['A'] 10 | }, 11 | { 12 | categories: { 13 | one: ['A'], 14 | two: ['B', 'C'] 15 | }, 16 | name: 'Post 2', 17 | content: 'Post 2', 18 | key: 'post-2.md', 19 | tags: ['A', 'B', 'C'] 20 | }, 21 | { 22 | categories: { 23 | one: ['B'], 24 | two: ['C', 'D'] 25 | }, 26 | name: 'Post 3', 27 | content: 'Post 3', 28 | key: 'post-3.md', 29 | tags: ['B', 'C', 'D'] 30 | }, 31 | { 32 | categories: { 33 | one: ['A'], 34 | two: ['B', 'C'] 35 | }, 36 | name: 'Post 2', 37 | content: 'Post 2', 38 | key: 'post-2.md', 39 | tags: ['A', 'B', 'C'] 40 | }, 41 | { 42 | categories: { 43 | one: ['B'], 44 | two: ['C', 'D'] 45 | }, 46 | name: 'Post 3', 47 | content: 'Post 3', 48 | key: 'post-3.md', 49 | tags: ['B', 'C', 'D'] 50 | }, 51 | { 52 | categories: { 53 | three: ['B'], 54 | four: ['E', 'F', 'G'] 55 | }, 56 | name: 'Post 4', 57 | content: 'Post 4', 58 | key: 'post-4.md', 59 | tags: ['B', 'E', 'F', 'G'] 60 | }, 61 | { 62 | categories: { 63 | three: ['B'], 64 | four: ['E', 'F', 'G'] 65 | }, 66 | name: 'Post 4', 67 | content: 'Post 4', 68 | key: 'post-4.md', 69 | tags: ['B', 'E', 'F', 'G'] 70 | }, 71 | { 72 | categories: { 73 | four: ['C', 'F'] 74 | }, 75 | name: 'Post 5', 76 | content: 'Post 5', 77 | key: 'post-5.md', 78 | tags: ['C', 'F'] 79 | }, 80 | { 81 | categories: { 82 | four: ['F', 'G'] 83 | }, 84 | name: 'Post 6', 85 | content: 'Post 6', 86 | key: 'post-6.md', 87 | tags: ['F', 'G'] 88 | } 89 | ]; 90 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const util = require('util'); 4 | const assert = require('assert'); 5 | const get = require('get-value'); 6 | const forOwn = require('for-own'); 7 | const union = require('union-value'); 8 | 9 | const groupArray = require('..'); 10 | 11 | describe('errors', () => { 12 | it('should throw an error when invalid args are passed:', () => { 13 | assert.throws(() => groupArray(''), { 14 | message: 'group-array expects an array.' 15 | }); 16 | }); 17 | }); 18 | 19 | describe('group-array', () => { 20 | it('should return an empty array when an empty array is passed:', () => { 21 | assert.deepEqual(groupArray([]), []); 22 | }); 23 | 24 | it('should return the original array when no grouping properties are passed:', () => { 25 | assert.deepEqual(groupArray(['a', 'b', 'c']), ['a', 'b', 'c']); 26 | }); 27 | 28 | it('should create groups based on the value of the specified property', () => { 29 | let arr = [ 30 | { tag: 'one', content: 'A' }, 31 | { tag: 'one', content: 'B' }, 32 | { tag: 'two', content: 'C' }, 33 | { tag: 'two', content: 'D' }, 34 | { tag: 'three', content: 'E' }, 35 | { tag: 'three', content: 'F' } 36 | ]; 37 | 38 | let actual = groupArray(arr, 'tag'); 39 | 40 | assert.deepEqual(actual, { 41 | one: [ 42 | { tag: 'one', content: 'A' }, 43 | { tag: 'one', content: 'B' } 44 | ], 45 | two: [ 46 | { tag: 'two', content: 'C' }, 47 | { tag: 'two', content: 'D' } 48 | ], 49 | three: [ 50 | { tag: 'three', content: 'E' }, 51 | { tag: 'three', content: 'F' } 52 | ], 53 | }); 54 | }); 55 | 56 | it('should create groups based on the value of the specified property when the value has dots', () => { 57 | let arr = [ 58 | { tag: 'one.foo', content: 'A' }, 59 | { tag: 'one.foo', content: 'B' }, 60 | { tag: 'two.bar', content: 'C' }, 61 | { tag: 'two.bar', content: 'D' }, 62 | { tag: 'three.baz', content: 'E' }, 63 | { tag: 'three.baz', content: 'F' } 64 | ]; 65 | 66 | let actual = groupArray(arr, 'tag'); 67 | 68 | assert.deepEqual(actual, { 69 | 'one.foo': [ 70 | { tag: 'one.foo', content: 'A' }, 71 | { tag: 'one.foo', content: 'B' } 72 | ], 73 | 'two.bar': [ 74 | { tag: 'two.bar', content: 'C' }, 75 | { tag: 'two.bar', content: 'D' } 76 | ], 77 | 'three.baz': [ 78 | { tag: 'three.baz', content: 'E' }, 79 | { tag: 'three.baz', content: 'F' } 80 | ], 81 | }); 82 | }); 83 | 84 | it('should create groups based on numeric values of the specified property', () => { 85 | let arr = [ 86 | { tag: 1, content: 'A' }, 87 | { tag: 1, content: 'B' }, 88 | { tag: 2, content: 'C' }, 89 | { tag: 2, content: 'D' }, 90 | { tag: 3, content: 'E' }, 91 | { tag: 3, content: 'F' } 92 | ]; 93 | 94 | let actual = groupArray(arr, 'tag'); 95 | 96 | assert.deepEqual(actual, { 97 | '1': [ 98 | { tag: 1, content: 'A' }, 99 | { tag: 1, content: 'B' } 100 | ], 101 | '2': [ 102 | { tag: 2, content: 'C' }, 103 | { tag: 2, content: 'D' } 104 | ], 105 | '3': [ 106 | { tag: 3, content: 'E' }, 107 | { tag: 3, content: 'F' } 108 | ], 109 | }); 110 | }); 111 | 112 | it('should create groups based on boolean values of the specified property', () => { 113 | let arr = [ 114 | { tag: true, content: 'A' }, 115 | { tag: false, content: 'B' }, 116 | { tag: true, content: 'C' }, 117 | { tag: false, content: 'D' }, 118 | { tag: true, content: 'E' }, 119 | { tag: false, content: 'F' } 120 | ]; 121 | 122 | let actual = groupArray(arr, 'tag'); 123 | 124 | assert.deepEqual(actual, { 125 | 'true': [ 126 | { tag: true, content: 'A' }, 127 | { tag: true, content: 'C' }, 128 | { tag: true, content: 'E' }, 129 | ], 130 | 'false': [ 131 | { tag: false, content: 'B' }, 132 | { tag: false, content: 'D' }, 133 | { tag: false, content: 'F' } 134 | ] 135 | }); 136 | }); 137 | 138 | it('should support passing the property as an array:', () => { 139 | let arr = [ 140 | { tag: 'one', content: 'A' }, 141 | { tag: 'one', content: 'B' }, 142 | { tag: 'two', content: 'C' }, 143 | { tag: 'two', content: 'D' }, 144 | { tag: 'three', content: 'E' }, 145 | { tag: 'three', content: 'F' } 146 | ]; 147 | 148 | let actual = groupArray(arr, ['tag']); 149 | 150 | assert.deepEqual(actual, { 151 | one: [ 152 | { tag: 'one', content: 'A' }, 153 | { tag: 'one', content: 'B' } 154 | ], 155 | two: [ 156 | { tag: 'two', content: 'C' }, 157 | { tag: 'two', content: 'D' } 158 | ], 159 | three: [ 160 | { tag: 'three', content: 'E' }, 161 | { tag: 'three', content: 'F' } 162 | ], 163 | }); 164 | }); 165 | 166 | it('should create groups based on the value of nested properties', () => { 167 | let arr = [ 168 | { data: { tag: 'one' }, content: 'A' }, 169 | { data: { tag: 'one' }, content: 'B' }, 170 | { data: { tag: 'two' }, content: 'C' }, 171 | { data: { tag: 'two' }, content: 'D' }, 172 | { data: { tag: 'three' }, content: 'E' }, 173 | { data: { tag: 'three' }, content: 'F' } 174 | ]; 175 | 176 | let actual = groupArray(arr, 'data.tag'); 177 | 178 | assert.deepEqual(actual, { 179 | one: [ 180 | { data: { tag: 'one' }, content: 'A' }, 181 | { data: { tag: 'one' }, content: 'B' } 182 | ], 183 | two: [ 184 | { data: { tag: 'two' }, content: 'C' }, 185 | { data: { tag: 'two' }, content: 'D' } 186 | ], 187 | three: [ 188 | { data: { tag: 'three' }, content: 'E' }, 189 | { data: { tag: 'three' }, content: 'F' } 190 | ], 191 | }); 192 | }); 193 | 194 | it('should create groups based on numeric values of nested properties', () =>{ 195 | let arr = [ 196 | { data: { tag: 1 }, content: 'A' }, 197 | { data: { tag: 1 }, content: 'B' }, 198 | { data: { tag: 2 }, content: 'C' }, 199 | { data: { tag: 2 }, content: 'D' }, 200 | { data: { tag: 3 }, content: 'E' }, 201 | { data: { tag: 3 }, content: 'F' } 202 | ]; 203 | 204 | let actual = groupArray(arr, 'data.tag'); 205 | 206 | assert.deepEqual(actual, { 207 | 1: [ 208 | { data: { tag: 1}, content: 'A' }, 209 | { data: { tag: 1}, content: 'B' } 210 | ], 211 | 2: [ 212 | { data: { tag: 2}, content: 'C' }, 213 | { data: { tag: 2}, content: 'D' } 214 | ], 215 | 3: [ 216 | { data: { tag: 3}, content: 'E' }, 217 | { data: { tag: 3}, content: 'F' } 218 | ], 219 | }); 220 | }); 221 | 222 | it('should create groups from properties with object values:', () => { 223 | let arr = [ 224 | { data: { categories: { one: ['one'], four: ['five', 'six'] }}, content: 'A' }, 225 | { data: { categories: { one: ['one'] }}, content: 'B' }, 226 | { data: { categories: { one: ['two'], four: ['five', 'six'] }}, content: 'C' }, 227 | { data: { categories: { two: ['two'], four: ['five', 'six'] }}, content: 'D' }, 228 | { data: { categories: { two: ['three'], four: ['five', 'six'] }}, content: 'E' }, 229 | { data: { categories: { two: ['three'] }}, content: 'F' } 230 | ]; 231 | 232 | function getChildren(prop) { 233 | return function(obj) { 234 | let val = get(obj, prop); 235 | 236 | forOwn(val, arr => { 237 | arr.forEach(key => { 238 | union(this, key, [obj]); 239 | }); 240 | }); 241 | }; 242 | } 243 | 244 | let actual = groupArray(arr, 'data.categories', getChildren('data.categories')); 245 | assert.deepEqual(Object.keys(actual), ['one', 'four', 'two']); 246 | }); 247 | 248 | it('should create groups and ignore objects without matching property values:', () => { 249 | let arr = [ 250 | { tag: 'one', content: 'A' }, 251 | {content: 'B' }, 252 | { tag: 'two', content: 'C' }, 253 | {content: 'D' }, 254 | { tag: 'three', content: 'E' }, 255 | {content: 'F' } 256 | ]; 257 | 258 | let actual = groupArray(arr, 'tag'); 259 | 260 | assert.deepEqual(actual, { 261 | one: [ 262 | { tag: 'one', content: 'A' } 263 | ], 264 | two: [ 265 | { tag: 'two', content: 'C' } 266 | ], 267 | three: [ 268 | { tag: 'three', content: 'E' } 269 | ], 270 | }); 271 | }); 272 | 273 | it('should support properties with array values:', () => { 274 | let fixture = require('./fixtures/tags-array.js'); 275 | let actual = groupArray(fixture, 'tags'); 276 | assert.deepEqual(actual, require('./expected/tags-array.js')); 277 | }); 278 | 279 | it('should group nested properties with array values that are created by a function:', () => { 280 | let fixture = require('./fixtures/categories-tags.js'); 281 | let actual = groupArray(fixture, 'categories', function(val, key) { 282 | return val.categories[key]; 283 | }); 284 | assert.deepEqual(actual, require('./expected/categories-tags.js')); 285 | }); 286 | 287 | it('should create multiple, nested groups:', () => { 288 | let fixture = require('./fixtures/nested.js'); 289 | let actual = groupArray(fixture, 'data.tag', 'data.month'); 290 | assert.deepEqual(actual, require('./expected/nested.js')); 291 | }); 292 | 293 | it('should create multiple, deeply nested groups:', () => { 294 | let fixture = require('./fixtures/deeply-nested.js'); 295 | let actual = groupArray(fixture, 'data.tag', 'data.month', 'data.day'); 296 | assert.deepEqual(actual, require('./expected/deeply-nested.js')); 297 | }); 298 | 299 | it('should use a function create nested groups for multiple properties:', () => { 300 | let fixture = require('./fixtures/date-function.js'); 301 | let actual = groupArray(fixture, 'data.tag', function(obj) { 302 | let date = obj.data.date.split('-'); 303 | obj.data.month = date[0]; 304 | obj.data.day = date[1]; 305 | delete obj.data.date; 306 | return obj.data.month; 307 | }, function(obj) { 308 | return obj.data.day; 309 | }); 310 | 311 | assert.deepEqual(actual, require('./expected/deeply-nested.js')); 312 | }); 313 | 314 | it('should create multiple, insanely nested groups:', () => { 315 | let fixture = require('./fixtures/insanely-nested.js'); 316 | let actual = groupArray(fixture, 'data.year', 'data.tag', 'data.month', 'data.day'); 317 | assert.deepEqual(actual, require('./expected/insanely-nested.js')); 318 | }); 319 | 320 | it('should support properties as an array:', () => { 321 | let fixture = require('./fixtures/insanely-nested.js'); 322 | let actual = groupArray(fixture, ['data.year', 'data.tag', 'data.month', 'data.day']); 323 | assert.deepEqual(actual, require('./expected/insanely-nested.js')); 324 | }); 325 | }); 326 | 327 | describe('issues', () => { 328 | it('#10 should group keys with double quotes in the values', () => { 329 | let fixture = require('./fixtures/issue-10.js'); 330 | let actual = groupArray(fixture, 'tagKey'); 331 | assert.deepEqual(actual, require('./expected/issue-10.js')); 332 | }); 333 | 334 | it('#11 should group keys with an dot at the end', () => { 335 | let fixture = [{ name: 'foo', value: 1}, { name: 'foo.bar.', value: 2 }]; 336 | let expected = { 337 | foo: [{ name: 'foo', value: 1 }], 338 | 'foo.bar.': [{ name: 'foo.bar.', value: 2 }] 339 | }; 340 | 341 | let actual = groupArray(fixture, 'name'); 342 | assert.deepEqual(actual, expected); 343 | }); 344 | }); 345 | --------------------------------------------------------------------------------