diff --git a/.gitignore b/.gitignore index 19cc2c8..c35f00f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ _dist -_example node_modules .DS_Store .tscache diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100755 index 183d3fa..0000000 --- a/Gruntfile.js +++ /dev/null @@ -1,6 +0,0 @@ -/*global module:false*/ -module.exports = function(grunt) { - const sgc = require('./sharedGruntConfig')(grunt, __dirname, [], 'util'); - grunt.initConfig(sgc); -}; - diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 28baf98..0000000 --- a/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) HelpfulScripts 2015-2018 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index b507a29..0000000 --- a/README.md +++ /dev/null @@ -1,25 +0,0 @@ -*hsdatab* provides a JavaScript-based data management and query mechanism. - -Data is managed in a simple in-memory database that holds data in rows of columns. It autodetermines the types of data held in each column, along with the domain range for each column of data. A simple query language is -provided to filter matching data rows. - -## Usage -``` - -import { data } from 'hsdata'; - -// creating the data -const colNames = ['Name', 'Value', 'Start', 'End']; -const rows = [ - ['Harry', '100', '3/1/14', '11/20/14'], - ['Mary', '1500', '7/1/14', '9/30/14'], - ['Peter', '400', '5/20/14', '4/30/15'], - ['Jane', '700', '11/13/14', '8/15/15'] -]; -const data = new Data({colNames:colNames, rows:rows}); - -// querying the data -const query = {Name:["Peter", "Jane"]}; // query: Name is Peter or Jane -const result = data.filter(query); // returns new dataset with matching rows -console.log(result.getColumn('Name').join(', ')); // > Peter, Jane -``` diff --git a/data/hsDatab.json b/data/hsDatab.json new file mode 100644 index 0000000..db6ebdd --- /dev/null +++ b/data/hsDatab.json @@ -0,0 +1,3285 @@ +{ + "id": 0, + "name": "hsDatab", + "kind": 0, + "flags": {}, + "children": [ + { + "id": 42, + "name": "\"Data\"", + "kind": 1, + "kindString": "External module", + "flags": { + "isExported": true + }, + "originalName": "/Users/sth1pal/Documents/Development/JavaScript/node.js/ts6/dev/hsLibs/standalone/hsDatab/src/Data.ts", + "comment": {}, + "children": [ + { + "id": 56, + "name": "Data", + "kind": 128, + "kindString": "Class", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "# Data\nA simple in-memory database that holds data in rows of columns." + }, + "children": [ + { + "id": 67, + "name": "constructor", + "kind": 512, + "kindString": "Constructor", + "flags": { + "isExported": true + }, + "signatures": [ + { + "id": 68, + "name": "new Data", + "kind": 16384, + "kindString": "Constructor signature", + "flags": {}, + "parameters": [ + { + "id": 69, + "name": "data", + "kind": 32768, + "kindString": "Parameter", + "flags": { + "isOptional": true + }, + "type": { + "type": "reference", + "name": "DataSet", + "id": 43 + } + } + ], + "type": { + "type": "reference", + "name": "Data", + "id": 56 + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 86, + "character": 5 + } + ] + }, + { + "id": 117, + "name": "data", + "kind": 1024, + "kindString": "Property", + "flags": { + "isPrivate": true, + "isExported": true + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 392, + "character": 16 + } + ], + "type": { + "type": "array", + "elementType": { + "type": "reference", + "name": "DataRow", + "id": 152 + } + }, + "defaultValue": " []" + }, + { + "id": 118, + "name": "meta", + "kind": 1024, + "kindString": "Property", + "flags": { + "isPrivate": true, + "isExported": true + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 393, + "character": 16 + } + ], + "type": { + "type": "array", + "elementType": { + "type": "reference", + "name": "MetaStruct", + "id": 50 + } + }, + "defaultValue": " []" + }, + { + "id": 119, + "name": "name", + "kind": 1024, + "kindString": "Property", + "flags": { + "isPrivate": true, + "isExported": true + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 394, + "character": 16 + } + ], + "type": { + "type": "intrinsic", + "name": "string" + } + }, + { + "id": 134, + "name": "allRows", + "kind": 2048, + "kindString": "Method", + "flags": { + "isPrivate": true, + "isExported": true + }, + "signatures": [ + { + "id": 135, + "name": "allRows", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "A generator that provides the specified column value for each row in `Data` in sequence." + }, + "parameters": [ + { + "id": 136, + "name": "column", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "\n" + }, + "type": { + "type": "reference", + "name": "ColumnReference", + "id": 150 + } + } + ], + "type": { + "type": "reference", + "name": "Iterable", + "typeArguments": [ + { + "type": "reference", + "name": "DataVal", + "id": 151 + } + ] + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 493, + "character": 21 + } + ] + }, + { + "id": 104, + "name": "castData", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 105, + "name": "castData", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "type": { + "type": "intrinsic", + "name": "void" + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 260, + "character": 19 + } + ] + }, + { + "id": 141, + "name": "castVal", + "kind": 2048, + "kindString": "Method", + "flags": { + "isPrivate": true, + "isExported": true + }, + "signatures": [ + { + "id": 142, + "name": "castVal", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "returns": "The result of the cast.", + "tags": [ + { + "tag": "description", + "text": "Casts the sample to the specified data type.\n" + } + ] + }, + "parameters": [ + { + "id": 143, + "name": "type", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "The type to cast into. In case of _any_ - i.e. `type`\ndoes not match any of the previous keywords, no casting occurs." + }, + "type": { + "type": "intrinsic", + "name": "string" + } + }, + { + "id": 144, + "name": "val", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "reference", + "name": "DataVal", + "id": 151 + } + } + ], + "type": { + "type": "reference", + "name": "DataVal", + "id": 151 + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 529, + "character": 19 + } + ] + }, + { + "id": 82, + "name": "colAdd", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 83, + "name": "colAdd", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "adds a new column to the data set. if `newCol` already exists,\nthe column index is returned withoput change.", + "returns": "the index for the new column\n" + }, + "parameters": [ + { + "id": 84, + "name": "col", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "the name of the new column" + }, + "type": { + "type": "intrinsic", + "name": "string" + } + } + ], + "type": { + "type": "intrinsic", + "name": "number" + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 140, + "character": 17 + } + ] + }, + { + "id": 85, + "name": "colInitialize", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 86, + "name": "colInitialize", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "initializes the specifed column with values, adding a new column if needed.\nIf `val`is a function, it is called as ```\nval(colValue:DataVal, rowIndex:number, row:DataRow)\n```\n@param col the column to initialize\n@param initializer the value to initialize with, or a function whose return\nvalue is used to initialize the column" + }, + "parameters": [ + { + "id": 87, + "name": "col", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "reference", + "name": "ColumnReference", + "id": 150 + } + }, + { + "id": 88, + "name": "initializer", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "intrinsic", + "name": "any" + } + } + ], + "type": { + "type": "intrinsic", + "name": "void" + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 162, + "character": 24 + } + ] + }, + { + "id": 92, + "name": "colName", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 93, + "name": "colName", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "returns the column name for the specified column.\n`col` can be either an index or a name.", + "returns": "the column name or `undefined`.\n" + }, + "parameters": [ + { + "id": 94, + "name": "col", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "reference", + "name": "ColumnReference", + "id": 150 + } + } + ], + "type": { + "type": "intrinsic", + "name": "string" + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 194, + "character": 18 + } + ] + }, + { + "id": 95, + "name": "colNames", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 96, + "name": "colNames", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "returns the names for all columns.", + "returns": "an array of strings with the names.\n" + }, + "type": { + "type": "array", + "elementType": { + "type": "intrinsic", + "name": "string" + } + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 205, + "character": 19 + } + ] + }, + { + "id": 89, + "name": "colNumber", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 90, + "name": "colNumber", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "returns the column index of the specified column.\n`col` can be either an index or a name.", + "returns": "the column number or `undefined`.\n" + }, + "parameters": [ + { + "id": 91, + "name": "col", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "reference", + "name": "ColumnReference", + "id": 150 + } + } + ], + "type": { + "type": "intrinsic", + "name": "number" + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 179, + "character": 20 + } + ] + }, + { + "id": 97, + "name": "colType", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 98, + "name": "colType", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "returns the column type for the specified column.\n`col` can be either an index or a name.", + "returns": "the column type.\n" + }, + "parameters": [ + { + "id": 99, + "name": "col", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "reference", + "name": "ColumnReference", + "id": 150 + } + } + ], + "type": { + "type": "intrinsic", + "name": "string" + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 215, + "character": 18 + } + ] + }, + { + "id": 75, + "name": "export", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 76, + "name": "export", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "Exports to an object literal" + }, + "type": { + "type": "reference", + "name": "DataSet", + "id": 43 + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 111, + "character": 17 + } + ] + }, + { + "id": 106, + "name": "filter", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 107, + "name": "filter", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "filters this data set and returns a new data set with a\nshallow copy of rows that pass the `condition`.\nSee {@link DataFilters DataFilters} for rules and examples on how to construct conditions.", + "returns": "a new Data object with rows that pass the filter\n" + }, + "parameters": [ + { + "id": 108, + "name": "condition", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "filters" + }, + "type": { + "type": "reference", + "name": "Condition", + "id": 9 + } + } + ], + "type": { + "type": "reference", + "name": "Data", + "id": 56 + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 277, + "character": 17 + } + ] + }, + { + "id": 100, + "name": "findDomain", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 101, + "name": "findDomain", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "modifies `domain` to include all values in column `col`.\nIf no `col` is specified, the range of data indexes is returned.", + "returns": "the updated domain\n" + }, + "parameters": [ + { + "id": 102, + "name": "col", + "kind": 32768, + "kindString": "Parameter", + "flags": { + "isOptional": true + }, + "comment": { + "text": "optional; the column name or index" + }, + "type": { + "type": "reference", + "name": "ColumnReference", + "id": 150 + } + }, + { + "id": 103, + "name": "domain", + "kind": 32768, + "kindString": "Parameter", + "flags": { + "isOptional": true + }, + "comment": { + "text": "optional; the Domain range to update" + }, + "type": { + "type": "reference", + "name": "Domain", + "id": 149 + } + } + ], + "type": { + "type": "reference", + "name": "Domain", + "id": 149 + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 227, + "character": 21 + } + ] + }, + { + "id": 131, + "name": "findType", + "kind": 2048, + "kindString": "Method", + "flags": { + "isPrivate": true, + "isExported": true + }, + "signatures": [ + { + "id": 132, + "name": "findType", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "returns": "the type ('number', 'date', 'percent', 'nominal', 'currency') corresponding to the sample\n", + "tags": [ + { + "tag": "description", + "text": "determines the data type. Supported types are\n```\n'date': sample represents a Date, either as a Date object or a String\n'number': sample represents a number\n'percent': sample represents a percentage (special case of a real number)\n'nominal': sample represents a nominal (ordinal or categorical) value\n```" + } + ] + }, + "parameters": [ + { + "id": 133, + "name": "val", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "the value to bve typed." + }, + "type": { + "type": "reference", + "name": "DataVal", + "id": 151 + } + } + ], + "type": { + "type": "intrinsic", + "name": "string" + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 462, + "character": 20 + } + ] + }, + { + "id": 128, + "name": "findTypes", + "kind": 2048, + "kindString": "Method", + "flags": { + "isPrivate": true, + "isExported": true + }, + "signatures": [ + { + "id": 129, + "name": "findTypes", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "Determines the type of data in `col`. An array of counts is created for all\nencountered types, sorted by descending frequency. THe most likely type in position 0\nof the array is returned.", + "returns": "the most likely type of data in `col`.\n" + }, + "parameters": [ + { + "id": 130, + "name": "col", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "the index of the column to be typed." + }, + "type": { + "type": "reference", + "name": "ColumnReference", + "id": 150 + } + } + ], + "type": { + "type": "intrinsic", + "name": "string" + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 430, + "character": 21 + } + ] + }, + { + "id": 79, + "name": "getColumn", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 80, + "name": "getColumn", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "Returns the values in the specified column as a new array." + }, + "parameters": [ + { + "id": 81, + "name": "col", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "the column to return.\n" + }, + "type": { + "type": "reference", + "name": "ColumnReference", + "id": 150 + } + } + ], + "type": { + "type": "array", + "elementType": { + "type": "reference", + "name": "DataVal", + "id": 151 + } + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 129, + "character": 20 + } + ] + }, + { + "id": 77, + "name": "getData", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 78, + "name": "getData", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "returns the 2D array underlying the data base." + }, + "type": { + "type": "array", + "elementType": { + "type": "reference", + "name": "DataRow", + "id": 152 + } + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 121, + "character": 18 + } + ] + }, + { + "id": 120, + "name": "getMeta", + "kind": 2048, + "kindString": "Method", + "flags": { + "isPrivate": true, + "isExported": true + }, + "signatures": [ + { + "id": 121, + "name": "getMeta", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "parameters": [ + { + "id": 122, + "name": "col", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "reference", + "name": "ColumnReference", + "id": 150 + } + } + ], + "type": { + "type": "reference", + "name": "MetaStruct", + "id": 50 + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 396, + "character": 19 + } + ] + }, + { + "id": 70, + "name": "getName", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 71, + "name": "getName", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "returns": "the `name` field for this data base, if any\n" + }, + "type": { + "type": "intrinsic", + "name": "string" + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 95, + "character": 18 + } + ] + }, + { + "id": 72, + "name": "import", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 73, + "name": "import", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "Imports data from an object literal `data`" + }, + "parameters": [ + { + "id": 74, + "name": "data", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "the data set to import\n" + }, + "type": { + "type": "reference", + "name": "DataSet", + "id": 43 + } + } + ], + "type": { + "type": "intrinsic", + "name": "void" + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 103, + "character": 17 + } + ] + }, + { + "id": 113, + "name": "map", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 114, + "name": "map", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": " Maps one or more columns in each rows of values based\non the result of the `mapFn`, which behaves similarly to the Array.map() method.\nTwo modes are supported:\n# Array Mode\nIf `col` is omitted, the `mapFn` is passed the column arrays per row as parameter.\nThis allows for complex mapping combining conditions across multiple columns.\n```\ndata.map(function(values){\n values[1] = values[3] * values[5];\n return values;\n});\n```\nBe sure to return the `values` array as a result.\n# Column mode\nIf `col` is specified, either as index or by column name, the respective column value is passed\ninto `mapFn`, along with the row index and the entire row array. This allows for simple mapping.\n```\ndata.map('Price', function(value, i, values) {\n return value * 2;\n});\n```", + "returns": "a new Data object containing the mapping.\n" + }, + "parameters": [ + { + "id": 115, + "name": "col", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "the data column, or columns, to apply the mapping to." + }, + "type": { + "type": "union", + "types": [ + { + "type": "reference", + "name": "ColumnReference", + "id": 150 + }, + { + "type": "array", + "elementType": { + "type": "reference", + "name": "ColumnReference", + "id": 150 + } + } + ] + } + }, + { + "id": 116, + "name": "mapFn", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "a function to implement the mapping,\ncalled on each row of the data set in turn as `mapFn(val, i, c, rows)`, where\n- `val`: the column value in the current row\n- `c`: the column index in the current row\n- `i`: the row index\n- `rows`: the rows being iterated over\n` *\nfollows the same specifications as the function passed to Array.map().
\nFor column mode, some predefined map functions can be invoked by providing a\nrespective string instead of a function. The following functions are defined:\n\n\n\n
'noop'replace value with itself, performing no operation.
'cumulate'replace value with the cumulative sum of values up to the current element.
" + }, + "type": { + "type": "union", + "types": [ + { + "type": "intrinsic", + "name": "string" + }, + { + "type": "reference", + "name": "mapFn", + "id": 116 + } + ] + } + } + ], + "type": { + "type": "reference", + "name": "Data", + "id": 56 + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 359, + "character": 14 + } + ] + }, + { + "id": 123, + "name": "setData", + "kind": 2048, + "kindString": "Method", + "flags": { + "isPrivate": true, + "isExported": true + }, + "signatures": [ + { + "id": 124, + "name": "setData", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "sets `data` to the existing data set. If data has previously been set,\n`data` will be added to the end of the list if all `names` match those of the\nexisting set." + }, + "parameters": [ + { + "id": 125, + "name": "data", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "the data to add" + }, + "type": { + "type": "array", + "elementType": { + "type": "reference", + "name": "DataRow", + "id": 152 + } + } + }, + { + "id": 126, + "name": "names", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "an array of names that match the columns" + }, + "type": { + "type": "array", + "elementType": { + "type": "intrinsic", + "name": "string" + } + } + }, + { + "id": 127, + "name": "autoType", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "unless set to false, the method will attempt to determine the\ntype of data and automatically cast data points to their correct value\n" + }, + "type": { + "type": "intrinsic", + "name": "boolean" + }, + "defaultValue": "true" + } + ], + "type": { + "type": "intrinsic", + "name": "void" + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 412, + "character": 19 + } + ] + }, + { + "id": 109, + "name": "sort", + "kind": 2048, + "kindString": "Method", + "flags": { + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 110, + "name": "sort", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "returns": "the Data object in order to allow for chaining.\n", + "tags": [ + { + "tag": "description", + "text": "Sorts the rows of values based on the result of the `sortFn`,\nwhich behaves similarly to the Array.sort method.\nTwo modes are supported:\n# Array Mode\nIf `col` is omitted, the column arrays are passed as samples into the `sortFn`.\nThis allows for complex sorts, combining conditions across multiple columns.\n```\ndata.sort((row1, row2) => row1[5] - row2[5] );\n```\n# Column mode\nIf `col` is specified, either as index or by column name, the respective column value is passed\ninto `sortFn`. This allows filtering for simple conditions.
\n**The specified column will be automatically cast prior to sorting**
\n`data.sort('Date', function(val1, val2) { return val1 - val2; });`" + } + ] + }, + "parameters": [ + { + "id": 111, + "name": "sortFn", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "a function to implement the conditions,\nfollows the same specifications as the function passed to Array.sort().\nSome predefined sort function can be invoked by providing a\nrespective string instead of a function. The following functions are defined:\n\n\n\n
'`ascending`'sort in ascending order.
'`descending`'sort in decending order.
" + }, + "type": { + "type": "union", + "types": [ + { + "type": "intrinsic", + "name": "string" + }, + { + "type": "reference", + "name": "sortFn", + "id": 111 + } + ] + } + }, + { + "id": 112, + "name": "col", + "kind": 32768, + "kindString": "Parameter", + "flags": { + "isOptional": true + }, + "comment": { + "text": "optional; the data column to use for sorting." + }, + "type": { + "type": "reference", + "name": "ColumnReference", + "id": 150 + } + } + ], + "type": { + "type": "reference", + "name": "Data", + "id": 56 + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 307, + "character": 15 + } + ] + }, + { + "id": 137, + "name": "toDate", + "kind": 2048, + "kindString": "Method", + "flags": { + "isPrivate": true, + "isExported": true + }, + "signatures": [ + { + "id": 138, + "name": "toDate", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "returns": "a new Date object parsed from `str`.", + "tags": [ + { + "tag": "description", + "text": "returns a new Date object parsed from `str` and corrects for a difference in\ninterpreting centuries between webkit and mozilla in converting strings to Dates:\nThe string \"15/7/03\" will convert to Jul 15 1903 in Mozilla and July 15 2003 in Webkit.\nIf `limitYear` is not specified this method uses 1970 as the decision date:\nyears 00-69 will be interpreted as 2000-2069, years 70-99 as 1970-1999.\n" + } + ] + }, + "parameters": [ + { + "id": 139, + "name": "val", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "the string to convert to a date" + }, + "type": { + "type": "reference", + "name": "DataVal", + "id": 151 + } + }, + { + "id": 140, + "name": "limitYear", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "the year below which the century is corrected. Defaults to 1970." + }, + "type": { + "type": "intrinsic", + "name": "number" + }, + "defaultValue": "1970" + } + ], + "type": { + "type": "reference", + "name": "Date" + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 510, + "character": 18 + } + ] + }, + { + "id": 63, + "name": "toDataSet", + "kind": 2048, + "kindString": "Method", + "flags": { + "isStatic": true, + "isExported": true, + "isPublic": true + }, + "signatures": [ + { + "id": 64, + "name": "toDataSet", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "parameters": [ + { + "id": 65, + "name": "data", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "reference", + "name": "DataLiteralSet", + "id": 153 + } + }, + { + "id": 66, + "name": "name", + "kind": 32768, + "kindString": "Parameter", + "flags": { + "isOptional": true + }, + "type": { + "type": "intrinsic", + "name": "string" + } + } + ], + "type": { + "type": "reference", + "name": "DataSet", + "id": 43 + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 80, + "character": 27 + } + ] + }, + { + "id": 57, + "name": "type", + "kind": 2097152, + "kindString": "Object literal", + "flags": { + "isStatic": true, + "isExported": true, + "isPublic": true + }, + "children": [ + { + "id": 61, + "name": "currency", + "kind": 32, + "kindString": "Variable", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "currency values. Currently support6ed are values ofg the format '$dd[,ddd]'" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 74, + "character": 16 + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"currency\"" + }, + { + "id": 60, + "name": "date", + "kind": 32, + "kindString": "Variable", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "date values" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 72, + "character": 12 + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"date\"" + }, + { + "id": 59, + "name": "name", + "kind": 32, + "kindString": "Variable", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "nominal values, represented by arbitrary words" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 70, + "character": 12 + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"name\"" + }, + { + "id": 58, + "name": "number", + "kind": 32, + "kindString": "Variable", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "numeric values" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 68, + "character": 14 + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"number\"" + }, + { + "id": 62, + "name": "percent", + "kind": 32, + "kindString": "Variable", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "percent values: 'd%'" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 76, + "character": 15 + } + ], + "type": { + "type": "intrinsic", + "name": "string" + }, + "defaultValue": "\"percent\"" + } + ], + "groups": [ + { + "title": "Variables", + "kind": 32, + "children": [ + 61, + 60, + 59, + 58, + 62 + ] + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 66, + "character": 22 + } + ], + "type": { + "type": "intrinsic", + "name": "object" + } + } + ], + "groups": [ + { + "title": "Constructors", + "kind": 512, + "children": [ + 67 + ] + }, + { + "title": "Properties", + "kind": 1024, + "children": [ + 117, + 118, + 119 + ] + }, + { + "title": "Methods", + "kind": 2048, + "children": [ + 134, + 104, + 141, + 82, + 85, + 92, + 95, + 89, + 97, + 75, + 106, + 100, + 131, + 128, + 79, + 77, + 120, + 70, + 72, + 113, + 123, + 109, + 137, + 63 + ] + }, + { + "title": "Object literals", + "kind": 2097152, + "children": [ + 57 + ] + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 62, + "character": 17 + } + ] + }, + { + "id": 43, + "name": "DataSet", + "kind": 256, + "kindString": "Interface", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "a JSON format data set, using arrays of names and rows" + }, + "children": [ + { + "id": 45, + "name": "colNames", + "kind": 1024, + "kindString": "Property", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "an array of column names. Each name matches the column with the same index in DataRow" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 36, + "character": 12 + } + ], + "type": { + "type": "array", + "elementType": { + "type": "intrinsic", + "name": "string" + } + } + }, + { + "id": 44, + "name": "name", + "kind": 1024, + "kindString": "Property", + "flags": { + "isExported": true, + "isOptional": true + }, + "comment": { + "shortText": "an optional name for the data set" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 34, + "character": 8 + } + ], + "type": { + "type": "intrinsic", + "name": "string" + } + }, + { + "id": 46, + "name": "rows", + "kind": 1024, + "kindString": "Property", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "rows of data" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 38, + "character": 8 + } + ], + "type": { + "type": "array", + "elementType": { + "type": "reference", + "name": "DataRow", + "id": 152 + } + } + } + ], + "groups": [ + { + "title": "Properties", + "kind": 1024, + "children": [ + 45, + 44, + 46 + ] + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 32, + "character": 24 + } + ] + }, + { + "id": 50, + "name": "MetaStruct", + "kind": 256, + "kindString": "Interface", + "flags": {}, + "children": [ + { + "id": 53, + "name": "accessed", + "kind": 1024, + "kindString": "Property", + "flags": {}, + "sources": [ + { + "fileName": "Data.ts", + "line": 49, + "character": 12 + } + ], + "type": { + "type": "intrinsic", + "name": "boolean" + } + }, + { + "id": 54, + "name": "cast", + "kind": 1024, + "kindString": "Property", + "flags": {}, + "sources": [ + { + "fileName": "Data.ts", + "line": 50, + "character": 8 + } + ], + "type": { + "type": "intrinsic", + "name": "boolean" + } + }, + { + "id": 52, + "name": "column", + "kind": 1024, + "kindString": "Property", + "flags": {}, + "sources": [ + { + "fileName": "Data.ts", + "line": 48, + "character": 10 + } + ], + "type": { + "type": "intrinsic", + "name": "number" + } + }, + { + "id": 51, + "name": "name", + "kind": 1024, + "kindString": "Property", + "flags": {}, + "sources": [ + { + "fileName": "Data.ts", + "line": 47, + "character": 8 + } + ], + "type": { + "type": "intrinsic", + "name": "string" + } + }, + { + "id": 55, + "name": "types", + "kind": 1024, + "kindString": "Property", + "flags": {}, + "sources": [ + { + "fileName": "Data.ts", + "line": 51, + "character": 9 + } + ], + "type": { + "type": "array", + "elementType": { + "type": "reference", + "name": "TypeStruct", + "id": 47 + } + } + } + ], + "groups": [ + { + "title": "Properties", + "kind": 1024, + "children": [ + 53, + 54, + 52, + 51, + 55 + ] + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 46, + "character": 20 + } + ] + }, + { + "id": 47, + "name": "TypeStruct", + "kind": 256, + "kindString": "Interface", + "flags": {}, + "children": [ + { + "id": 49, + "name": "count", + "kind": 1024, + "kindString": "Property", + "flags": {}, + "sources": [ + { + "fileName": "Data.ts", + "line": 44, + "character": 42 + } + ], + "type": { + "type": "intrinsic", + "name": "number" + } + }, + { + "id": 48, + "name": "type", + "kind": 1024, + "kindString": "Property", + "flags": {}, + "sources": [ + { + "fileName": "Data.ts", + "line": 44, + "character": 27 + } + ], + "type": { + "type": "intrinsic", + "name": "string" + } + } + ], + "groups": [ + { + "title": "Properties", + "kind": 1024, + "children": [ + 49, + 48 + ] + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 44, + "character": 20 + } + ] + }, + { + "id": 150, + "name": "ColumnReference", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "defines a Column Reference, either as column name or index in the {@link Data.DataRow `DataRow`} array" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 23, + "character": 27 + } + ], + "type": { + "type": "union", + "types": [ + { + "type": "intrinsic", + "name": "number" + }, + { + "type": "intrinsic", + "name": "string" + } + ] + } + }, + { + "id": 153, + "name": "DataLiteralSet", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "a JSON format data set, using an array of {name:value, ...} literals" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 42, + "character": 26 + } + ], + "type": { + "type": "reference", + "name": "Array", + "typeArguments": [ + { + "type": "intrinsic", + "name": "any" + } + ] + } + }, + { + "id": 152, + "name": "DataRow", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "a single row of column values" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 29, + "character": 19 + } + ], + "type": { + "type": "array", + "elementType": { + "type": "reference", + "name": "DataVal", + "id": 151 + } + } + }, + { + "id": 151, + "name": "DataVal", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "a generic data value type, used in the {@link Data.DataRow `DataRow`} array" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 26, + "character": 19 + } + ], + "type": { + "type": "union", + "types": [ + { + "type": "intrinsic", + "name": "number" + }, + { + "type": "intrinsic", + "name": "string" + }, + { + "type": "reference", + "name": "Date" + } + ] + } + }, + { + "id": 147, + "name": "DateDomain", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "defines a Date domain that includes all values of a column" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 14, + "character": 22 + } + ], + "type": { + "type": "tuple", + "elements": [ + { + "type": "reference", + "name": "Date" + }, + { + "type": "reference", + "name": "Date" + } + ] + } + }, + { + "id": 149, + "name": "Domain", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "defines a generic domain that can be any of the typed domains." + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 20, + "character": 18 + } + ], + "type": { + "type": "union", + "types": [ + { + "type": "reference", + "name": "NumDomain", + "id": 146 + }, + { + "type": "reference", + "name": "DateDomain", + "id": 147 + }, + { + "type": "reference", + "name": "NameDomain", + "id": 148 + } + ] + } + }, + { + "id": 148, + "name": "NameDomain", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "defines a categorical domain that includes all values of a column" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 17, + "character": 22 + } + ], + "type": { + "type": "array", + "elementType": { + "type": "intrinsic", + "name": "string" + } + } + }, + { + "id": 146, + "name": "NumDomain", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "defines a numeric domain that includes all values of a column" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 11, + "character": 21 + } + ], + "type": { + "type": "tuple", + "elements": [ + { + "type": "intrinsic", + "name": "number" + }, + { + "type": "intrinsic", + "name": "number" + } + ] + } + }, + { + "id": 145, + "name": "NumRange", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "defines a [min-max] range" + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 8, + "character": 20 + } + ], + "type": { + "type": "tuple", + "elements": [ + { + "type": "intrinsic", + "name": "number" + }, + { + "type": "intrinsic", + "name": "number" + } + ] + } + }, + { + "id": 159, + "name": "mapFn", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 55, + "character": 17 + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 160, + "name": "__type", + "kind": 65536, + "kindString": "Type literal", + "flags": {}, + "signatures": [ + { + "id": 161, + "name": "__call", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "parameters": [ + { + "id": 162, + "name": "colVal", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "intrinsic", + "name": "any" + } + }, + { + "id": 163, + "name": "colIndex", + "kind": 32768, + "kindString": "Parameter", + "flags": { + "isOptional": true + }, + "type": { + "type": "intrinsic", + "name": "number" + } + }, + { + "id": 164, + "name": "rowIndex", + "kind": 32768, + "kindString": "Parameter", + "flags": { + "isOptional": true + }, + "type": { + "type": "intrinsic", + "name": "number" + } + }, + { + "id": 165, + "name": "rows", + "kind": 32768, + "kindString": "Parameter", + "flags": { + "isOptional": true + }, + "type": { + "type": "array", + "elementType": { + "type": "array", + "elementType": { + "type": "intrinsic", + "name": "any" + } + } + } + } + ], + "type": { + "type": "intrinsic", + "name": "any" + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 55, + "character": 20 + } + ] + } + } + }, + { + "id": 154, + "name": "sortFn", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "sources": [ + { + "fileName": "Data.ts", + "line": 54, + "character": 18 + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 155, + "name": "__type", + "kind": 65536, + "kindString": "Type literal", + "flags": {}, + "signatures": [ + { + "id": 156, + "name": "__call", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "parameters": [ + { + "id": 157, + "name": "x", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "intrinsic", + "name": "any" + } + }, + { + "id": 158, + "name": "y", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "intrinsic", + "name": "any" + } + } + ], + "type": { + "type": "intrinsic", + "name": "number" + } + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 54, + "character": 20 + } + ] + } + } + } + ], + "groups": [ + { + "title": "Classes", + "kind": 128, + "children": [ + 56 + ] + }, + { + "title": "Interfaces", + "kind": 256, + "children": [ + 43, + 50, + 47 + ] + }, + { + "title": "Type aliases", + "kind": 4194304, + "children": [ + 150, + 153, + 152, + 151, + 147, + 149, + 148, + 146, + 145, + 159, + 154 + ] + } + ], + "sources": [ + { + "fileName": "Data.ts", + "line": 1, + "character": 0 + } + ] + }, + { + "id": 1, + "name": "\"DataFilters\"", + "kind": 1, + "kindString": "External module", + "flags": { + "isExported": true + }, + "originalName": "/Users/sth1pal/Documents/Development/JavaScript/node.js/ts6/dev/hsLibs/standalone/hsDatab/src/DataFilters.ts", + "comment": { + "shortText": "Use the {@link filter `filter`} function to executes a queries on a {@link Data `Data`} object.\nEach row in the data is checked and those for which `conditions` holds true are returned as a new `Data` object.", + "text": "# Condition construction\n\n### General Condition\n```\nCondition =\n IndexCondition -> conditions on the row index\n|| RecursiveCondition -> (set of) conditions on column values\n```\n\n### IndexCondition\n```\nIndexCondition =\n rowIndex:number -> true if row index matches\n```\n\n### RecursiveCondition\n```\nRecursiveCondition =\n OrCondition -> OR: true if any compound condition is true\n|| AndCondition -> AND: true if all compound conditions are true\n\nOrCondition = -> OR: true if\n AndCondition[] -> any of the AndConditions are true\n|| IndexCondition[] -> any of thr IndexConditions are true\n\nAndCondition = -> AND: true if\n SetAndCondition -> all SetAndConditions are true\n|| TermAndCondition -> or if all TermAndConditions are true\n\nSetAndCondition = { -> AND: true if all sub-conditions are true\n 'or': RecursiveCondition -> true if any RecursiveCondition is true\n|| 'and': RecursiveCondition -> true if all RecursiveCondition are true\n|| 'not': RecursiveCondition -> true if the condition is false\n\nTermAndCondition = { -> Terminal AND: true if all terminal sub-conditions are true\n colDesc:colValue -> true if colValue matches\n|| colDesc:[colValue, ...] -> true if any of the colValues match\n|| colDesc:function(value,row) -> true if function returns true\n}\n\ncolDesc = either column name or index\n```\n\n### Practical Tips\n```\n {'or': [recurCond, ...]} -> OR, same as [recurCond, ...]\n|| {'or': {SetCond, ...}} -> OR, same as [SetCond, ...]\n|| {'and': [recurCond, ...]} -> AND, true if all recurCond are true\n|| {'and': {SetCond, ...}} -> AND, same as {SetCond, ...}\n|| {'not': {SetCond, ...}} -> NAND: true if the SetCond are true\n|| {'not': [recurCond, ...]} -> NOR: true if any of the recurCond are true\n```\n\n# Example\n\n\nconst colNames = ['Name', 'Value', 'Start', 'End'];\nconst rows = [\n ['Harry', '100', '3/1/14', '11/20/14'],\n ['Mary', '1500', '7/1/14', '9/30/14'],\n ['Peter', '400', '5/20/14', '4/30/15'],\n ['Jane', '700', '11/13/14', '8/15/15']\n]\nconst data = new hsdatab.Data({colNames:colNames, rows:rows});\n\nqueries = [\n ['0', undefined, 'undefined query => pass all'],\n ['1', [], 'empty OR: [] => fail all'],\n ['2', {}, 'empty AND: {} => pass all'],\n ['3', 1, '2nd row: pass row 1'],\n ['4', [1,3], '2nd+4th: pass rows: 1 and 3'],\n ['5', {Name:\"Jane\"}, 'Name is Jane'],\n ['6', {1:1500}, 'Column 2 is 1500'],\n ['7', {Name:[\"Peter\", \"Jane\"]}, 'Name is Peter or Jane'],\n ['8', [{Name:\"Peter\"}, {Value:1500}], 'Name is Peter or Value is 1500'],\n ['9', {or:{Name:\"Peter\", Value:1500}}, 'OR: same as 8:'],\n ['A', {or:[{Name:\"Peter\"}, {Value:1500}]}, 'OR: [{Name is Peter}, {Value is 1500}]'],\n ['B', {Name:\"Peter\", Value:400}, 'Name is Peter and Value is 400'],\n ['C', {and:{Name:\"Peter\", Value:400}}, 'AND: {Name is Peter, Value is 400}'],\n ['D', {and:{Name:\"Peter\", Value:1500}}, 'AND: {Name is Peter, Value is 1500}'],\n ['E', {and:[{Name:\"Peter\"}, {Value:400}]}, 'AND:[{Name is Peter}, {Value is 400}]'],\n ['F', {and:[{Name:\"Peter\"}, {Value:1500}]},'AND:[{Name is Peter}, {Value is 1500}]'],\n ['G', {not:{Name:\"Peter\", Value:400}}, 'NAND: not {Name is Peter and Value is 400}'],\n ['H', {not:{Name:\"Peter\", Value:1500}}, 'NAND: not {Name is Peter and Value is 1500}'],\n ['I', {not:[{Name:\"Peter\"}, {Value:1500}]},'NOR: not [{Name is Peter} or {Value is 1500}]'],\n ['J', {Name:(v) => v.length===4}, 'Name has 4 letters']\n];\n\nm.mount(root, {\n view:() => m('', [\n m('h3', 'Given the data set:'),\n m('table#data', [\n m('tr', colNames.map(n => m('th', n))),\n ...rows.map(row => m('tr', [m('td', row[0]),m('td', row[1]),m('td', row[2].toDateString()),m('td', row[3].toDateString())]))\n ]),\n m('h3', 'The following queries yield:'),\n m('table', [\n m('tr', [m('th','#'), m('th','Query'), m('th',\"Live Result, by 'Name' field\")]),\n ...queries.map(q => {\n const result = data.filter(q[1]).getColumn('Name').join(', ');\n return m('tr', [m('td',`${q[0]}:`), m('td',`${q[2]}`), m('td',`[ ${result} ]`)]);\n })\n ])\n ])\n});\n\n\n $exampleID { height: 600px; }\n #data th { width:15%; }\n table {\n font-size: 10pt;\n margin-left: 10px;\n }\n\n\n" + }, + "children": [ + { + "id": 2, + "name": "SetAndCondition", + "kind": 256, + "kindString": "Interface", + "flags": { + "isExported": true + }, + "children": [ + { + "id": 4, + "name": "and", + "kind": 1024, + "kindString": "Property", + "flags": { + "isExported": true, + "isOptional": true + }, + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 140, + "character": 7 + } + ], + "type": { + "type": "reference", + "name": "RecursiveCondition", + "id": 11 + } + }, + { + "id": 5, + "name": "not", + "kind": 1024, + "kindString": "Property", + "flags": { + "isExported": true, + "isOptional": true + }, + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 141, + "character": 7 + } + ], + "type": { + "type": "reference", + "name": "RecursiveCondition", + "id": 11 + } + }, + { + "id": 3, + "name": "or", + "kind": 1024, + "kindString": "Property", + "flags": { + "isExported": true, + "isOptional": true + }, + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 139, + "character": 6 + } + ], + "type": { + "type": "reference", + "name": "RecursiveCondition", + "id": 11 + } + } + ], + "groups": [ + { + "title": "Properties", + "kind": 1024, + "children": [ + 4, + 5, + 3 + ] + } + ], + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 138, + "character": 32 + } + ] + }, + { + "id": 6, + "name": "TermAndCondition", + "kind": 256, + "kindString": "Interface", + "flags": { + "isExported": true + }, + "indexSignature": { + "id": 7, + "name": "__index", + "kind": 8192, + "kindString": "Index signature", + "flags": {}, + "parameters": [ + { + "id": 8, + "name": "colDesc", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "intrinsic", + "name": "string" + } + } + ], + "type": { + "type": "union", + "types": [ + { + "type": "reference", + "name": "DataVal", + "id": 151 + }, + { + "type": "array", + "elementType": { + "type": "reference", + "name": "DataVal", + "id": 151 + } + }, + { + "type": "reference", + "name": "TermConditionFunction", + "id": 14 + } + ] + } + }, + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 144, + "character": 33 + } + ] + }, + { + "id": 13, + "name": "AndCondition", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 136, + "character": 24 + } + ], + "type": { + "type": "union", + "types": [ + { + "type": "reference", + "name": "SetAndCondition", + "id": 2 + }, + { + "type": "reference", + "name": "TermAndCondition", + "id": 6 + } + ] + } + }, + { + "id": 9, + "name": "Condition", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 129, + "character": 21 + } + ], + "type": { + "type": "union", + "types": [ + { + "type": "reference", + "name": "IndexCondition", + "id": 10 + }, + { + "type": "reference", + "name": "RecursiveCondition", + "id": 11 + } + ] + } + }, + { + "id": 10, + "name": "IndexCondition", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "comment": { + "shortText": "true if row index matches the number(s)" + }, + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 132, + "character": 26 + } + ], + "type": { + "type": "intrinsic", + "name": "number" + } + }, + { + "id": 12, + "name": "OrCondition", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 135, + "character": 23 + } + ], + "type": { + "type": "union", + "types": [ + { + "type": "array", + "elementType": { + "type": "reference", + "name": "AndCondition", + "id": 13 + } + }, + { + "type": "array", + "elementType": { + "type": "reference", + "name": "IndexCondition", + "id": 10 + } + } + ] + } + }, + { + "id": 11, + "name": "RecursiveCondition", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 134, + "character": 30 + } + ], + "type": { + "type": "union", + "types": [ + { + "type": "reference", + "name": "AndCondition", + "id": 13 + }, + { + "type": "reference", + "name": "OrCondition", + "id": 12 + } + ] + } + }, + { + "id": 14, + "name": "TermConditionFunction", + "kind": 4194304, + "kindString": "Type alias", + "flags": { + "isExported": true + }, + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 152, + "character": 33 + } + ], + "type": { + "type": "reflection", + "declaration": { + "id": 15, + "name": "__type", + "kind": 65536, + "kindString": "Type literal", + "flags": {}, + "signatures": [ + { + "id": 16, + "name": "__call", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "parameters": [ + { + "id": 17, + "name": "value", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "reference", + "name": "DataVal", + "id": 151 + } + }, + { + "id": 18, + "name": "row", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "reference", + "name": "DataRow", + "id": 152 + } + } + ], + "type": { + "type": "intrinsic", + "name": "boolean" + } + } + ], + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 152, + "character": 35 + } + ] + } + } + }, + { + "id": 38, + "name": "filter", + "kind": 64, + "kindString": "Function", + "flags": { + "isExported": true + }, + "signatures": [ + { + "id": 39, + "name": "filter", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "filters a `Data` object for the given `Condition`s and returns a new `Data` object with those rows for which\n`cond` holds true.", + "returns": "a new `Data` object with the filtered rows\n" + }, + "parameters": [ + { + "id": 40, + "name": "data", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "the `Data` object to filter" + }, + "type": { + "type": "reference", + "name": "Data", + "id": 56 + } + }, + { + "id": 41, + "name": "cond", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "the complex condition to test against" + }, + "type": { + "type": "reference", + "name": "Condition", + "id": 9 + } + } + ], + "type": { + "type": "reference", + "name": "Data", + "id": 56 + } + } + ], + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 234, + "character": 22 + } + ] + }, + { + "id": 28, + "name": "resolveCondition", + "kind": 64, + "kindString": "Function", + "flags": {}, + "signatures": [ + { + "id": 29, + "name": "resolveCondition", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "comment": { + "shortText": "applies `condition` to a row of data and returns `true` if the row passes." + }, + "parameters": [ + { + "id": 30, + "name": "condition", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "the complex condition to test against" + }, + "type": { + "type": "reference", + "name": "Condition", + "id": 9 + } + }, + { + "id": 31, + "name": "row", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "the row values" + }, + "type": { + "type": "reference", + "name": "DataRow", + "id": 152 + } + }, + { + "id": 32, + "name": "r", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "the row index in the data set" + }, + "type": { + "type": "intrinsic", + "name": "number" + } + }, + { + "id": 33, + "name": "colNumber", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "reflection", + "declaration": { + "id": 34, + "name": "__type", + "kind": 65536, + "kindString": "Type literal", + "flags": {}, + "signatures": [ + { + "id": 35, + "name": "__call", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "parameters": [ + { + "id": 36, + "name": "name", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "intrinsic", + "name": "string" + } + } + ], + "type": { + "type": "intrinsic", + "name": "number" + } + } + ], + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 181, + "character": 80 + } + ] + } + } + }, + { + "id": 37, + "name": "and", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "comment": { + "text": "\n" + }, + "type": { + "type": "intrinsic", + "name": "boolean" + }, + "defaultValue": "true" + } + ], + "type": { + "type": "intrinsic", + "name": "boolean" + } + } + ], + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 181, + "character": 25 + } + ] + }, + { + "id": 19, + "name": "resolveTerminalCondition", + "kind": 64, + "kindString": "Function", + "flags": {}, + "signatures": [ + { + "id": 20, + "name": "resolveTerminalCondition", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "parameters": [ + { + "id": 21, + "name": "name", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "intrinsic", + "name": "string" + } + }, + { + "id": 22, + "name": "val", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "intrinsic", + "name": "any" + } + }, + { + "id": 23, + "name": "row", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "reference", + "name": "DataRow", + "id": 152 + } + }, + { + "id": 24, + "name": "colNumber", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "reflection", + "declaration": { + "id": 25, + "name": "__type", + "kind": 65536, + "kindString": "Type literal", + "flags": {}, + "signatures": [ + { + "id": 26, + "name": "__call", + "kind": 4096, + "kindString": "Call signature", + "flags": {}, + "parameters": [ + { + "id": 27, + "name": "name", + "kind": 32768, + "kindString": "Parameter", + "flags": {}, + "type": { + "type": "intrinsic", + "name": "string" + } + } + ], + "type": { + "type": "intrinsic", + "name": "number" + } + } + ], + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 155, + "character": 79 + } + ] + } + } + } + ], + "type": { + "type": "intrinsic", + "name": "boolean" + } + } + ], + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 155, + "character": 33 + } + ] + } + ], + "groups": [ + { + "title": "Interfaces", + "kind": 256, + "children": [ + 2, + 6 + ] + }, + { + "title": "Type aliases", + "kind": 4194304, + "children": [ + 13, + 9, + 10, + 12, + 11, + 14 + ] + }, + { + "title": "Functions", + "kind": 64, + "children": [ + 38, + 28, + 19 + ] + } + ], + "sources": [ + { + "fileName": "DataFilters.ts", + "line": 1, + "character": 0 + } + ] + }, + { + "id": 166, + "name": "\"index\"", + "kind": 1, + "kindString": "External module", + "flags": { + "isExported": true + }, + "originalName": "/Users/sth1pal/Documents/Development/JavaScript/node.js/ts6/dev/hsLibs/standalone/hsDatab/src/index.ts", + "sources": [ + { + "fileName": "index.ts", + "line": 1, + "character": 0 + } + ] + }, + { + "id": 167, + "name": "\"overview\"", + "kind": 1, + "kindString": "External module", + "flags": { + "isExported": true + }, + "originalName": "/Users/sth1pal/Documents/Development/JavaScript/node.js/ts6/dev/hsLibs/standalone/hsDatab/src/overview.ts", + "comment": { + "shortText": "# hsDatab", + "text": "Helpful Scripts framework-independent data management functions. [Github page](https://github.com/HelpfulScripts/hsDatab)\n\n**hsDatab** provides a JavaScript-based data management and query mechanism.\nData is managed in a simple in-memory database that holds data in rows of columns.\nIt autodetermines the types of data held in each column, along with the\ndomain range for each column of data.\nComplex filters can be applied by defining {@link DataFilters `Condition`}s using a simple query object structure.\n\n## Data Types\nsupported {@link Data.Data.type data types} include\n- **number**: numeric values\n- **name**: nominal values, represented by arbitrary words\n- **date**: date values\n- **currency**: Currently supported: '$dd[,ddd]'\n- **percent**: 'd%'\n\n## Data Class\nThe fundamental object in this library is {@link Data.Data `Data`},\na simple row-column based database object,\nfeaturing named columns, sorting, mapping and filtering functions.\n\n## Example\n\n\nconst colNames = ['Name', 'Value', 'Start', 'End'];\nconst rows = [\n ['Harry', '100', '3/1/14', '11/20/14'],\n ['Mary', '1500', '7/1/14', '9/30/14'],\n ['Peter', '400', '5/20/14', '4/30/15'],\n ['Jane', '700', '11/13/14', '8/15/15']\n]\nconst data = new hsdatab.Data({colNames:colNames, rows:rows});\n\nquery = {Name:[\"Peter\", \"Jane\"]};\nconst result = data.filter(query);\n\nm.mount(root, {\n view:() => m('', [\n m('h3', 'Given the data set:'),\n m('pre',\n m('table#data', [\n m('tr', colNames.map(n => m('th', n))),\n ...rows.map(row => m('tr', [\n m('td', row[0]),\n m('td', row[1]),\n m('td', `${row[2].getMonth()+1}/${row[2].getDate()}/${row[2].getFullYear()}`),\n m('td', `${row[3].getMonth()+1}/${row[3].getDate()}/${row[3].getFullYear()}`)\n ]))\n ])),\n m('h3', 'The column types and domains are'),\n m('pre', m('table',\n m('tr', m('th', 'Column'), m('th', 'Type'), m('th', 'Domain')),\n m('tr', m('td', '\"Name\":'), m('td', data.colType(\"Name\")), m('td', data.findDomain(\"Name\").join(', '))),\n m('tr', m('td', '\"Value\":'), m('td', data.colType(\"Value\")), m('td', data.findDomain(\"Value\").join(' - '))),\n m('tr', m('td', '\"Start\":'), m('td', data.colType(\"Start\")), m('td', data.findDomain(\"Start\").map(d => d.toDateString()).join(' - '))),\n m('tr', m('td', '\"Stop\":'), m('td', data.colType(\"End\")), m('td', data.findDomain(\"End\").map(d => d.toDateString()).join(' - ')))\n )),\n m('h3', 'The query:'),\n m('code', '{Name:[\"Peter\", \"Jane\"]}'),\n m('h3', 'yields results with \"Name\"'),\n m('code', result.getColumn('Name').join(', '))\n ])\n});\n\n\n $exampleID { height: 600px; }\n #data th { width:15%; }\n #data { font-size: 11pt; }\n\n\n" + }, + "sources": [ + { + "fileName": "overview.ts", + "line": 1, + "character": 0 + } + ] + } + ], + "groups": [ + { + "title": "External modules", + "kind": 1, + "children": [ + 42, + 1, + 166, + 167 + ] + } + ] +} \ No newline at end of file diff --git a/data/index.json b/data/index.json new file mode 100644 index 0000000..c9f319f --- /dev/null +++ b/data/index.json @@ -0,0 +1 @@ +{"docs": ["hsDatab.json"], "title": "HS Libraries"} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..0bc10a0 --- /dev/null +++ b/index.html @@ -0,0 +1,11 @@ + + + + + HS Docs + + + + + + \ No newline at end of file diff --git a/indexGH.html b/indexGH.html new file mode 100644 index 0000000..2854c97 --- /dev/null +++ b/indexGH.html @@ -0,0 +1,11 @@ + + + + + HS Docs + + + + + + \ No newline at end of file diff --git a/package.json b/package.json deleted file mode 100755 index b8c851a..0000000 --- a/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "hsdatab", - "version": "2.0.2", - "main": "./index.js", - "types": "./index.d.ts", - "keywords": [ - "" - ], - "description": "Helpful Scripts simple cross-framework data table management and filtering utilities ", - "author": { - "name": "masterscripter", - "email": "helpfulscripts@gmail.com" - }, - "scripts": {}, - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/HelpfulScripts/hsDatab.git" - }, - "engines": { - "node": ">= 0.10.0" - }, - "plugins": [ - "plugins/markdown" - ], - "dependencies": {} -} diff --git a/sharedGruntConfig.js b/sharedGruntConfig.js deleted file mode 100644 index 22d19ea..0000000 --- a/sharedGruntConfig.js +++ /dev/null @@ -1,290 +0,0 @@ -const path = require('path'); -const webpack = require("webpack"); -//const webpackConfig = require('./webpack.config'); - -const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); - -module.exports = (grunt, dir, dependencies, type) => { - const pkg = grunt.file.readJSON(dir+'/package.json'); - const slash = pkg.name.lastIndexOf('/'); - const lib = slash<0? pkg.name : pkg.name.slice(slash+1); - const libPath = lib.toLowerCase(); - console.log(dir); - - // These plugins provide necessary tasks. - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-contrib-clean'); - grunt.loadNpmTasks('grunt-contrib-copy'); - grunt.loadNpmTasks('grunt-contrib-less'); - grunt.loadNpmTasks('grunt-typedoc'); - grunt.loadNpmTasks('grunt-tslint'); - grunt.loadNpmTasks('grunt-ts'); - grunt.loadNpmTasks('grunt-webpack'); - - //------ Add Doc Tasks - grunt.registerTask('doc', ['clean:docs', 'typedoc', 'copy:docs']); - - //------ Add Staging Tasks - grunt.registerTask('stage', [`${(type === 'app')? 'copy:stageApp': 'copy:deployLib'}`]); - - //------ Add Test Tasks - grunt.registerTask('ospec', () => { require('child_process').spawnSync('npm', ['test'], {stdio: 'inherit'}); }); - if (type === 'node') { - grunt.loadNpmTasks('grunt-jasmine-node-coverage'); - grunt.registerTask('test', ['clean:test', 'copy:test', 'build-specES5', 'jasmine_node' ]); } - else { - grunt.registerTask('test', ['clean:test', 'copy:test', 'build-spec', /*'ospec'*/ ]); - } - - //------ Add Build Tasks - grunt.registerTask('build-html', ['copy:build']); - grunt.registerTask('build-css', ['less']); - grunt.registerTask('build-example', ['clean:example', 'copy:example', 'ts:example', 'less:example', 'webpack:exampleDev']); - grunt.registerTask('build-app', ['copy:example', 'webpack:appDev']); - grunt.registerTask('build-js', ['tslint:src', 'ts:src']); - grunt.registerTask('build-es5', ['tslint:src', 'ts:srcES5']); - grunt.registerTask('build-jsMin', ['ts:srcMin']); - grunt.registerTask('build-spec', ['tslint:spec', 'ts:test']); - grunt.registerTask('build-specES5', ['tslint:spec', 'ts:testES5']); - - let buildTasks = ['clean:src', 'build-html', 'build-css']; - switch (type) { - case 'node': buildTasks = buildTasks.concat(['build-es5', 'copy:example']); break; - case 'util': buildTasks = buildTasks.concat(['build-js']); break; - case 'app': buildTasks = buildTasks.concat(['build-js', 'build-app']); break; - case 'lib': - default: buildTasks = buildTasks.concat(['build-js', 'build-example']); break; - } - grunt.registerTask('build', buildTasks); - grunt.registerTask('buildMin', ['build-jsMin']); - - //------ Add other MultiTasks - grunt.registerTask('make', ['build', 'test', 'doc', 'stage']); - grunt.registerTask('once', ['make']); - grunt.registerTask('default', ['make', 'watch']); - grunt.registerTask('product', ['buildMin', 'webpack:appProd', 'stage']); - - //------ Add general help - grunt.registerTask('h', 'help on grunt options', function() { - grunt.log.writeln(` grunt: \t make, then watch`); - grunt.log.writeln(` grunt once: \t make only, don't watch`); - grunt.log.writeln(` grunt make: \t build, test, doc, and stage`); - grunt.log.writeln(` grunt product: make optimized, don't watch; relevant for apps only`); - }); - - //------ Add Task Configurations - return { - pkg: grunt.file.readJSON(dir+'/package.json'), - libPath: grunt.config.process(lib).toLowerCase(), - banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + - '<%= grunt.template.today("yyyy-mm-dd") %>\n' + - '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' + - '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + - ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n', - clean: { - src: ['_dist'], - docs: ['_dist/docs'], - test: ['_dist/**/tests'], - example:['_example', '_dist/example'] - }, - copy: { - build: { expand:true, cwd:'src/', - src:['*.html'], dest:'_dist/' - }, - example:{ expand:true, cwd: 'src/example', - src:['**/*', '!**/*.ts'], dest:'_dist/example' - }, - deployLib: { files: [ - { expand:true, cwd: '_dist/src', - src:['**/*'], dest:`node_modules/${libPath}/` }, - { expand:true, cwd: './', - src:['./package.json'], dest:`node_modules/${libPath}/` } - ]}, - stageApp: { files: [ - { expand:true, cwd: '_dist/src', - src:['**/*.css*'], dest:'_dist' }, - { expand:true, cwd: './', - src:['./package.json'], dest:`node_modules/${libPath}/` } - ]}, - docs: { files: [ - { expand:true, cwd: '_dist/docs', - src:['**/*.json'], dest:`node_modules/${libPath}/docs`}, - { expand:true, cwd: './', - src:['*.md'], dest:`node_modules/${libPath}`} - ]}, - test: { files: [ - { expand:true, cwd:'_dist/', - src:['*.js', '*.css', '*.html'], dest:'_dist/test/' - }, - { cwd:'example/', expand:true, src:['*.json'], dest:'_dist/test/'} - ]} - }, - less: { - options: { - sourceMap: true - }, - css: { - files: { - '_dist/src/<%= pkg.name %>.css': 'src/css/<%= pkg.name %>.less' - } - }, - example: { - files: { - '_dist/example/<%= pkg.name %>.css': `src/example/${libPath}.less` - } - } - }, - tslint: { - options: { - configuration: __dirname+'/tslint.json', - force: false, - fix: false - }, - src: { - src: ["src/**/*.ts", "!src/**/*.spec.ts"] - }, - spec: { - src: ["src/**/*.spec.ts"] - } - }, - ts: { - src : { - outDir: "_dist/src", - src: ["src/**/*.ts", "!src/**/*.spec.ts", "!src/example/*.ts"], - tsconfig: __dirname+'/tsconfigGrunt.json' - }, - srcES5 : { - outDir: "_dist/src", - src: ["src/**/*.ts", "!src/**/*.spec.ts", "!src/example/*.ts"], - tsconfig: __dirname+'/tsconfigGruntES5.json' - }, - srcMin : { - outDir: "_dist/src", - src: ["src/**/*.ts", "!src/**/*.spec.ts", "!src/example/*.ts"], - tsconfig: __dirname+'/tsconfigProduct.json' - }, - example : { - outDir: "_example", - src: ["src/example/*.ts"], - tsconfig: __dirname+'/tsconfigGrunt.json' - }, - test : { - outDir: "_dist/tests", - src: ["src/**/*.spec.ts"], - tsconfig: __dirname+'/tsconfigGrunt.json' - }, - testES5 : { - outDir: "_dist/tests", - src: ["src/**/*.spec.ts"], - tsconfig: __dirname+'/tsconfigGruntES5.json' - } - }, - typedoc: { - code: { - options: { - target: 'es6', - module: 'commonjs', - moduleResolution: "node", - json: `_dist/docs/${lib}.json`, - out: '_dist/docs', - mode: 'modules', - name: `${lib}` - }, - src: ['src/**/*.ts'] - } - }, - jasmine_node: { - options: { forceExit: true }, - all: { - options: { - projectRoot: '_dist/tests', - coverage: { - reportDir: '_dist/docs/tests', - relativize: true, - includeAllSources: true, - report: ['html'] - }, - jasmine: { - spec_dir: '', - spec_files: [ - '_dist/tests/*.spec.js' - ] - } - }, - src: ['_dist/test/**/*.js'] - } - }, - - webpack: { - options: { - stats: !process.env.NODE_ENV || process.env.NODE_ENV === 'development' - }, - appProd: { - entry: './_dist/src/index.js', - output: { - filename: `${lib}.js`, - path: path.resolve(dir, './_dist') - }, - plugins: [ - new UglifyJsPlugin({ - uglifyOptions: { - ecma: 6, - mangle:false - } - }) - ] - }, - appDev: { - entry: './_dist/src/index.js', - devtool: "inline-source-map", - output: { - filename: `${lib}.js`, - path: path.resolve(dir, './_dist') - } - }, - - exampleProd: { - entry: './_example/example/start.js', - output: { - filename: `${lib}.js`, - path: path.resolve(dir, '_dist/example') - } - }, - exampleDev: { - entry: './_example/example/start.js', - devtool: "inline-source-map", - output: { - filename: `${lib}.js`, - path: path.resolve(dir, '_dist/example') - } - } - }, - watch: { - dependencies: { - files: dependencies.map(d => `./node_modules/${d.toLowerCase()}/index.js`), - tasks: ['make'] - }, - gruntfile: { - files: ['Gruntfile.js', __dirname+'/sharedGruntConfig.js'], - tasks: ['make'] - }, - js: { - files: ['src/**/*.ts', '!src/**/*.spec.ts', '!src/**/*.less'], - tasks: ['make'] - }, - less: { - files: ['src/**/*.less'], - tasks: ['build-css', 'stage'] - }, - html: { - files: ['src/**/*.html'], - tasks: ['build-html', 'stage'] - }, - specs: { - files: ['src/**/*.spec.ts'], - tasks: ['test'] - } - } - } -}; - diff --git a/src/Data.html b/src/Data.html new file mode 100644 index 0000000..8e87921 --- /dev/null +++ b/src/Data.html @@ -0,0 +1,568 @@ + + +

