From c8d5bdcc42262badbcaa6782bf2bf0a5e1a94d32 Mon Sep 17 00:00:00 2001 From: DenQ Date: Fri, 15 Sep 2017 12:06:44 +0300 Subject: [PATCH 1/5] Split method works --- dist/list-to-tree.js | 36 ++++++++++++++++++++++++++++++++ spec/split-spec.js | 49 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 spec/split-spec.js diff --git a/dist/list-to-tree.js b/dist/list-to-tree.js index 87f9f74..2bdbe9a 100644 --- a/dist/list-to-tree.js +++ b/dist/list-to-tree.js @@ -23,6 +23,20 @@ function sortBy(collection, propertyA, propertyB) { }); }; +function compareById(vector) { + return (a, b) => { + const aid = Number(a.parent); + const bid = Number(b.parent); + if (aid > bid) { + return vector ? 1 : -1; + } else if (aid < bid) { + return vector ? -1 : 1; + } else { + return 0 + } + }; +} + module.exports = class LTT{ constructor(list, options = {}) { @@ -47,6 +61,28 @@ module.exports = class LTT{ this.tree.sort(criteria); } + split(list) { + list.sort(compareById(true)); + const rootParentId = list[0].parent; + + const collection = []; + list.forEach((item) => { + if (item.parent === rootParentId) { + collection.push([item]); + } else { + collection.forEach((el) => { + el.forEach((child) => { + if (child.id === item.parent) { + el.push(item); + } + }); + }); + + } + }); + return collection; + } + GetTree() { const { key_child, empty_children } = this.options; return this.tree.toJson({ diff --git a/spec/split-spec.js b/spec/split-spec.js new file mode 100644 index 0000000..6d95a6f --- /dev/null +++ b/spec/split-spec.js @@ -0,0 +1,49 @@ +const LTT = require('../dist/list-to-tree'); + +const key_id = 'id'; +const key_parent = 'parent'; +const key_child = 'child'; +const list = [ + { id: 1, parent: 0 }, + { id: 2, parent: 0 }, + { id: 3, parent: 2 }, + { id: 4, parent: 3 }, + { id: 5, parent: 2 }, + { id: 6, parent: 1 }, + { id: 7, parent: 0 }, + { id: 8, parent: 7 }, + { id: 9, parent: 7 }, + { id: 10, parent: 8 }, + { id: 11, parent: 6 }, + { id: 12, parent: 0 }, +]; +const ltt = new LTT(list, { + key_id, + key_parent, + key_child, +}); + +describe('Split', function() { + + it('Run method - split', function() { + const collection = ltt.split(list); + expect(collection.length).toBe(4); + + expect(collection[0][0].id).toBe(7); + expect(collection[0][1].id).toBe(8); + expect(collection[0][2].id).toBe(9); + expect(collection[0][3].id).toBe(10); + + expect(collection[1][0].id).toBe(1); + expect(collection[1][1].id).toBe(6); + expect(collection[1][2].id).toBe(11); + + expect(collection[2][0].id).toBe(2); + expect(collection[2][1].id).toBe(5); + expect(collection[2][2].id).toBe(3); + expect(collection[2][3].id).toBe(4); + + expect(collection[3][0].id).toBe(12); + }); + +}) From df17ead2afcf3dfb6f88431f999716ea431ed976 Mon Sep 17 00:00:00 2001 From: DenQ Date: Fri, 15 Sep 2017 12:12:19 +0300 Subject: [PATCH 2/5] Move help function to utils --- dist/list-to-tree.js | 34 +++------------------------------- utils/compare-by-id.js | 13 +++++++++++++ utils/sort-by.js | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 31 deletions(-) create mode 100644 utils/compare-by-id.js create mode 100644 utils/sort-by.js diff --git a/dist/list-to-tree.js b/dist/list-to-tree.js index 2bdbe9a..b831052 100644 --- a/dist/list-to-tree.js +++ b/dist/list-to-tree.js @@ -1,4 +1,6 @@ const IronTree = require('@denq/iron-tree'); +const sortBy = require('../utils/sort-by'); +const compareById = require('../utils/compare-by-id'); const defaultOptions = { key_id: 'id' , @@ -7,36 +9,6 @@ const defaultOptions = { empty_children: false, }; -function sortBy(collection, propertyA, propertyB) { - return collection.sort(function(a, b) { - if (a[propertyB] < b[propertyB]) { - if (a[propertyA] > b[propertyA]) { - return 1; - } - return -1; - } else { - if (a[propertyA] < b[propertyA]) { - return -1; - } - return 1; - } - }); -}; - -function compareById(vector) { - return (a, b) => { - const aid = Number(a.parent); - const bid = Number(b.parent); - if (aid > bid) { - return vector ? 1 : -1; - } else if (aid < bid) { - return vector ? -1 : 1; - } else { - return 0 - } - }; -} - module.exports = class LTT{ constructor(list, options = {}) { @@ -62,7 +34,7 @@ module.exports = class LTT{ } split(list) { - list.sort(compareById(true)); + list.sort(compareById(true, 'parent')); const rootParentId = list[0].parent; const collection = []; diff --git a/utils/compare-by-id.js b/utils/compare-by-id.js new file mode 100644 index 0000000..d8d8394 --- /dev/null +++ b/utils/compare-by-id.js @@ -0,0 +1,13 @@ +module.exports = function compareById(vector, key) { + return (a, b) => { + const aid = Number(a[key]); + const bid = Number(b[key]); + if (aid > bid) { + return vector ? 1 : -1; + } else if (aid < bid) { + return vector ? -1 : 1; + } else { + return 0 + } + }; +} diff --git a/utils/sort-by.js b/utils/sort-by.js new file mode 100644 index 0000000..5bf9d47 --- /dev/null +++ b/utils/sort-by.js @@ -0,0 +1,15 @@ +module.exports = function sortBy(collection, propertyA, propertyB) { + return collection.sort(function(a, b) { + if (a[propertyB] < b[propertyB]) { + if (a[propertyA] > b[propertyA]) { + return 1; + } + return -1; + } else { + if (a[propertyA] < b[propertyA]) { + return -1; + } + return 1; + } + }); +} From 3c64b3fe9000be79a560245e2b7042d3baac89a7 Mon Sep 17 00:00:00 2001 From: DenQ Date: Fri, 15 Sep 2017 12:17:21 +0300 Subject: [PATCH 3/5] Extract 'buildTree' method --- dist/list-to-tree.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/dist/list-to-tree.js b/dist/list-to-tree.js index b831052..fc10164 100644 --- a/dist/list-to-tree.js +++ b/dist/list-to-tree.js @@ -16,17 +16,23 @@ module.exports = class LTT{ options = Object.assign({}, defaultOptions, options); this.options = options; - const { key_id, key_parent } = options; - sortBy(_list, key_parent, key_id); + const tree = this.buildTree(_list); + + this.tree = tree; + } + + buildTree(list) { + const { key_id, key_parent } = this.options; + + sortBy(list, key_parent, key_id); const tree = new IronTree({ [key_id]: 0 }); - _list.forEach((item, index) => { + list.forEach((item, index) => { tree.add((parentNode) => { return parentNode.get(key_id) === item[key_parent]; }, item); }); - - this.tree = tree; + return tree; } sort(criteria) { From 3f650d58f12a1fc10f78f644702861d26f3960b7 Mon Sep 17 00:00:00 2001 From: DenQ Date: Fri, 15 Sep 2017 12:20:56 +0300 Subject: [PATCH 4/5] Dependency options for split method --- dist/list-to-tree.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/dist/list-to-tree.js b/dist/list-to-tree.js index fc10164..2c478d7 100644 --- a/dist/list-to-tree.js +++ b/dist/list-to-tree.js @@ -17,8 +17,9 @@ module.exports = class LTT{ options = Object.assign({}, defaultOptions, options); this.options = options; + // this.split(_list); const tree = this.buildTree(_list); - + this.tree = tree; } @@ -40,17 +41,19 @@ module.exports = class LTT{ } split(list) { - list.sort(compareById(true, 'parent')); - const rootParentId = list[0].parent; + const { key_id, key_parent } = this.options; + + list.sort(compareById(true, key_parent)); + const rootParentId = list[0][key_parent]; const collection = []; list.forEach((item) => { - if (item.parent === rootParentId) { + if (item[key_parent] === rootParentId) { collection.push([item]); } else { collection.forEach((el) => { el.forEach((child) => { - if (child.id === item.parent) { + if (child[key_id] === item[key_parent]) { el.push(item); } }); From 455974d708e6df5e46459b25076aeb87a9361ef7 Mon Sep 17 00:00:00 2001 From: DenQ Date: Fri, 15 Sep 2017 17:50:27 +0300 Subject: [PATCH 5/5] Prototype for optimizations --- bin/performance.js | 14 ++++++++---- dist/list-to-tree.js | 52 ++++++++++++++++++++++++++++++++++---------- package-lock.json | 8 +++---- package.json | 2 +- spec/split-spec.js | 39 +++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 21 deletions(-) diff --git a/bin/performance.js b/bin/performance.js index b64a088..33c3378 100644 --- a/bin/performance.js +++ b/bin/performance.js @@ -1,8 +1,9 @@ +"use strict" const LTT = require('../dist/old/list-to-tree.npm.js'); const LTT1 = require('../dist/list-to-tree.js'); const IronTree = require('@denq/iron-tree'); -const LENGTH = 20000; +const LENGTH = 25000; function getList() { @@ -29,8 +30,12 @@ function performanceCalc(fn, ...params) { console.log(`Result: ${result}. Execution Time: ${end - start} ms`) } +const list = getList(); +const xlist = list.map(item => Object.assign({}, item)); + function runListToTree() { - var ltt = new LTT(getList(), { + // const _list = Array(list.length).fill().map((item, index) => list[index]); + var ltt = new LTT(xlist, { key_id: 'id', key_parent: 'parent', key_child: 'children', @@ -41,7 +46,7 @@ function runListToTree() { function runIronTree() { const tree = new IronTree({ id: 0 }); - getList().forEach((item, index) => { + list.forEach((item, index) => { tree.add((parentNode) => { return parentNode.get('id') === item.parent; }, item); @@ -53,10 +58,11 @@ function runIronTree() { } function runNewListToTree() { - const ltt = new LTT1(getList(), { + const ltt = new LTT1(list, { key_id: 'id', key_parent: 'parent', key_child: 'children', + many_items: true, }); var tree = ltt.GetTree(); return 'new list-to-tree'; diff --git a/dist/list-to-tree.js b/dist/list-to-tree.js index 2c478d7..0d27dd5 100644 --- a/dist/list-to-tree.js +++ b/dist/list-to-tree.js @@ -1,5 +1,5 @@ const IronTree = require('@denq/iron-tree'); -const sortBy = require('../utils/sort-by'); +const sortBy = require('../utils/sort-by'); const compareById = require('../utils/compare-by-id'); const defaultOptions = { @@ -7,6 +7,7 @@ const defaultOptions = { key_parent: 'parent' , key_child: 'child', empty_children: false, + many_items: false, }; module.exports = class LTT{ @@ -17,13 +18,17 @@ module.exports = class LTT{ options = Object.assign({}, defaultOptions, options); this.options = options; - // this.split(_list); - const tree = this.buildTree(_list); - - this.tree = tree; + if (options.many_items) { + const collections = this.split(_list); + this.tree = collections.map((item) => { + return this.buildTreeFromList(item); + }); + } else { + this.tree = this.buildTreeFromList(_list); + } } - buildTree(list) { + buildTreeFromList(list) { const { key_id, key_parent } = this.options; sortBy(list, key_parent, key_id); @@ -37,7 +42,23 @@ module.exports = class LTT{ } sort(criteria) { - this.tree.sort(criteria); + const { many_items } = this.options; + if (many_items) { + this.tree.forEach((item) => { + item.sort(criteria); + }); + //hack + this.tree = this.tree.map((item) => { + const id = item.rootNode.children[0].get('id') + item.set('id', id); + item.set('xid', id); + return item; + }); + this.tree.sort(criteria); + // end hack + } else { + this.tree.sort(criteria); + } } split(list) { @@ -65,11 +86,18 @@ module.exports = class LTT{ } GetTree() { - const { key_child, empty_children } = this.options; - return this.tree.toJson({ - key_children: key_child, - empty_children: false, - })[key_child]; + const { key_child, empty_children, many_items } = this.options; + if (many_items) { + const trees = this.tree.map((item) => { + return item.toJson(this.options).children[0]; + }); + return [].concat(...trees); + } else { + return this.tree.toJson({ + key_children: key_child, + empty_children: false, + })[key_child]; + } } } diff --git a/package-lock.json b/package-lock.json index 0ef1c54..82f509c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,13 @@ { "name": "list-to-tree", - "version": "2.1.0", + "version": "2.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { "@denq/iron-tree": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@denq/iron-tree/-/iron-tree-1.2.0.tgz", - "integrity": "sha512-xLjz/wKxPoI6XhwRWZDtdBJyx0CrrkrqqT+7iKmlLW3oIHaWDU7m/gTNrToxu469yH0RtwXOcqpE8dDIEEVAaw==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@denq/iron-tree/-/iron-tree-1.3.0.tgz", + "integrity": "sha512-vc4yrcynR1T9ttEIh1rMkPvXpF2XNfYGJIL1ucs8A+Xka0NEU+4wQW7OyR4N+VgYUfArfsqD+dNTkyRhDL55mQ==" }, "balanced-match": { "version": "1.0.0", diff --git a/package.json b/package.json index 1e0604d..8131f0e 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,6 @@ "jasmine": "^2.8.0" }, "dependencies": { - "@denq/iron-tree": "^1.2.0" + "@denq/iron-tree": "^1.3.0" } } diff --git a/spec/split-spec.js b/spec/split-spec.js index 6d95a6f..dc92102 100644 --- a/spec/split-spec.js +++ b/spec/split-spec.js @@ -1,8 +1,25 @@ +require("util").inspect.defaultOptions.depth = null; const LTT = require('../dist/list-to-tree'); +function compareById(vector) { + return (a, b) => { + const aid = Number(a.get('id')); + const bid = Number(b.get('id')); + if (aid > bid) { + return vector ? 1 : -1; + } else if (aid < bid) { + return vector ? -1 : 1; + } else { + return 0 + } + }; +} + const key_id = 'id'; const key_parent = 'parent'; const key_child = 'child'; +const many_items = true; + const list = [ { id: 1, parent: 0 }, { id: 2, parent: 0 }, @@ -21,7 +38,9 @@ const ltt = new LTT(list, { key_id, key_parent, key_child, + many_items, }); +ltt.sort(compareById(true)); describe('Split', function() { @@ -46,4 +65,24 @@ describe('Split', function() { expect(collection[3][0].id).toBe(12); }); + it('GetTree', function() { + const tree = ltt.GetTree(); + + expect(tree[0].id).toBe(1); + expect(tree[0].children[0].id).toBe(6); + expect(tree[0].children[0].children[0].id).toBe(11); + + expect(tree[1].id).toBe(2); + expect(tree[1].children[0].id).toBe(3); + expect(tree[1].children[0].children[0].id).toBe(4); + expect(tree[1].children[1].id).toBe(5); + + expect(tree[2].id).toBe(7); + expect(tree[2].children[0].id).toBe(8); + expect(tree[2].children[0].children[0].id).toBe(10); + expect(tree[2].children[1].id).toBe(9); + + expect(tree[3].id).toBe(12); + }); + })