Skip to content

Commit 4952e6b

Browse files
Merge pull request #44 from philipstanislaus/fix-prototype-missing
feat: add option to assign to keep prototype, see #34
2 parents 7a40578 + 0ca9035 commit 4952e6b

10 files changed

+127
-7
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ You can provide a second argument to arrayToTree with configuration options. Rig
7070
- `dataField`: Key which will contain all properties/data of the original items. Set to null if you don't want a container. Default: `"data"`
7171
- `throwIfOrphans`: Option to throw an error if the array of items contains one or more items that have no parents in the array or if the array of items contains items with a circular parent/child relationship. This option has a small runtime penalty, so it's disabled by default. When enabled, the function will throw an error containing the parentIds that were not found in the items array, or in the case of only circular item relationships a generic error. The function will throw an error if the number of nodes in the tree is smaller than the number of nodes in the original array. When disabled, the function will just ignore orphans and circular relationships and not add them to the tree. Default: `false`
7272
- `rootParentIds`: Object with parent ids as keys and `true` as values that should be considered the top or root elements of the tree. This is useful when your tree is a subset of full tree, which means there is no item whose parent id is one of `undefined`, `null` or `''`. The array you pass in will be replace the default value. `undefined` and `null` are always considered to be rootParentIds. For more details, see [#23](https://github.com/philipstanislaus/performant-array-to-tree/issues/23). Default: `{'': true}`
73+
- `assign`: Option that enables `Object.assign` instead of the spread operator to create an item in the tree when `dataField` is `null`. This is useful if your items have a prototype that should be maintained. If enabled and `dataField` is `null`, the original node item will be used, and the `children` property will be assigned, calling any setters on that field. If `dataField` is not `null`, this option has no effect, since the original node will be used under the `dataField` of a new object. If you are unsure whether you need to enable this, it's likely fine to leave it disabled. Default: `false`
7374

7475
Example:
7576

build/arrayToTree.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface Config {
1414
[rootParentId: string]: true;
1515
};
1616
nestedIds: boolean;
17+
assign: boolean;
1718
}
1819
/**
1920
* Unflattens an array to a tree with runtime O(n)

build/arrayToTree.js

Lines changed: 9 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/arrayToTree.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/arrayToTree.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/arrayToTree.spec.js

Lines changed: 46 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/arrayToTree.spec.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/arrayToTree.spec.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,65 @@ describe("arrayToTree", () => {
694694
},
695695
]);
696696
});
697+
698+
it("should keep prototype if assign is enabled", () => {
699+
const animal = {
700+
legs() {
701+
return 4;
702+
},
703+
};
704+
705+
const mom = Object.create(animal);
706+
mom.id = "mom";
707+
mom.parentId = null;
708+
const kitty = Object.create(animal);
709+
kitty.id = "kitty";
710+
kitty.parentId = "mom";
711+
712+
const tree = arrayToTree([mom, kitty], { dataField: null, assign: true });
713+
714+
expect(tree).to.deep.equal([mom]);
715+
716+
expect(tree[0].__proto__).to.deep.equal(animal);
717+
718+
expect(tree[0].legs()).to.equal(4);
719+
});
720+
721+
it("should not keep prototype if assign is disabled", () => {
722+
const animal = {
723+
legs() {
724+
return 4;
725+
},
726+
};
727+
728+
const mom = Object.create(animal);
729+
mom.id = "mom";
730+
mom.parentId = null;
731+
const kitty = Object.create(animal);
732+
kitty.id = "kitty";
733+
kitty.parentId = "mom";
734+
735+
const tree = arrayToTree([mom, kitty], { dataField: null, assign: false });
736+
737+
expect(tree).to.deep.equal([
738+
{
739+
id: "mom",
740+
parentId: null,
741+
children: [
742+
{
743+
id: "kitty",
744+
parentId: "mom",
745+
children: [],
746+
},
747+
],
748+
},
749+
]);
750+
751+
expect(mom.legs()).to.equal(4);
752+
753+
expect(tree[0].__proto__).to.deep.equal(Object.prototype);
754+
expect(tree[0].legs).to.equal(undefined);
755+
});
697756
});
698757

699758
describe("countNodes", () => {

src/arrayToTree.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface Config {
1414
throwIfOrphans: boolean;
1515
rootParentIds: { [rootParentId: string]: true }; // use an object here for fast lookups
1616
nestedIds: boolean;
17+
assign: boolean;
1718
}
1819

1920
const defaultConfig: Config = {
@@ -24,6 +25,7 @@ const defaultConfig: Config = {
2425
throwIfOrphans: false,
2526
rootParentIds: { "": true },
2627
nestedIds: true,
28+
assign: false,
2729
};
2830

2931
/**
@@ -84,6 +86,10 @@ export function arrayToTree(
8486
// add the current item's data to the item in the lookup table
8587
if (conf.dataField) {
8688
lookup[itemId][conf.dataField] = item;
89+
} else if (conf.assign) {
90+
lookup[itemId] = Object.assign(item, {
91+
[conf.childrenField]: lookup[itemId][conf.childrenField],
92+
});
8793
} else {
8894
lookup[itemId] = {
8995
...item,

tslint.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"extends": ["tslint:latest", "tslint-config-prettier"],
33
"rules": {
4-
"no-implicit-dependencies": [true, "dev"]
4+
"no-implicit-dependencies": [true, "dev"],
5+
"prefer-object-spread": false
56
}
67
}

0 commit comments

Comments
 (0)