treem
Version:
High performance conversion of flat data (like SQL result rows) into nested objects.
156 lines (143 loc) • 4.17 kB
JavaScript
import EqualArray from 'equal-array';
import Header from './header';
import Node from './node';
const DEFAULT_KEY_SYMBOL = '#';
const DEFAULT_COLLECTION_SYMBOL = '+';
const DEFAULT_SEPARATOR_SYMBOL = '.';
export default class Treem {
constructor(options = {}) {
const symbols = {
key: DEFAULT_KEY_SYMBOL,
collection: DEFAULT_COLLECTION_SYMBOL,
separator: DEFAULT_SEPARATOR_SYMBOL
}
if (options.symbols !== undefined) {
if (options.symbols.key !== undefined) {
symbols.key = options.symbols.key;
}
if (options.symbols.collection !== undefined) {
symbols.collection = options.symbols.collection;
}
if (options.symbols.separator !== undefined) {
symbols.separator = options.symbols.separator;
}
}
this._separator = symbols.separator;
// default options
this._config = {
headers: undefined,
reuseObject: 'key',
overwriteObject: false,
root: 'collection',
wrap: {
single: () => {return {}},
collection: () => []
},
detect: {
key: node => {
return {
check: node.startsWith(symbols.key),
name: node.substring(symbols.key.length)
}
},
collection: node => {
return {
check: node.startsWith(symbols.collection),
name: node.substring(symbols.collection.length)
}
}
},
prune: x => x === undefined || x === null,
equalArray: new EqualArray()
}
this._config.headers = options.headers;
if (options.reuseObject !== undefined) {
this._config.reuseObject = options.reuseObject;
}
if (options.overwriteObject !== undefined) {
this._config.overwriteObject = options.overwriteObject;
}
if (options.root !== undefined) {
this._config.root = options.root;
}
if (options.wrap !== undefined) {
if (options.wrap.single !== undefined) {
this._config.wrap.single = options.wrap.single;
}
if (options.wrap.collection !== undefined) {
this._config.wrap.collection = options.wrap.collection;
}
}
if (options.detect !== undefined) {
if (options.detect.key !== undefined) {
this._config.detect.key = options.detect.key;
}
if (options.detect.collection !== undefined) {
this._config.detect.collection = options.detect.collection;
}
}
if (options.prune !== undefined) {
if (options.prune !== true) {
if (options.prune === false) {
this._config.prune = () => false;
} else {
this._config.prune = options.prune;
}
}
}
this.reset();
}
get data() {
return this._data;
}
reset() {
this._firstFeed = true;
if (this._config.root === 'collection') {
this._data = this._config.wrap.collection('');
} else {
this._data = this._config.wrap.single('');
}
this._root = undefined;
}
fill(rows) {
this.reset();
this.feed(rows);
}
feed(rows) {
if (rows.length === 0) return;
if (this._root === undefined) {
let headers = this._config.headers;
if (headers === undefined) {
headers = {};
for (let property in rows[0]) {
headers[property] = property;
}
}
this._root = this._makeRoot(headers);
}
if (this._config.root === 'collection') {
rows.forEach(row => this._root.add(row, this));
} else {
let start = 0;
if (this._firstFeed) {
this._root.add(rows[0], this);
start = 1;
}
const memData = this._data;
for (let i = start; i < rows.length; i++) {
this._root.add(rows[i], this);
if (!this._config.overwriteObject) {
this._data = memData;
}
}
}
this._firstFeed = false;
}
_makeRoot(headers) {
const columns = [];
for (let header in headers) {
columns.push(new Header(header, headers[header], this._separator, this._config.detect));
}
return new Node('_data', columns, this._config, this._config.root === 'collection');
}
}