feat: groupby nodule
This commit is contained in:
parent
590f66f67d
commit
6d4f74679f
@ -225,6 +225,12 @@ Only the columns specified in the `structure` object will be exported from the `
|
||||
|
||||
export() // get the joined data from the Nodule
|
||||
|
||||
## GroupByNodule
|
||||
|
||||
The `GroupByNodule` class can take in `Table`s and a single value of `groupByValue` to group rows by a table header. On `export()` this will return an object with keys representing an array of objects (or `rows`)
|
||||
|
||||
The `asTable()` method is overloaded and actually returns an array of new `Table` instances, operating differently than the `Nodule` base class `asTable()`
|
||||
|
||||
## Constant Values
|
||||
|
||||
A set of declared constant variables has been provided for safer typing. Although importing them is not essential, the values they represent are the only options for certain options.
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lovelacejs",
|
||||
"version": "0.1.6",
|
||||
"version": "0.2.0",
|
||||
"description": "Lovelace.js is a modern JavaScript Library to create objects that easily mutate data through relationships, filtering, and tranforming the shape of data.",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
|
65
src/entities/nodules/GroupByNodule.js
Normal file
65
src/entities/nodules/GroupByNodule.js
Normal file
@ -0,0 +1,65 @@
|
||||
import Nodule from '../Nodule.js'
|
||||
import Table from '../Table.js'
|
||||
|
||||
class GroupByNodule extends Nodule {
|
||||
constructor (props) {
|
||||
super (props)
|
||||
this._assignProps(props)
|
||||
}
|
||||
|
||||
/* Overload the Nodule Method
|
||||
Returns an Array of Tables with modified ids */
|
||||
asTable = () => {
|
||||
const exports = this.export()
|
||||
const tables = []
|
||||
for (let key in exports) {
|
||||
const newTableProps = {
|
||||
id: `${this.id}-${key}`,
|
||||
label: `${this.label} by ${key}`,
|
||||
rows: exports[key]
|
||||
}
|
||||
const table = new Table(newTableProps)
|
||||
tables.push(table)
|
||||
}
|
||||
return tables
|
||||
}
|
||||
|
||||
export = () => {
|
||||
const { groupByValue } = this
|
||||
const rows = this.tables.map(t => t.export() ).flat()
|
||||
const groupedByRows = rows.reduce((groups, r) => {
|
||||
const val = r[groupByValue]
|
||||
groups[val] = groups[val] || []
|
||||
groups[val].push(r)
|
||||
return groups
|
||||
}, {})
|
||||
return groupedByRows
|
||||
}
|
||||
|
||||
setGroupByValue = value => {
|
||||
const valueValidation = this._validateGroupByValue(value)
|
||||
if(valueValidation.status === 'ERR') throw valueValidation
|
||||
else this.groupByValue = value
|
||||
}
|
||||
|
||||
_assignProps = props => {
|
||||
if (props.groupByValue) this.setGroupByValue(props.groupByValue)
|
||||
}
|
||||
|
||||
_validateGroupByValue = value => {
|
||||
const err = {
|
||||
status: 'ERR',
|
||||
error: {
|
||||
label: 'Filter Parameter are not valid',
|
||||
messages: []
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value !== 'string') {
|
||||
const valueType = typeof value
|
||||
err.error.messages.push(`GroupBy value was of type ${valueType}, should be a string`)
|
||||
} else return { status: 'OK' }
|
||||
}
|
||||
}
|
||||
|
||||
export default GroupByNodule
|
123
tests/core/nodules/groupByNoduleTests.js
Normal file
123
tests/core/nodules/groupByNoduleTests.js
Normal file
@ -0,0 +1,123 @@
|
||||
import GroupByNodule from '../../../src/entities/nodules/GroupByNodule.js'
|
||||
import Table from '../../../src/entities/Table.js'
|
||||
|
||||
const groupByTest = () => {
|
||||
const expectedOutput = {
|
||||
AshBritt: [
|
||||
{ id: 'abc', data: 'row', contractor: 'AshBritt' },
|
||||
{ id: 'qwe', data: 'lh', contractor: 'AshBritt' },
|
||||
{ id: 'XYZ', data: 'row', contractor: 'AshBritt' }
|
||||
],
|
||||
HeyDay: [
|
||||
{ id: 'XYZ', data: 'row', contractor: 'HeyDay' }
|
||||
]
|
||||
}
|
||||
|
||||
let table = {}
|
||||
try {
|
||||
table = new Table({
|
||||
id: 'XYZ',
|
||||
label: 'Test Table',
|
||||
rows: [
|
||||
{ id: 'abc', data: 'row', contractor: 'AshBritt' },
|
||||
{ id: 'qwe', data: 'lh', contractor: 'AshBritt' },
|
||||
{ id: 'XYZ', data: 'row', contractor: 'AshBritt' },
|
||||
{ id: 'XYZ', data: 'row', contractor: 'HeyDay' },
|
||||
]
|
||||
})
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
|
||||
let groupByNodule = {}
|
||||
try {
|
||||
groupByNodule = new GroupByNodule({
|
||||
id: 'ABC',
|
||||
label: 'Test Group By',
|
||||
tables: [table],
|
||||
groupByValue: 'contractor'
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
return false
|
||||
}
|
||||
|
||||
const groupedRows = groupByNodule.export()
|
||||
if (JSON.stringify(groupedRows) === JSON.stringify(expectedOutput)) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const groupByAsTables = () => {
|
||||
|
||||
const expectedOutput = [
|
||||
{
|
||||
id: 'ABC-AshBritt',
|
||||
label: 'Test Group by AshBritt',
|
||||
rows: [
|
||||
{ id: 'abc', data: 'row', contractor: 'AshBritt' },
|
||||
{ id: 'qwe', data: 'lh', contractor: 'AshBritt' },
|
||||
{ id: 'XYZ', data: 'row', contractor: 'AshBritt' }
|
||||
],
|
||||
headers: [ 'id', 'data', 'contractor' ],
|
||||
type: 'Table',
|
||||
isValid: true
|
||||
},
|
||||
{
|
||||
id: 'ABC-HeyDay',
|
||||
label: 'Test Group by HeyDay',
|
||||
rows: [
|
||||
{ id: 'XYZ', data: 'row', contractor: 'HeyDay' }
|
||||
],
|
||||
headers: [ 'id', 'data', 'contractor' ],
|
||||
type: 'Table',
|
||||
isValid: true
|
||||
}
|
||||
]
|
||||
|
||||
let table = {}
|
||||
try {
|
||||
table = new Table({
|
||||
id: 'XYZ',
|
||||
label: 'Test Table',
|
||||
rows: [
|
||||
{ id: 'abc', data: 'row', contractor: 'AshBritt' },
|
||||
{ id: 'qwe', data: 'lh', contractor: 'AshBritt' },
|
||||
{ id: 'XYZ', data: 'row', contractor: 'AshBritt' },
|
||||
{ id: 'XYZ', data: 'row', contractor: 'HeyDay' },
|
||||
]
|
||||
})
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
|
||||
let groupByNodule = {}
|
||||
try {
|
||||
groupByNodule = new GroupByNodule({
|
||||
id: 'ABC',
|
||||
label: 'Test Group',
|
||||
tables: [table],
|
||||
groupByValue: 'contractor'
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
return false
|
||||
}
|
||||
|
||||
// make checks here
|
||||
const groupedTables = groupByNodule.asTable()
|
||||
const groupedTablesProps = groupedTables.map(t => t.getProperties())
|
||||
if (JSON.stringify(groupedTablesProps) === JSON.stringify(expectedOutput)) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default [
|
||||
{ name: 'Entity | GroupBy Value', test: groupByTest },
|
||||
{ name: 'Entity | GroupBy As Table', test: groupByAsTables },
|
||||
]
|
@ -5,6 +5,7 @@ import noduleTests from '../tests/core/NoduleTests.js'
|
||||
import filterNoduleTests from '../tests/core/nodules/filterNoduleTests.js'
|
||||
import joinNoduleTests from '../tests/core/nodules/joinNoduleTests.js'
|
||||
import transformNoduleTests from '../tests/core/nodules/transformNoduleTests.js'
|
||||
import groupByNoduleTests from '../tests/core/nodules/groupByNoduleTests.js'
|
||||
|
||||
function runTestsAndReturnFailures (tests) {
|
||||
const testTotalCount = tests.length
|
||||
@ -34,7 +35,8 @@ const testsArray = [
|
||||
noduleTests,
|
||||
filterNoduleTests,
|
||||
joinNoduleTests,
|
||||
transformNoduleTests
|
||||
transformNoduleTests,
|
||||
groupByNoduleTests
|
||||
]
|
||||
|
||||
init (testsArray.flat())
|
Loading…
x
Reference in New Issue
Block a user