src/Data.ts

+
   1/**
+   2 */

+   3
+   4 /** */
+   5import { Condition, filter } from './DataFilters';
+   6
+   7/** defines a [min-max] range */
+   8export type NumRange = [number, number];
+   9
+  10/** defines a numeric domain that includes all values of a column */
+  11export type NumDomain = [number, number];
+  12
+  13/** defines a Date domain that includes all values of a column */
+  14export type DateDomain = [Date, Date];
+  15
+  16/** defines a categorical domain that includes all values of a column */
+  17export type NameDomain = string[];
+  18
+  19/** defines a generic domain that can be any of the typed domains. */
+  20export type Domain = NumDomain | DateDomain | NameDomain;
+  21
+  22/** defines a Column Reference, either as column name or index in the {@link Data.DataRow `DataRow`} array */
+  23export type ColumnReference = number|string;
+  24
+  25/** a generic data value type, used in the {@link Data.DataRow `DataRow`} array */
+  26export type DataVal = number|string|Date;
+  27
+  28/** a single row of column values */
+  29export type DataRow = DataVal[];
+  30
+  31/** a JSON format data set, using arrays of names and rows */
+  32export interface DataSet {
+  33    /** an optional name for the data set */
+  34    name?:  string;
+  35    /** an array of column names. Each name matches the column with the same index in DataRow */
+  36    colNames:  string[];   
+  37    /** rows of data */
+  38    rows:   DataRow[];
+  39}
+  40
+  41/** a JSON format data set, using an array of {name:value, ...} literals*/
+  42export type DataLiteralSet = Array;
+  43
+  44interface TypeStruct { type: string; count: number;};
+  45
+  46interface MetaStruct {
+  47    name:       string;         // column name
+  48    column:     number;         // column index
+  49    accessed:   boolean;        // has column data been accessed?
+  50    cast:       boolean;        // has column data been cast 
+  51    types:      TypeStruct[];   // data types, sorted by likelihood
+  52}
+  53
+  54export type sortFn = (x:any, y:any) => number;
+  55export type mapFn  = (colVal:any, colIndex?:number, rowIndex?:number, rows?:any[][]) => any;
+  56
+  57/**
+  58 * # Data
+  59 * A simple in-memory database that holds data in rows of columns.
+  60 * 
+  61 */

+  62export class Data {
+  63    //----------------------------
+  64    // public part
+  65    //----------------------------
+  66    public static type = {
+  67        /** numeric values */
+  68        number:     'number',
+  69        /** nominal values, represented by arbitrary words */
+  70        name:       'name',
+  71        /** date values */
+  72        date:       'date',
+  73        /** currency values. Currently support6ed are values ofg the format '$dd[,ddd]' */
+  74        currency:   'currency',
+  75        /** percent values: 'd%' */
+  76        percent:    'percent',
+  77//        nominal:    'nominal'
+  78    };
+  79
+  80    public static toDataSet(data:DataLiteralSet, name?:string):DataSet {
+  81        data = data || [{}];
+  82        const names = Object.keys(data[0]);
+  83        const rows = data.map((r:any) => 
+  84            names.map((n:string) => r[n]));
+  85        return { rows:rows, colNames:names, name:name||undefined };
+  86    }
+  87
+  88    constructor(data?:DataSet) {
+  89        this.import(data);
+  90    }
+  91
+  92    /**
+  93     * @return the `name` field for this data base, if any
+  94     */

+  95    public getName():string {
+  96        return this.name;
+  97    }
+  98
+  99    /**
+ 100     * Imports data from an object literal `data`
+ 101     * @param data the data set to import
+ 102     */

+ 103    public import(data:DataSet) {
+ 104        this.name = data.name;
+ 105        this.setData(data.rows, data.colNames);
+ 106    }
+ 107
+ 108    /**
+ 109     * Exports to an object literal
+ 110     */

+ 111    public export():DataSet {
+ 112        return {
+ 113            rows: this.getData(),
+ 114            colNames:this.colNames()
+ 115        };
+ 116    }
+ 117
+ 118    /**
+ 119     * returns the 2D array underlying the data base.
+ 120     */

+ 121    public getData():DataRow[] {
+ 122        return this.data;
+ 123    }
+ 124
+ 125    /**
+ 126     * Returns the values in the specified column as a new array.
+ 127     * @param col the column to return.
+ 128     */

+ 129    public getColumn(col:ColumnReference): DataVal[] {
+ 130        const cn = this.colNumber(col);
+ 131        return this.data.map((row:DataRow) => row[cn]);
+ 132    }
+ 133
+ 134    /**
+ 135     * adds a new column to the data set. if `newCol` already exists, 
+ 136     * the column index is returned withoput change.
+ 137     * @param col the name of the new column
+ 138     * @return the index for the new column
+ 139     */

+ 140    public colAdd(col:string):number {
+ 141        let m = this.getMeta(col);
+ 142        if (m === undefined) { 
+ 143            m = this.meta[col] = {};
+ 144            m.name   = col; 
+ 145            m.column = this.meta.length;
+ 146            this.meta.push(m);      // access name by both column name and index
+ 147            m.cast     = false;         // has not been cast yet
+ 148            m.accessed = false;         // has not been accessed yet
+ 149        }
+ 150        return m.column;
+ 151    }
+ 152
+ 153    /**
+ 154     * initializes the specifed column with values, adding a new column if needed. 
+ 155     * If `val`is a function, it is called as ```
+ 156     * val(colValue:DataVal, rowIndex:number, row:DataRow)
+ 157     * ```
+ 158     * @param col the column to initialize
+ 159     * @param initializer the value to initialize with, or a function whose return
+ 160     * value is used to initialize the column
+ 161     */

+ 162    public colInitialize(col:ColumnReference, initializer:any) {
+ 163        const cn = this.colNumber(col);
+ 164        if (!cn  && typeof col === 'string') { this.colAdd(col); }
+ 165        const fn = typeof initializer === 'function';
+ 166        if (cn!==undefined) {
+ 167            this.data.map((r:DataRow, i:number) =>
+ 168                r[cn] = fn? initializer(r[cn], i, r) : initializer
+ 169            );
+ 170        }
+ 171    }
+ 172
+ 173    /**
+ 174     * returns the column index of the specified column. 
+ 175     * `col` can be either an index or a name.
+ 176     * @param column the data column, name or index, for which to return the index. 
+ 177     * @return the column number or `undefined`.
+ 178     */

+ 179    public colNumber(col:ColumnReference) {
+ 180        const m = this.getMeta(col);
+ 181        if (!m) { return undefined; }
+ 182        else {
+ 183            m.accessed = true; 
+ 184            return m.column; 
+ 185        }
+ 186    }
+ 187    
+ 188    /**
+ 189     * returns the column name for the specified column. 
+ 190     * `col` can be either an index or a name.
+ 191     * @param column the data column, name or index. 
+ 192     * @return the column name or `undefined`.
+ 193     */

+ 194    public colName(col:ColumnReference) {
+ 195        var m = this.getMeta(col);
+ 196        if (!m) { return undefined; }
+ 197        m.accessed = true; 
+ 198        return m.name; 
+ 199    }
+ 200
+ 201    /**
+ 202     * returns the names for all columns. 
+ 203     * @return an array of strings with the names.
+ 204     */

+ 205    public colNames():string[] {
+ 206        return this.meta.map((m:MetaStruct) => m.name); 
+ 207    }
+ 208
+ 209    /**
+ 210     * returns the column type for the specified column. 
+ 211     * `col` can be either an index or a name.
+ 212     * @param column the data column, name or index. 
+ 213     * @return the column type.
+ 214     */

+ 215    public colType(col:ColumnReference) { 
+ 216        const meta = this.getMeta(col);
+ 217        return meta? meta.types[0].type : Data.type.name;
+ 218    }
+ 219
+ 220    /**
+ 221     * modifies `domain` to include all values in column `col`.
+ 222     * If no `col` is specified, the range of data indexes is returned.
+ 223     * @param col optional; the column name or index 
+ 224     * @param domain optional; the Domain range to update
+ 225     * @return the updated domain
+ 226     */

+ 227    public findDomain(col?:ColumnReference, domain?:Domain):Domain {
+ 228        if (domain===undefined) { domain = []; }
+ 229        if (col === undefined) { // use array index as domain
+ 230            domain[0] = 0;
+ 231            domain[1] = this.data.length-1;
+ 232        } else {
+ 233            const c = this.colNumber(col);
+ 234            const type = this.colType(col);
+ 235            if (this.data === undefined) {
+ 236                console.log('no data'); 
+ 237            }
+ 238            switch(type) {
+ 239                case Data.type.name: 
+ 240                    this.data.forEach((r:DataRow) => {
+ 241                        const nomDom = domain;
+ 242                        if (nomDom.indexOf(''+r[c]) < 0) { nomDom.push(''+r[c]); }
+ 243                    });
+ 244                    break;
+ 245                default: 
+ 246                    this.data.forEach((r:DataRow) => {
+ 247                        let v:number = r[c];
+ 248                        if (domain[0]===undefined) { domain[0] = v; }
+ 249                        if (domain[1]===undefined) { domain[1] = v; }
+ 250                        if (v!==undefined && v!==null) {
+ 251                            if (v + 252                            else if (v>domain[1]) { domain[1] = v; }
+ 253                        }
+ 254                    });
+ 255            }
+ 256        }
+ 257        return domain;
+ 258    }
+ 259
+ 260    public castData() {
+ 261        this.meta.forEach((c:MetaStruct) => {
+ 262            const col = c.column;
+ 263            if (!c.cast) {
+ 264                this.data.forEach((row:DataRow) => row[col] = this.castVal(c.types[0].type, row[col]));
+ 265            }
+ 266            c.cast = true;
+ 267        });
+ 268    }
+ 269
+ 270    /**
+ 271     * filters this data set and returns a new data set with a 
+ 272     * shallow copy of rows that pass the `condition`.
+ 273     * See {@link DataFilters DataFilters} for rules and examples on how to construct conditions.
+ 274     * @param condition filters 
+ 275     * @return a new Data object with rows that pass the filter
+ 276     */

+ 277    public filter(condition:Condition):Data {
+ 278        return filter(this, condition);
+ 279    }
+ 280
+ 281    /**
+ 282     * @description Sorts the rows of values based on the result of the `sortFn`, 
+ 283     * which behaves similarly to the Array.sort method.  
+ 284     * Two modes are supported:
+ 285     * # Array Mode
+ 286     * If `col` is omitted, the column arrays are passed as samples into the `sortFn`. 
+ 287     * This allows for complex sorts, combining conditions across multiple columns.
+ 288     * ```
+ 289     * data.sort((row1, row2) => row1[5] - row2[5] );
+ 290     * ```
+ 291     * # Column mode
+ 292     * If `col` is specified, either as index or by column name, the respective column value is passed
+ 293     * into `sortFn`. This allows filtering for simple conditions.

+ 294     * **The specified column will be automatically cast prior to sorting**

+ 295     * `data.sort('Date', function(val1, val2) { return val1 - val2; });`
+ 296     * @param col optional; the data column to use for sorting. 
+ 297     * @param sortFn a function to implement the conditions, 
+ 298     * follows the same specifications as the function passed to Array.sort(). 
+ 299     * Some predefined sort function can be invoked by providing a 
+ 300     * respective string instead of a function. The following functions are defined:
+ 301        
+ 302        
+ 303        
+ 304        
'`ascending`'sort in ascending order.
'`descending`'sort in decending order.

+ 305     * @return the Data object in order to allow for chaining.
+ 306     */

+ 307    public sort(sortFn:string|sortFn, col?:ColumnReference):Data {
+ 308        let fn = sortFn;
+ 309        if (!col) {
+ 310            this.data.sort(fn);
+ 311        } else {
+ 312            col = this.colNumber(col);
+ 313            if (sortFn === 'descending') { fn = (a:any, b:any)  => (b>a)?1:((b + 314            if (sortFn === 'ascending')  { fn = (a:any, b:any)  => (ba)?-1:0); }
+ 315            this.data.sort((r1:any[], r2:any[]) => fn(r1[col], r2[col])); 
+ 316        }
+ 317        return this;
+ 318    }
+ 319
+ 320    /** 
+ 321    *  Maps one or more columns in each rows of values based 
+ 322     * on the result of the `mapFn`, which behaves similarly to the Array.map() method.
+ 323     * Two modes are supported:
+ 324     * # Array Mode
+ 325     * If `col` is omitted, the `mapFn` is passed the column arrays per row as parameter. 
+ 326     * This allows for complex mapping combining conditions across multiple columns.
+ 327     * ```
+ 328     * data.map(function(values){ 
+ 329     *    values[1] = values[3] * values[5]; 
+ 330     *    return values; 
+ 331     * });
+ 332     * ```
+ 333     * Be sure to return the `values` array as a result.
+ 334     * # Column mode
+ 335     * If `col` is specified, either as index or by column name, the respective column value is passed
+ 336     * into `mapFn`, along with the row index and the entire row array. This allows for simple mapping.
+ 337     * ```
+ 338     * data.map('Price', function(value, i, values) { 
+ 339     *    return value * 2; 
+ 340     * });
+ 341     * ```
+ 342     * @param col the data column, or columns, to apply the mapping to. 
+ 343     * @param mapFn a function to implement the mapping,
+ 344     * called on each row of the data set in turn as `mapFn(val, i, c, rows)`, where
+ 345     * - `val`: the column value in the current row
+ 346     * - `c`: the column index in the current row
+ 347     * - `i`: the row index 
+ 348     * - `rows`: the rows being iterated over
+ 349`    * 
+ 350     * follows the same specifications as the function passed to Array.map().

+ 351     * For column mode, some predefined map functions can be invoked by providing a 
+ 352     * respective string instead of a function. The following functions are defined:
+ 353        
+ 354        
+ 355        
+ 356        
'noop'replace value with itself, performing no operation.
'cumulate'replace value with the cumulative sum of values up to the current element.

+ 357     * @return a new Data object containing the mapping.
+ 358     */

+ 359    public map(col:ColumnReference|ColumnReference[], mapFn:string|mapFn):Data {
+ 360        const noop = (val:any) => val;
+ 361        const cumulate = () => { 
+ 362            let sum=0; 
+ 363            return (val:number, i:number) => { sum += +val; return sum; };
+ 364        };
+ 365        function getFn() {
+ 366            let fn; // define fn inside each col loop to ensure initialization
+ 367            switch (mapFn) {
+ 368                case 'cumulate': fn = cumulate(); break;
+ 369                case 'noop':     fn = noop; break;
+ 370                default:         fn = mapFn;
+ 371            }
+ 372            return fn;
+ 373        }
+ 374
+ 375        let result = new Data({colNames:this.colNames(), rows:this.data.slice(), name:this.getName()});
+ 376
+ 377        const names = col['length']? col : [col];            
+ 378        names.map((cn:ColumnReference) => {
+ 379            const c = this.colNumber(cn);
+ 380            let fn = getFn(); // define fn inside each col loop to ensure initialization
+ 381            result.data = result.data.map((row:any[], i:number, rows:any[][]) => { 
+ 382                row[c] = fn(row[c], c, i, rows); 
+ 383                return row;
+ 384            });
+ 385        });
+ 386        return result;
+ 387    }
+ 388
+ 389    //----------------------------
+ 390    // private part
+ 391    //----------------------------
+ 392    private data: DataRow[]    = [];
+ 393    private meta: MetaStruct[] = [];
+ 394    private name: string;
+ 395
+ 396    private getMeta(col:ColumnReference):MetaStruct { 
+ 397        if (!this.meta) { this.meta = []; }
+ 398        if (!this.meta[col]) { return undefined; }
+ 399        this.meta[col].accessed = true;
+ 400        return this.meta[col]; 
+ 401    }
+ 402
+ 403    /**
+ 404     * sets `data` to the existing data set. If data has previously been set, 
+ 405     * `data` will be added to the end of the list if all `names`  match those of the 
+ 406     * existing set. 
+ 407     * @param data the data to add
+ 408     * @param names an array of names that match the columns
+ 409     * @param autoType unless set to false, the method will attempt to determine the 
+ 410     * type of data and automatically cast data points to their correct value
+ 411     */

+ 412    private setData(data:DataRow[], names:string[], autoType=true):void {
+ 413        this.meta = [];
+ 414        this.data = data;
+ 415        if (!names) {
+ 416            console.log();
+ 417        }
+ 418        names.forEach((col:string) => this.colAdd(col));
+ 419        names.forEach((col:string) => this.findTypes(col));
+ 420        this.castData();
+ 421    }
+ 422
+ 423    /**
+ 424     * Determines the type of data in `col`. An array of counts is created for all
+ 425     * encountered types, sorted by descending frequency. THe most likely type in position 0
+ 426     * of the array is returned.
+ 427     * @param col the index of the column to be typed. 
+ 428     * @return the most likely type of data in `col`.
+ 429     */

+ 430    private findTypes(col:ColumnReference):string {
+ 431        const m = this.getMeta(col);
+ 432        const types:TypeStruct[] = [];
+ 433        Object.keys(Data.type).forEach((t:string) => {
+ 434            const ts = { type: Data.type[t], count: 0 }; 
+ 435            types.push(ts);
+ 436            types[Data.type[t]] = ts;
+ 437        });
+ 438        for (let v of this.allRows(col)) {
+ 439            const t = this.findType(v);
+ 440            if (t !== null) { types[t].count++; }
+ 441        }
+ 442        types.sort(function(a:TypeStruct, b:TypeStruct) { 
+ 443            if (a.type==='currency'&&a.count>0) { return -1; }
+ 444            if (b.type==='currency'&&b.count>0) { return 1; }
+ 445            return b.count - a.count;
+ 446        });
+ 447        m.types = types;
+ 448        return types[0].type;
+ 449    }
+ 450
+ 451    /**
+ 452     * @description determines the data type. Supported types are 
+ 453     * ```
+ 454     * 'date':    sample represents a Date, either as a Date object or a String 
+ 455     * 'number':  sample represents a number
+ 456     * 'percent': sample represents a percentage (special case of a real number)
+ 457     * 'nominal': sample represents a nominal (ordinal or categorical) value
+ 458     * ```
+ 459     * @param val the value to bve typed.
+ 460     * @returns the type ('number', 'date', 'percent', 'nominal', 'currency') corresponding to the sample
+ 461     */

+ 462    private findType(val:DataVal) {
+ 463        if (val && val!=='') {
+ 464            if (val instanceof Date) { return Data.type.date; }         // if val is already a date
+ 465            if (typeof val === 'number') { return Data.type.number; }   // if val is already a number
+ 466
+ 467            // else: val is a string:
+ 468            const strVal = ''+val;
+ 469            if (''+parseFloat(strVal) === strVal)                              { return Data.type.number; }
+ 470            if (strVal.startsWith('$') && !isNaN(parseFloat(strVal.slice(1)))) { return Data.type.currency; }
+ 471            if (strVal.endsWith('%') && !isNaN(parseFloat(strVal)))            { return Data.type.percent; }
+ 472            if (!isNaN(this.toDate(strVal).getTime()))                        { return Data.type.date; }
+ 473
+ 474            // european large number currency representation: '$dd[,ddd]'
+ 475            if ((/^\$\d{0,2}((,\d\d\d)*)/g).test(val)) { 
+ 476                if (!isNaN(parseFloat(val.trim().replace(/[^eE\+\-\.\d]/g, '').replace(/,/g, '')))) { 
+ 477                    return Data.type.currency; 
+ 478                }
+ 479            }
+ 480            switch (strVal.toLowerCase()) {
+ 481                case "null": break;
+ 482                case "#ref!": break;
+ 483                default: if (val.length>0) { return Data.type.name; }
+ 484            }
+ 485        }
+ 486        return null;
+ 487    }    
+ 488
+ 489    /**
+ 490     * A generator that provides the specified column value for each row in `Data` in sequence. 
+ 491     * @param column 
+ 492     */

+ 493    private * allRows(column:ColumnReference):Iterable {
+ 494        const c = this.colNumber(column);
+ 495        for (let r=0; r + 496            yield this.data[r][c];
+ 497        }
+ 498    }
+ 499
+ 500    /**
+ 501     * @param val the string to convert to a date
+ 502     * @param limitYear the year below which the century is corrected. Defaults to 1970.
+ 503     * @returns a new Date object parsed from `str`.
+ 504     * @description returns a new Date object parsed from `str` and corrects for a difference in 
+ 505     * interpreting centuries between webkit and mozilla in converting strings to Dates:
+ 506     * The string "15/7/03" will convert to Jul 15 1903 in Mozilla and July 15 2003 in Webkit.
+ 507     * If `limitYear` is not specified this method uses 1970 as the decision date: 
+ 508     * years 00-69 will be interpreted as 2000-2069, years 70-99 as 1970-1999.
+ 509     */

+ 510    private toDate(val:DataVal, limitYear=1970):Date {
+ 511        let d:Date;
+ 512        if (val instanceof Date) { d = val; }
+ 513                            else { d = new Date(val); }   
+ 514        let yr=d.getFullYear();
+ 515        if (yr < 100) { 
+ 516            yr += 1900; 
+ 517            d.setFullYear( (yr < limitYear)? yr+100 : yr);
+ 518        }
+ 519        return d;
+ 520    }
+ 521
+ 522    /**
+ 523     * @param type ['date' | 'percent' | 'real' | _any_] The type to cast into. In case of _any_ - i.e. `type` 
+ 524     * does not match any of the previous keywords, no casting occurs.
+ 525     * @param sample The value to cast.
+ 526     * @returns The result of the cast. 
+ 527     * @description Casts the sample to the specified data type.
+ 528     */

+ 529    private castVal(type:string, val:DataVal):DataVal {
+ 530        switch (type) {
+ 531            case Data.type.date:    if (val instanceof Date) { return val; }
+ 532                            val = this.toDate(val);
+ 533                            if (isNaN(val.getTime())) { val = null; }
+ 534                            break;
+ 535            case Data.type.percent: if (typeof val === 'string') {
+ 536                                const num = parseFloat(val);
+ 537                                val = (val).endsWith('%')? num/100 : num;
+ 538                            } 
+ 539                            if (isNaN(val)) { val = null; }
+ 540                            break;
+ 541            case Data.type.currency:// replace all except 'e/E', '.', '+/-' and digits
+ 542             if (typeof val === 'string') { val = val.replace(/[^eE\+\-\.\d]/g, ''); }            
+ 543             /* falls through */
+ 544            case Data.type.number:  if (typeof val === 'string') { val = parseFloat(val); }
+ 545                            if (isNaN(val)) { val = null; }
+ 546                            break;
+ 547            default:        val = ''+val;
+ 548        }
+ 549        return val;
+ 550     }     
+ 551}
+ + \ No newline at end of file diff --git a/src/Data.spec.html b/src/Data.spec.html new file mode 100644 index 0000000..7f18f3f --- /dev/null +++ b/src/Data.spec.html @@ -0,0 +1,54 @@ + + +

src/Data.spec.ts

+
   1var o = require("mithril/ospec/ospec");
+   2import * as hsdatab     from './';
+   3
+   4const colNames = ['Name', 'Value', 'Start', 'End'];
+   5const rows = [
+   6  ['Harry', '100', '3/1/14', '11/20/14'], 
+   7  ['Mary', '1500', '7/1/14',  '9/30/14'],
+   8  ['Peter', '400', '5/20/14', '4/30/15'],  
+   9  ['Jane', '700', '11/13/14', '8/15/15']
+  10];
+  11
+  12const data = new hsdatab.Data({colNames:colNames, rows:rows});
+  13
+  14const query = {Name:["Peter", "Jane"]};
+  15const result = data.filter(query);
+  16
+  17o.spec("Data", () => {
+  18    o("is created with 4 rows", () => {
+  19        o(data.getData().length).equals(4);
+  20    });
+  21});
+  22o.spec('Data Filters', () => {
+  23    o('Query for {Name:["Peter", "Jane"]}', () => {
+  24        o(result.getData().length).equals(2)('has two rows');
+  25        o(result.getColumn('Name')[0]).equals('Peter')('has first row name Peter');
+  26    });
+  27});
+  28
+  29
+  30/*
+  31        var vnode = MyComponent.view()
+  32
+  33        o(vnode.tag).equals("div")
+  34        o(vnode.children.length).equals(1)
+  35        o(vnode.children[0].tag).equals("p")
+  36        o(vnode.children[0].children).equals("Hello world")
+  37*/

+ + \ No newline at end of file diff --git a/src/Data.ts b/src/Data.ts deleted file mode 100644 index 3c83a09..0000000 --- a/src/Data.ts +++ /dev/null @@ -1,540 +0,0 @@ -/** - */ - - /** */ -import { Condition, filter } from './DataFilters'; - -/** defines a [min-max] range */ -export type NumRange = [number, number]; - -/** defines a numeric domain that includes all values of a column */ -export type NumDomain = [number, number]; - -/** defines a Date domain that includes all values of a column */ -export type DateDomain = [Date, Date]; - -/** defines a categorical domain that includes all values of a column */ -export type NameDomain = string[]; - -/** defines a generic domain that can be any of the typed domains. */ -export type Domain = NumDomain | DateDomain | NameDomain; - -/** defines a Column Reference, either as column name or index in the {@link Data.DataRow `DataRow`} array */ -export type ColumnReference = number|string; - -/** a generic data value type, used in the {@link Data.DataRow `DataRow`} array */ -export type DataVal = number|string|Date; - -/** a single row of column values */ -export type DataRow = DataVal[]; - -/** a JSON format data set, using arrays of names and rows */ -export interface DataSet { - /** an optional name for the data set */ - name?: string; - /** an array of column names. Each name matches the column with the same index in DataRow */ - colNames: string[]; - /** rows of data */ - rows: DataRow[]; -} - -/** a JSON format data set, using an array of {name:value, ...} literals*/ -export type DataLiteralSet = Array; - -interface TypeStruct { type: string; count: number;}; - -interface MetaStruct { - name: string; // column name - column: number; // column index - accessed: boolean; // has column data been accessed? - cast: boolean; // has column data been cast - types: TypeStruct[]; // data types, sorted by likelihood -} - -export type sortFn = (x:any, y:any) => number; -export type mapFn = (colVal:any, colIndex?:number, rowIndex?:number, rows?:any[][]) => any; - -/** - * # Data - * A simple in-memory database that holds data in rows of columns. - * - */ -export class Data { - //---------------------------- - // public part - //---------------------------- - public static type = { - number: 'number data', - name: 'name data', - date: 'date data', - currency: 'currency data', - percent: 'percent data', - nominal: 'nominal data' - }; - - public static toDataSet(data:DataLiteralSet, name?:string):DataSet { - data = data || [{}]; - const names = Object.keys(data[0]); - const rows = data.map((r:any) => - names.map((n:string) => r[n])); - return { rows:rows, colNames:names, name:name||undefined }; - } - - constructor(data?:DataSet) { - this.import(data); - } - - /** - * @return the `name` field for this data base, if any - */ - public getName():string { - return this.name; - } - - /** - * Imports data from an object literal `data` - * @param data the data set to import - */ - public import(data:DataSet) { - this.name = data.name; - this.setData(data.rows, data.colNames); - } - - /** - * Exports to an object literal - */ - public export():DataSet { - return { - rows: this.getData(), - colNames:this.colNames() - }; - } - - /** - * returns the 2D array underlying the data base. - */ - public getData():DataRow[] { - return this.data; - } - - /** - * Returns the values in the specified column as a new array. - * @param col the column to return. - */ - public getColumn(col:ColumnReference): DataVal[] { - const cn = this.colNumber(col); - return this.data.map((row:DataRow) => row[cn]); - } - - /** - * adds a new column to the data set. if `newCol` already exists, - * the column index is returned withoput change. - * @param col the name of the new column - * @return the index for the new column - */ - public colAdd(col:string):number { - let m = this.getMeta(col); - if (m === undefined) { - m = this.meta[col] = {}; - m.name = col; - m.column = this.meta.length; - this.meta.push(m); // access name by both column name and index - m.cast = false; // has not been cast yet - m.accessed = false; // has not been accessed yet - } - return m.column; - } - - /** - * initializes the specifed column with values, adding a new column if needed. - * If `val`is a function, it is called as ``` - * val(colValue:DataVal, rowIndex:number, row:DataRow) - * ``` - * @param col the column to initialize - * @param initializer the value to initialize with, or a function whose return - * value is used to initialize the column - */ - public colInitialize(col:ColumnReference, initializer:any) { - const cn = this.colNumber(col); - if (!cn && typeof col === 'string') { this.colAdd(col); } - const fn = typeof initializer === 'function'; - if (cn!==undefined) { - this.data.map((r:DataRow, i:number) => - r[cn] = fn? initializer(r[cn], i, r) : initializer - ); - } - } - - /** - * returns the column index of the specified column. - * `col` can be either an index or a name. - * @param column the data column, name or index, for which to return the index. - * @return the column number or `undefined`. - */ - public colNumber(col:ColumnReference) { - const m = this.getMeta(col); - if (!m) { return undefined; } - else { - m.accessed = true; - return m.column; - } - } - - /** - * returns the column name for the specified column. - * `col` can be either an index or a name. - * @param column the data column, name or index. - * @return the column name or `undefined`. - */ - public colName(col:ColumnReference) { - var m = this.getMeta(col); - if (!m) { return undefined; } - m.accessed = true; - return m.name; - } - - /** - * returns the names for all columns. - * @return an array of strings with the names. - */ - public colNames():string[] { - return this.meta.map((m:MetaStruct) => m.name); - } - - /** - * returns the column type for the specified column. - * `col` can be either an index or a name. - * @param column the data column, name or index. - * @return the column type. - */ - public colType(col:ColumnReference) { - const meta = this.getMeta(col); - return meta? meta.types[0].type : Data.type.name; - } - - /** - * modifies `domain` to include all values in column `col`. - * @param col the column name or index - * @param domain the - */ - public findDomain(col:ColumnReference, domain:Domain) { - if (col === undefined) { // use array index as domain - domain[0] = 0; - domain[1] = this.data.length-1; - } else { - const c = this.colNumber(col); - const type = this.colType(col); - if (this.data === undefined) { - console.log('no data'); - } - switch(type) { - case Data.type.nominal: - this.data.forEach((r:DataRow) => { - const nomDom = domain; - if (nomDom.indexOf(''+r[c]) < 0) { nomDom.push(''+r[c]); } - }); - break; - default: - this.data.forEach((r:DataRow) => { - let v:number = r[c]; - if (v!==undefined && v!==null) { - domain[0] = (vdomain[1])? v : domain[1]; - } - }); - } - } - } - - public castData() { - this.meta.forEach((c:MetaStruct) => { - const col = c.column; - if (!c.cast) { - this.data.forEach((row:DataRow) => row[col] = this.castVal(c.types[0].type, row[col])); - } - c.cast = true; - }); - } - - /** - * filters this data set and returns a new data set with a - * shallow copy of rows that pass the `condition`. - * See {@link DataFilters DataFilters} for rules and examples on how to construct conditions. - * @param condition filters - * @return a new Data object with rows that pass the filter - */ - public filter(condition:Condition):Data { - return filter(this, condition); - } - - /** - * @description Sorts the rows of values based on the result of the `sortFn`, - * which behaves similarly to the Array.sort method. - * Two modes are supported: - * # Array Mode - * If `col` is omitted, the column arrays are passed as samples into the `sortFn`. - * This allows for complex sorts, combining conditions across multiple columns. - * ``` - * data.sort((row1, row2) => row1[5] - row2[5] ); - * ``` - * # Column mode - * If `col` is specified, either as index or by column name, the respective column value is passed - * into `sortFn`. This allows filtering for simple conditions.
- * **The specified column will be automatically cast prior to sorting**
- * `data.sort('Date', function(val1, val2) { return val1 - val2; });` - * @param col optional; the data column to use for sorting. - * @param sortFn a function to implement the conditions, - * follows the same specifications as the function passed to Array.sort(). - * Some predefined sort function can be invoked by providing a - * respective string instead of a function. The following functions are defined: - - - -
'`ascending`'sort in ascending order.
'`descending`'sort in decending order.
- * @return the Data object in order to allow for chaining. - */ - public sort(sortFn:string|sortFn, col?:ColumnReference):Data { - let fn = sortFn; - if (!col) { - this.data.sort(fn); - } else { - col = this.colNumber(col); - if (sortFn === 'descending') { fn = (a:any, b:any) => (b>a)?1:((b (ba)?-1:0); } - this.data.sort((r1:any[], r2:any[]) => fn(r1[col], r2[col])); - } - return this; - } - - /** - * Maps one or more columns in each rows of values based - * on the result of the `mapFn`, which behaves similarly to the Array.map() method. - * Two modes are supported: - * # Array Mode - * If `col` is omitted, the `mapFn` is passed the column arrays per row as parameter. - * This allows for complex mapping combining conditions across multiple columns. - * ``` - * data.map(function(values){ - * values[1] = values[3] * values[5]; - * return values; - * }); - * ``` - * Be sure to return the `values` array as a result. - * # Column mode - * If `col` is specified, either as index or by column name, the respective column value is passed - * into `mapFn`, along with the row index and the entire row array. This allows for simple mapping. - * ``` - * data.map('Price', function(value, i, values) { - * return value * 2; - * }); - * ``` - * @param col the data column, or columns, to apply the mapping to. - * @param mapFn a function to implement the mapping, - * called on each row of the data set in turn as `mapFn(val, i, c, rows)`, where - * - `val`: the column value in the current row - * - `c`: the column index in the current row - * - `i`: the row index - * - `rows`: the rows being iterated over -` * - * follows the same specifications as the function passed to Array.map().
- * For column mode, some predefined map functions can be invoked by providing a - * respective string instead of a function. The following functions are defined: - - - -
'noop'replace value with itself, performing no operation.
'cumulate'replace value with the cumulative sum of values up to the current element.
- * @return a new Data object containing the mapping. - */ - public map(col:ColumnReference|ColumnReference[], mapFn:string|mapFn):Data { - const noop = (val:any) => val; - const cumulate = () => { - let sum=0; - return (val:number, i:number) => { sum += +val; return sum; }; - }; - function getFn() { - let fn; // define fn inside each col loop to ensure initialization - switch (mapFn) { - case 'cumulate': fn = cumulate(); break; - case 'noop': fn = noop; break; - default: fn = mapFn; - } - return fn; - } - - let result = new Data({colNames:this.colNames(), rows:this.data.slice(), name:this.getName()}); - - const names = col['length']? col : [col]; - names.map((cn:ColumnReference) => { - const c = this.colNumber(cn); - let fn = getFn(); // define fn inside each col loop to ensure initialization - result.data = result.data.map((row:any[], i:number, rows:any[][]) => { - row[c] = fn(row[c], c, i, rows); - return row; - }); - }); - return result; - } - - //---------------------------- - // private part - //---------------------------- - private data: DataRow[] = []; - private meta: MetaStruct[] = []; - private name: string; - - private getMeta(col:ColumnReference):MetaStruct { - if (!this.meta) { this.meta = []; } - if (!this.meta[col]) { return undefined; } - this.meta[col].accessed = true; - return this.meta[col]; - } - - /** - * sets `data` to the existing data set. If data has previously been set, - * `data` will be added to the end of the list if all `names` match those of the - * existing set. - * @param data the data to add - * @param names an array of names that match the columns - * @param autoType unless set to false, the method will attempt to determine the - * type of data and automatically cast data points to their correct value - */ - private setData(data:DataRow[], names:string[], autoType=true):void { - this.meta = []; - this.data = data; - if (!names) { - console.log(); - } - names.forEach((col:string) => this.colAdd(col)); - names.forEach((col:string) => this.findTypes(col)); - this.castData(); - } - - /** - * Determines the type of data in `col`. An array of counts is created for all - * encountered types, sorted by descending frequency. THe most likely type in position 0 - * of the array is returned. - * @param col the index of the column to be typed. - * @return the most likely type of data in `col`. - */ - private findTypes(col:ColumnReference):string { - const m = this.getMeta(col); - const types:TypeStruct[] = []; - Object.keys(Data.type).forEach((t:string) => { - const ts = { type: Data.type[t], count: 0 }; - types.push(ts); - types[Data.type[t]] = ts; - }); - for (let v of this.allRows(col)) { - const t = this.findType(v); - if (t !== null) { types[t].count++; } - } - types.sort(function(a:TypeStruct, b:TypeStruct) { - if (a.type==='currency'&&a.count>0) { return -1; } - if (b.type==='currency'&&b.count>0) { return 1; } - return b.count - a.count; - }); - m.types = types; - return types[0].type; - } - - /** - * @description determines the data type. Supported types are - * ``` - * 'date': sample represents a Date, either as a Date object or a String - * 'number': sample represents a number - * 'percent': sample represents a percentage (special case of a real number) - * 'nominal': sample represents a nominal (ordinal or categorical) value - * ``` - * @param val the value to bve typed. - * @returns the type ('number', 'date', 'percent', 'nominal', 'currency') corresponding to the sample - */ - private findType(val:DataVal) { - if (val && val!=='') { - if (val instanceof Date) { return Data.type.date; } // if val is already a date - if (typeof val === 'number') { return Data.type.number; } // if val is already a number - - // else: val is a string: - const strVal = ''+val; - if (''+parseFloat(strVal) === strVal) { return Data.type.number; } - if (strVal.startsWith('$') && !isNaN(parseFloat(strVal.slice(1)))) { return Data.type.currency; } - if (strVal.endsWith('%') && !isNaN(parseFloat(strVal))) { return Data.type.percent; } - if (!isNaN(this.toDate(strVal).getTime())) { return Data.type.date; } - - // european large number currency representation: '$dd,ddd[,ddd]' - if ((/^\$\d{0,2}((,\d\d\d)*)/g).test(val)) { - if (!isNaN(parseFloat(val.trim().replace(/[^eE\+\-\.\d]/g, '').replace(/,/g, '')))) { - return Data.type.currency; - } - } - switch (strVal.toLowerCase()) { - case "null": break; - case "#ref!": break; - default: if (val.length>0) { return Data.type.nominal; } - } - } - return null; - } - - /** - * A generator that provides the specified column value for each row in `Data` in sequence. - * @param column - */ - private * allRows(column:ColumnReference):Iterable { - const c = this.colNumber(column); - for (let r=0; rval; } - else { d = new Date(val); } - let yr=d.getFullYear(); - if (yr < 100) { - yr += 1900; - d.setFullYear( (yr < limitYear)? yr+100 : yr); - } - return d; - } - - /** - * @param type ['date' | 'percent' | 'real' | _any_] The type to cast into. In case of _any_ - i.e. `type` - * does not match any of the previous keywords, no casting occurs. - * @param sample The value to cast. - * @returns The result of the cast. - * @description Casts the sample to the specified data type. - */ - private castVal(type:string, val:DataVal):DataVal { - switch (type) { - case Data.type.date: if (val instanceof Date) { return val; } - val = this.toDate(val); - if (isNaN(val.getTime())) { val = null; } - break; - case Data.type.percent: if (typeof val === 'string') { - const num = parseFloat(val); - val = (val).endsWith('%')? num/100 : num; - } - if (isNaN(val)) { val = null; } - break; - case Data.type.currency:// replace all except 'e/E', '.', '+/-' and digits - if (typeof val === 'string') { val = val.replace(/[^eE\+\-\.\d]/g, ''); } - /* falls through */ - case Data.type.number: if (typeof val === 'string') { val = parseFloat(val); } - if (isNaN(val)) { val = null; } - break; - default: val = ''+val; - } - return val; - } -} \ No newline at end of file diff --git a/src/DataFilters.html b/src/DataFilters.html new file mode 100644 index 0000000..7669d54 --- /dev/null +++ b/src/DataFilters.html @@ -0,0 +1,267 @@ + + +

src/DataFilters.ts

+
   1
+   2/**
+   3* Use the {@link filter `filter`} function to executes a queries on a {@link Data `Data`} object. 
+   4* Each row in the data is checked and those for which `conditions` holds true are returned as a new `Data` object. 
+   5
+   6* # Condition construction
+   7*  
+   8* ### General Condition
+   9* ```
+  10* Condition = 
+  11*    IndexCondition            -> conditions on the row index
+  12* || RecursiveCondition        -> (set of) conditions on column values
+  13* ```
+  14
+  15* ### IndexCondition
+  16* ```
+  17* IndexCondition =
+  18*    rowIndex:number           -> true if row index matches
+  19* ```
+  20
+  21* ### RecursiveCondition
+  22* ```
+  23* RecursiveCondition =
+  24*    OrCondition               -> OR: true if any compound condition is true
+  25* || AndCondition              -> AND: true if all compound conditions are true
+  26
+  27* OrCondition =                -> OR: true if
+  28*    AndCondition[]               -> any of the AndConditions are true
+  29* || IndexCondition[]             -> any of thr IndexConditions are true
+  30
+  31* AndCondition =               -> AND: true if
+  32*    SetAndCondition              -> all SetAndConditions are true
+  33* || TermAndCondition             -> or if all TermAndConditions are true
+  34*
+  35* SetAndCondition = {          -> AND: true if all sub-conditions are true
+  36*    'or':  RecursiveCondition    -> true if any RecursiveCondition is true
+  37* || 'and': RecursiveCondition    -> true if all RecursiveCondition are true
+  38* || 'not': RecursiveCondition    -> true if the condition is false
+  39*
+  40* TermAndCondition = {         -> Terminal AND: true if all terminal sub-conditions are true
+  41*    colDesc:colValue             -> true if colValue matches 
+  42* || colDesc:[colValue, ...]      -> true if any of the colValues match
+  43* || colDesc:function(value,row)  -> true if function returns true 
+  44* }
+  45
+  46* colDesc = either column name or index
+  47* ```
+  48
+  49* ### Practical Tips
+  50* ```
+  51*    {'or': [recurCond, ...]}    -> OR, same as [recurCond, ...]
+  52* || {'or': {SetCond, ...}}      -> OR, same as [SetCond, ...]
+  53* || {'and': [recurCond, ...]}   -> AND, true if all recurCond are true
+  54* || {'and': {SetCond, ...}}     -> AND, same as {SetCond, ...}
+  55* || {'not': {SetCond, ...}}     -> NAND: true if the SetCond are true
+  56* || {'not': [recurCond, ...]}   -> NOR: true if any of the recurCond are true
+  57* ```
+  58*      
+  59* # Example
+  60
+  61
+  62* const colNames = ['Name', 'Value', 'Start', 'End'];
+  63* const rows = [
+  64*   ['Harry', '100', '3/1/14', '11/20/14'], 
+  65*   ['Mary', '1500', '7/1/14',  '9/30/14'],
+  66*   ['Peter', '400', '5/20/14', '4/30/15'],  
+  67*   ['Jane', '700', '11/13/14', '8/15/15']
+  68* ]
+  69* const data = new hsdatab.Data({colNames:colNames, rows:rows});
+  70
+  71* queries = [
+  72*   ['0', undefined,                           'undefined query => pass all'],
+  73*   ['1', [],                                  'empty OR:  []   => fail all'],
+  74*   ['2', {},                                  'empty AND: {}   => pass all'],
+  75*   ['3', 1,                                   '2nd row: pass row 1'],
+  76*   ['4', [1,3],                               '2nd+4th: pass rows: 1 and 3'],
+  77*   ['5', {Name:"Jane"},                       'Name is Jane'],
+  78*   ['6', {1:1500},                            'Column 2 is 1500'],
+  79*   ['7', {Name:["Peter", "Jane"]},            'Name is Peter or Jane'],
+  80*   ['8', [{Name:"Peter"}, {Value:1500}],      'Name is Peter or Value is 1500'],
+  81*   ['9', {or:{Name:"Peter", Value:1500}},     'OR:  same as 8:'],
+  82*   ['A', {or:[{Name:"Peter"}, {Value:1500}]}, 'OR: [{Name is Peter}, {Value is 1500}]'],
+  83*   ['B', {Name:"Peter", Value:400},           'Name is Peter and Value is 400'],
+  84*   ['C', {and:{Name:"Peter", Value:400}},     'AND: {Name is Peter, Value is 400}'],
+  85*   ['D', {and:{Name:"Peter", Value:1500}},    'AND: {Name is Peter, Value is 1500}'],
+  86*   ['E', {and:[{Name:"Peter"}, {Value:400}]}, 'AND:[{Name is Peter}, {Value is 400}]'],
+  87*   ['F', {and:[{Name:"Peter"}, {Value:1500}]},'AND:[{Name is Peter}, {Value is 1500}]'],
+  88*   ['G', {not:{Name:"Peter", Value:400}},     'NAND: not {Name is Peter and Value is 400}'],
+  89*   ['H', {not:{Name:"Peter", Value:1500}},    'NAND: not {Name is Peter and Value is 1500}'],
+  90*   ['I', {not:[{Name:"Peter"}, {Value:1500}]},'NOR: not [{Name is Peter} or {Value is 1500}]'],
+  91*   ['J', {Name:(v) => v.length===4},          'Name has 4 letters']
+  92* ];
+  93*
+  94* m.mount(root, { 
+  95*   view:() => m('', [
+  96*       m('h3', 'Given the data set:'),
+  97*       m('table#data', [
+  98*           m('tr', colNames.map(n => m('th', n))),
+  99*           ...rows.map(row => m('tr', [m('td', row[0]),m('td', row[1]),m('td', row[2].toDateString()),m('td', row[3].toDateString())]))
+ 100*       ]),
+ 101*       m('h3', 'The following queries yield:'),
+ 102*       m('table', [
+ 103*           m('tr', [m('th','#'), m('th','Query'), m('th',"Live Result, by 'Name' field")]),
+ 104*           ...queries.map(q => {
+ 105*               const result = data.filter(q[1]).getColumn('Name').join(', ');
+ 106*               return m('tr', [m('td',`${q[0]}:`), m('td',`${q[2]}`), m('td',`[ ${result} ]`)]);
+ 107*           })
+ 108*       ])
+ 109*   ])
+ 110* });
+ 111
+ 112
+ 113*   $exampleID { height: 600px; }
+ 114*   #data th { width:15%; }
+ 115*   table { 
+ 116*       font-size: 10pt;
+ 117*       margin-left: 10px;
+ 118*   }
+ 119
+ 120*      
+ 121*/

+ 122
+ 123/** */
+ 124import { Data,
+ 125         DataVal,
+ 126         DataRow
+ 127} from './Data'; 
+ 128
+ 129export type Condition = IndexCondition | RecursiveCondition;
+ 130
+ 131/** true if row index matches the number(s) */
+ 132export type IndexCondition = number;
+ 133
+ 134export type RecursiveCondition = AndCondition | OrCondition;
+ 135export type OrCondition = AndCondition[] | IndexCondition[];
+ 136export type AndCondition = SetAndCondition | TermAndCondition;
+ 137
+ 138export interface SetAndCondition {
+ 139    or?: RecursiveCondition;
+ 140    and?:RecursiveCondition;
+ 141    not?:RecursiveCondition;
+ 142};
+ 143
+ 144export interface TermAndCondition { 
+ 145    [colDesc:string]: 
+ 146        DataVal 
+ 147      | DataVal[]
+ 148      | TermConditionFunction
+ 149    ;
+ 150};
+ 151
+ 152export type TermConditionFunction = (value:DataVal, row:DataRow) => boolean;
+ 153
+ 154
+ 155function resolveTerminalCondition(name:string, val:any, row:DataRow, colNumber:(name:string)=>number):boolean { 
+ 156    const col = colNumber(name);
+ 157    const valIsFunction = (typeof val === 'function');
+ 158    const valIsArray = ((typeof val === 'object') && (val.length!==undefined));
+ 159    if (isNaN(col)) { 
+ 160        console.log(`column name '${name}' cannot be resolved in terminal condition ${name}=${val}`);
+ 161        console.log(row);
+ 162        return false; // -> this condition is not met;
+ 163    } else if (valIsFunction) { 
+ 164        // query true if function evaluates to true
+ 165        return val(row[col], row);
+ 166    } else if (valIsArray) {
+ 167        // query true if empty array, or at least one c true
+ 168        return (val.length === 0) || val.some((v:any) => row[col] === v); 
+ 169    } else { // object: all conditions have to be met, unless specified as or
+ 170        return (row[col] === val); 
+ 171    }
+ 172}
+ 173
+ 174/**
+ 175 * applies `condition` to a row of data and returns `true` if the row passes.
+ 176 * @param condition the complex condition to test against
+ 177 * @param r the row index in the data set
+ 178 * @param row the row values 
+ 179 * @param and 
+ 180 */

+ 181function resolveCondition(condition:Condition, row:DataRow, r:number, colNumber:(name:string)=>number, and=true):boolean { 
+ 182    let orResult = false;
+ 183    let andResult= true;          
+ 184    // undefined condition is TRUE
+ 185    if (condition===undefined) { return true; }
+ 186    
+ 187    // Simple Index Condition on row index:
+ 188    else if (typeof condition === 'number') { return (condition === r); }
+ 189
+ 190    // Recursive Condition - OR: [...], AND {...}: 
+ 191    else if (typeof condition === 'object') {
+ 192        // array -> or condition on a list of row indices or compound conditions
+ 193        const mc = condition;
+ 194
+ 195        // OR condition: [...] 
+ 196        if (mc.length !== undefined) {            
+ 197            return (mc.length === 0)? 
+ 198                // empty OR is false:
+ 199                false : 
+ 200                // else: OR is true if any sub-condition is met
+ 201                mc.some((cd:AndCondition) => resolveCondition(cd, row, r, colNumber, false));
+ 202        } 
+ 203        // AND condition: {...}
+ 204        else { 
+ 205            for (const name in condition) {
+ 206                let conditionMet = and; // initialize with false for OR, and true for AND conjunction
+ 207                const setCond = condition;
+ 208                
+ 209                // resolve SetConditions:
+ 210                switch (name) {
+ 211                    case 'or':  conditionMet = resolveCondition(setCond.or, row, r, colNumber, false); break;
+ 212                    case 'and': conditionMet = resolveCondition(setCond.and, row, r, colNumber, true); break;
+ 213                    case 'not': conditionMet = !resolveCondition(setCond.not, row, r, colNumber, true); break;
+ 214                    default:    conditionMet = resolveTerminalCondition(name, condition[name], row, colNumber); 
+ 215                }
+ 216                if (conditionMet) { orResult  = true;  if(!and) { break; }} // OR conjunction: exit for name loop if condition met
+ 217                             else { andResult = false; if(and)  { break; }} // AND conjunction: exit for name loop if condition not met
+ 218            }
+ 219        }    
+ 220    } else {
+ 221        console.error(`unrecognized condition: ${JSON.stringify(condition)}`);
+ 222        return false;
+ 223    }
+ 224    return and? andResult : orResult;
+ 225}
+ 226
+ 227/**
+ 228 * filters a `Data` object for the given `Condition`s and returns a new `Data` object with those rows for which
+ 229 * `cond` holds true.
+ 230 * @param data the `Data` object to filter
+ 231 * @param cond the complex condition to test against
+ 232 * @return a new `Data` object with the filtered rows 
+ 233 */

+ 234export function filter(data:Data, cond:Condition):Data {
+ 235    const colNumber = (name:string):number => data.colNumber(name);
+ 236    try {
+ 237        return new Data({
+ 238            name:     data.getName(),
+ 239            colNames: data.colNames(), 
+ 240            rows:data.getData().filter((row:DataRow, i:number) => {
+ 241                const keep = resolveCondition(cond, row, i, colNumber);
+ 242                return keep;
+ 243            })
+ 244        });
+ 245    } catch(err) {
+ 246        console.log(err);
+ 247        console.log(err.stack);
+ 248    }
+ 249}
+ 250
+ + \ No newline at end of file diff --git a/src/DataFilters.ts b/src/DataFilters.ts deleted file mode 100644 index 18a2261..0000000 --- a/src/DataFilters.ts +++ /dev/null @@ -1,254 +0,0 @@ - -/** -* -* The HsData object will feature its own column meta information, as well as -* a copy of the rows array which allows for `filter` and `sort` operations. -* However, the column arrays will be shared with the original HsData object in order to be memory efficient. -* This means that `map` and `newColumn` operations on the new object will affect the original object or any -* object derived via `query`. -* @description executes a query on the data. Each row in the data is checked and those for which -* `conditions` is true are returned as a new HsData object. -* -* ## General Condition -* ``` -* Condition = -* IndexCondition -> conditions on the row index -* || RecursiveCondition -> (set of) conditions on column values -* ``` -* -* ## IndexCondition -* ``` -* IndexCondition = -* rowIndex:number -> true if row index matches -* ``` -* -* ## RecursiveCondition -* ``` -* RecursiveCondition = -* OrCondition -> OR: true if any compound condition is true -* || AndCondition -> AND: true if all compound conditions are true -* -* OrCondition = -> OR: true if -* AndCondition[] -> any of the AndConditions are true -* || IndexCondition[] -> any of thr IndexConditions are true -* -* AndCondition = -> AND: true if -* SetAndCondition -> all SetAndConditions are true -* || TermAndCondition -> or if all TermAndConditions are true -* -* SetAndCondition = { -> AND: true if all sub-conditions are true -* 'or': RecursiveCondition -> true if any RecursiveCondition is true -* || 'and': RecursiveCondition -> true if all RecursiveCondition are true -* || 'not': RecursiveCondition -> true if the condition is false -* -* TermAndCondition = { -> Terminal AND: true if all terminal sub-conditions are true -* colDesc:colValue -> true if colValue matches -* || colDesc:[colValue, ...] -> true if any of the colValues match -* || colDesc:function(value,row) -> true if function returns true -* } -* -* colDesc = either column name or index -* ``` -* -* ## Practical Tips -* ``` -* {'or': [recurCond, ...]} -> OR, same as [recurCond, ...] -* || {'or': {SetCond, ...}} -> OR, same as [SetCond, ...] -* || {'and': [recurCond, ...]} -> AND, true if all recurCond are true -* || {'and': {SetCond, ...}} -> AND, same as {SetCond, ...} -* || {'not': {SetCond, ...}} -> NAND: true if the SetCond are true -* || {'not': [recurCond, ...]} -> NOR: true if any of the recurCond are true -* ``` -* -* # Example -* -* -* const colNames = ['Name', 'Value', 'Start', 'End']; -* const rows = [ -* ['Harry', '100', '3/1/14', '11/20/14'], -* ['Mary', '1500', '7/1/14', '9/30/14'], -* ['Peter', '400', '5/20/14', '4/30/15'], -* ['Jane', '700', '11/13/14', '8/15/15'] -* ] -* const data = new hsdata.Data({colNames:colNames, rows:rows}); -* -* queries = [ -* ['0', undefined, 'undefined query => pass all'], -* ['1', [], 'empty OR: [] => fail all'], -* ['2', {}, 'empty AND: {} => pass all'], -* ['3', 1, '2nd row: pass row 1'], -* ['4', [1,3], '2nd+4th: pass rows: 1 and 3'], -* ['5', {Name:"Jane"}, 'Name is Jane'], -* ['6', {1:1500}, 'Column 2 is 1500'], -* ['7', {Name:["Peter", "Jane"]}, 'Name is Peter or Jane'], -* ['8', [{Name:"Peter"}, {Value:1500}], 'Name is Peter or Value is 1500'], -* ['9', {or:{Name:"Peter", Value:1500}}, 'OR: same as 8:'], -* ['A', {or:[{Name:"Peter"}, {Value:1500}]}, 'OR: [{Name is Peter}, {Value is 1500}]'], -* ['B', {Name:"Peter", Value:400}, 'Name is Peter and Value is 400'], -* ['C', {and:{Name:"Peter", Value:400}}, 'AND: {Name is Peter, Value is 400}'], -* ['D', {and:{Name:"Peter", Value:1500}}, 'AND: {Name is Peter, Value is 1500}'], -* ['E', {and:[{Name:"Peter"}, {Value:400}]}, 'AND:[{Name is Peter}, {Value is 400}]'], -* ['F', {and:[{Name:"Peter"}, {Value:1500}]},'AND:[{Name is Peter}, {Value is 1500}]'], -* ['G', {not:{Name:"Peter", Value:400}}, 'NAND: not {Name is Peter and Value is 400}'], -* ['H', {not:{Name:"Peter", Value:1500}}, 'NAND: not {Name is Peter and Value is 1500}'], -* ['I', {not:[{Name:"Peter"}, {Value:1500}]},'NOR: not [{Name is Peter} or {Value is 1500}]'], -* ['J', {Name:(v) => v.length===4}, 'Name has 4 letters'] -* ]; -* -* m.mount(root, { -* view:() => m('', [ -* m('h3', 'Given the data set:'), -* m('table#data', [ -* m('tr', colNames.map(n => m('th', n))), -* ...rows.map(row => m('tr', [m('td', row[0]),m('td', row[1]),m('td', row[2].toDateString()),m('td', row[3].toDateString())])) -* ]), -* m('h3', 'The following queries yield:'), -* m('table', [ -* m('tr', [m('th','#'), m('th','Query'), m('th',"Live Result, by 'Name' field")]), -* ...queries.map(q => { -* const result = data.filter(q[1]).getColumn('Name').join(', '); -* return m('tr', [m('td',`${q[0]}:`), m('td',`${q[2]}`), m('td',`[ ${result} ]`)]); -* }) -* ]) -* ]) -* }); -* -* -* $exampleID { height: 600px; } -* #data th { width:15%; } -* table { -* font-size: 10pt; -* margin-left: 10px; -* } -* -* -*/ - -/** */ -import { Data, - DataVal, - DataRow -} from './Data'; - -export type Condition = IndexCondition | RecursiveCondition; - -/** true if row index matches the number(s) */ -export type IndexCondition = number; - -export type RecursiveCondition = AndCondition | OrCondition; -export type OrCondition = AndCondition[] | IndexCondition[]; -export type AndCondition = SetAndCondition | TermAndCondition; - -export interface SetAndCondition { - or?: RecursiveCondition; - and?:RecursiveCondition; - not?:RecursiveCondition; -}; - -export interface TermAndCondition { - [colDesc:string]: - DataVal - | DataVal[] - | TermConditionFunction - ; -}; - -export type TermConditionFunction = (value:DataVal, row:DataRow) => boolean; - - -function resolveTerminalCondition(name:string, val:any, row:DataRow, colNumber:(name:string)=>number):boolean { - const col = colNumber(name); - const valIsFunction = (typeof val === 'function'); - const valIsArray = ((typeof val === 'object') && (val.length!==undefined)); - if (isNaN(col)) { - console.log(`column name '${name}' cannot be resolved in terminal condition ${name}=${val}`); - console.log(row); - return false; // -> this condition is not met; - } else if (valIsFunction) { - // query true if function evaluates to true - return val(row[col], row); - } else if (valIsArray) { - // query true if empty array, or at least one c true - return (val.length === 0) || val.some((v:any) => row[col] === v); - } else { // object: all conditions have to be met, unless specified as or - return (row[col] === val); - } -} - -/** - * applies `condition` to a row of data and returns `true` if the row passes. - * @param condition the complex condition to test against - * @param r the row index in the data set - * @param row the row values - * @param and - */ -function resolveCondition(condition:Condition, row:DataRow, r:number, colNumber:(name:string)=>number, and=true):boolean { - let orResult = false; - let andResult= true; - // undefined condition is TRUE - if (condition===undefined) { return true; } - - // Simple Index Condition on row index: - else if (typeof condition === 'number') { return (condition === r); } - - // Recursive Condition - OR: [...], AND {...}: - else if (typeof condition === 'object') { - // array -> or condition on a list of row indices or compound conditions - const mc = condition; - - // OR condition: [...] - if (mc.length !== undefined) { - return (mc.length === 0)? - // empty OR is false: - false : - // else: OR is true if any sub-condition is met - mc.some((cd:AndCondition) => resolveCondition(cd, row, r, colNumber, false)); - } - // AND condition: {...} - else { - for (const name in condition) { - let conditionMet = and; // initialize with false for OR, and true for AND conjunction - const setCond = condition; - - // resolve SetConditions: - switch (name) { - case 'or': conditionMet = resolveCondition(setCond.or, row, r, colNumber, false); break; - case 'and': conditionMet = resolveCondition(setCond.and, row, r, colNumber, true); break; - case 'not': conditionMet = !resolveCondition(setCond.not, row, r, colNumber, true); break; - default: conditionMet = resolveTerminalCondition(name, condition[name], row, colNumber); - } - if (conditionMet) { orResult = true; if(!and) { break; }} // OR conjunction: exit for name loop if condition met - else { andResult = false; if(and) { break; }} // AND conjunction: exit for name loop if condition not met - } - } - } else { - console.error(`unrecognized condition: ${JSON.stringify(condition)}`); - return false; - } - return and? andResult : orResult; -} - -export type ReduceFn = (keep:boolean, row:DataRow, i:number) => void; - -export function filter(data:Data, cond:Condition, reduceFn?:string|ReduceFn):Data { - const noop = () => 0; - const colNumber = (name:string):number => data.colNumber(name); - let fn:ReduceFn; - switch (reduceFn) { - default: fn = (typeof reduceFn === 'function')? reduceFn : noop; - } - try { - return new Data({ - name: data.getName(), - colNames: data.colNames(), - rows:data.getData().filter((row:DataRow, i:number) => { - const keep = resolveCondition(cond, row, i, colNumber); - if (fn) { fn(keep, row, i); } - return keep; - }) - }); - } catch(err) { - console.log(err); - console.log(err.stack); - } -} diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..07a3840 --- /dev/null +++ b/src/index.html @@ -0,0 +1,32 @@ + + +

src/index.ts

+
   1export { NumRange,
+   2         NumDomain,
+   3         DateDomain,
+   4         NameDomain,
+   5         Domain,
+   6         ColumnReference,
+   7         DataVal,
+   8         DataRow,
+   9         DataSet
+  10        }       from './Data';
+  11
+  12export { Data } from './Data';
+  13export { Condition} from './DataFilters';
+  14
+  15
+ + \ No newline at end of file diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index d2b3579..0000000 --- a/src/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -export { NumRange, - NumDomain, - DateDomain, - NameDomain, - Domain, - ColumnReference, - DataVal, - DataRow, - DataSet - } from './Data'; - -export { Data } from './Data'; -export { Condition} from './DataFilters'; - diff --git a/src/overview.html b/src/overview.html new file mode 100644 index 0000000..bc9c4aa --- /dev/null +++ b/src/overview.html @@ -0,0 +1,96 @@ + + +

src/overview.ts

+
   1/**
+   2 * # hsDatab
+   3 * 
+   4 * Helpful Scripts framework-independent data management functions. [Github page](https://github.com/HelpfulScripts/hsDatab)
+   5 * 
+   6 * **hsDatab** provides a JavaScript-based data management and query mechanism.
+   7 * Data is managed in a simple in-memory database that holds data in rows of columns. 
+   8 * It autodetermines the types of data held in each column, along with the 
+   9 * domain range for each column of data. 
+  10 * Complex filters can be applied by defining {@link DataFilters `Condition`}s using a simple query object structure. 
+  11 * 
+  12 * ## Data Types
+  13 * supported {@link Data.Data.type data types} include
+  14 * - **number**: numeric values
+  15 * - **name**: nominal values, represented by arbitrary words
+  16 * - **date**: date values
+  17 * - **currency**: Currently supported: '$dd[,ddd]'
+  18 * - **percent**: 'd%'
+  19 * 
+  20 * ## Data Class
+  21 * The fundamental object in this library is {@link Data.Data `Data`}, 
+  22 * a simple row-column based database object, 
+  23 * featuring named columns, sorting, mapping and filtering functions.
+  24 *
+  25 * ## Example
+  26 * 
+  27 * 
+  28 * const colNames = ['Name', 'Value', 'Start', 'End'];
+  29 * const rows = [
+  30 *   ['Harry', '100', '3/1/14', '11/20/14'], 
+  31 *   ['Mary', '1500', '7/1/14',  '9/30/14'],
+  32 *   ['Peter', '400', '5/20/14', '4/30/15'],  
+  33 *   ['Jane', '700', '11/13/14', '8/15/15']
+  34 * ]
+  35 * const data = new hsdatab.Data({colNames:colNames, rows:rows});
+  36 * 
+  37 * query = {Name:["Peter", "Jane"]};
+  38 * const result = data.filter(query);
+  39 *
+  40 * m.mount(root, {
+  41 *   view:() => m('', [
+  42 *       m('h3', 'Given the data set:'),
+  43 *       m('pre',
+  44 *       m('table#data', [
+  45 *           m('tr', colNames.map(n => m('th', n))),
+  46 *           ...rows.map(row => m('tr', [
+  47 *              m('td', row[0]),
+  48 *              m('td', row[1]),
+  49 *              m('td', `${row[2].getMonth()+1}/${row[2].getDate()}/${row[2].getFullYear()}`),
+  50 *              m('td', `${row[3].getMonth()+1}/${row[3].getDate()}/${row[3].getFullYear()}`)
+  51 *          ]))
+  52 *       ])),
+  53 *       m('h3', 'The column types and domains are'),
+  54 *       m('pre', m('table', 
+  55 *                  m('tr', m('th', 'Column'),   m('th', 'Type'),   m('th', 'Domain')),
+  56 *                  m('tr', m('td', '"Name":'),  m('td', data.colType("Name")),   m('td', data.findDomain("Name").join(', '))),
+  57 *                  m('tr', m('td', '"Value":'), m('td', data.colType("Value")),  m('td', data.findDomain("Value").join(' - '))),
+  58 *                  m('tr', m('td', '"Start":'), m('td', data.colType("Start")),  m('td', data.findDomain("Start").map(d => d.toDateString()).join(' - '))),
+  59 *                  m('tr', m('td', '"Stop":'),  m('td', data.colType("End")),    m('td', data.findDomain("End").map(d => d.toDateString()).join(' - ')))
+  60 *       )),
+  61 *       m('h3', 'The query:'),
+  62 *       m('code', '{Name:["Peter", "Jane"]}'),
+  63 *       m('h3', 'yields results with "Name"'),
+  64 *       m('code', result.getColumn('Name').join(', '))
+  65 *   ])
+  66 * });
+  67 * 
+  68 * 
+  69 *   $exampleID { height: 600px; }
+  70 *   #data th { width:15%; }
+  71 *   #data  { font-size: 11pt; }
+  72 * 
+  73 *      
+  74 */

+  75
+  76 /** */
+  77
+  78 
+  79
+ + \ No newline at end of file diff --git a/src/overview.ts b/src/overview.ts deleted file mode 100644 index 2c6f1a7..0000000 --- a/src/overview.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * # hsData - * - * Helpful Scripts data management functions that are framework independent. - * - * ## Data Types - * -   {@link Data.NumRange NumRange} defines a single [min, max] numeric range. - * -   {@link Data.NumDomain NumDomain} defines a numeric domain that includes all values of a column - * -   {@link Data.DateDomain DateDomain} defines a Date domain that includes all values of a column - * -   {@link Data.NameDomain NameDomain} defines a categorical domain that includes all values of a column - * -   {@link Data.Domain Domain} defines a generic domain that can be any of the typed domains. - * -   {@link Data.ColSpecifier ColSpecifier} defines a Column Specifier, either as column name or index in the {@link Data.DataRow `DataRow`} array - * -   {@link Data.DataVal DataVal} a generic data value type, used in the {@link Data.DataRow `DataRow`} array - * -   {@link Data.DataRow DataRow} a single row of column values - * - * ## Data Class - * -   {@link Data.Data Data} A simple row-column based database object, featuring named columns, sorting, mapping and filtering functions - */ - - /** */ - - diff --git a/tsconfigGrunt.json b/tsconfigGrunt.json deleted file mode 100644 index 0135a6d..0000000 --- a/tsconfigGrunt.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", - "module": "es6", - "rootDir": "./src", - "moduleResolution": "node", - "inlineSourceMap": true, - "removeComments": true, - "noImplicitAny": true, - "suppressImplicitAnyIndexErrors": true, - "declaration": true - }, - "compileOnSave": true, - "include": [], - "exclude": ["**/node_modules"] -} diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 5694204..0000000 --- a/tslint.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "rules": { - "no-unused-expression": true, - "no-duplicate-variable": true, - "no-unused-variable": true, - "curly": true, - "class-name": true, - "semicolon": ["always"], - "triple-equals": true - } -} \ No newline at end of file