When I delete an object property, I need to remove all references to descendant properties scattered around my application:
const people = {
mary: {
children: [
john: new ChildClass(),
paul: new ChildClass(),
george: new ChildClass()
]
},
homer: {
children: [
bart: new ChildClass(),
lisa: new ChildClass(),
maggie: new ChildClass()
]
}
};
const all_children = [];
/* pseudocode:
foreach people(children) {
foreach children(child) {
all_children.push(child);
}
}
*/
all_children.forEach(child => {
/* does something with mary and homer's children */
});
//////////////////
delete people.mary;
all_children.forEach(child => {
/* STILL does something with mary and homer's children,
even though mary has been deleted */
});
So I either need a way to delete all references to descendant properties, or I need a way to indicate that the reference is tied to a deleted parent and ignore it in my calculations.
What is the proper/most maintainable way to accomplish this.
Unless you really need to keep all the children in one array for performance reasons, the easier solution is to scrap the all_children array in favor of a function that returns an array of all children, like
function getAllChildren () {
var all_children = [];
/* pseudocode:
foreach people(children) {
foreach children(child) {
all_children.push(child);
}
}
*/
return all_children;
}
This way, if you delete Mary or Homer, calling this function will automatically reflect the deletion.
If I understand the question correctly, you're afraid of the children objects still exist in memory after you remove parent object from the object array. This isn't the case for javascript, because the language doesn't implement true classes. And therefore each copy of the new ChildClass() is passed by value and not reference.
Therefore you don't need to keep track of the children references in order to clean them up.
Related
I would like to add a value with array.push to my first element of array [0] to the field yesterday, I don't know very well what the structure is to be able to add this value. try the following way
var cumpleaños = [
{
ayer: "",
},
{
hoy: "12-07-20",
} ,
{
mañana: "12-08-20"
}
];
cumpleaños.push([0].ayer.("12-06-20"))
console.log(cumpleaños[0].ayer)
Thank you very much for the help!
You don't need to push anything since you're not adding a new value, you're just modifying an existing value:
cumpleaños[0].ayer = "12-06-20";
As a side note, your data structure would be much more effectively represented by a single object not inside an array:
const cumpleaños = {
ayer: "12-06-20",
hoy: "12-07-20",
mañana: "12-08-20"
};
You would do assignment to the first element of the array. (If the oder is always the same)
cumpleaños[0].ayer = "12-06-20"
Let's say I have an array as so with objects:
[
{
"id": "5a97e047f826a0111b754beb",
"name": "Hogwarts",
"parentId": "5c7bf2191d41c810b2ad6186",
"childrenIds": []
},
{
"id": "5c7bf2191d41c810b2ad6186",
"name": "Defense Against The Dark Arts",
"parentId": null,
"childrenIds": [
"5a97e047f826a0111b754beb"
]
}
]
What I'd like to do is a function that returns another array, but this time with only items that don't have a parentID as root elements, and have a children array on them containing their children, so on reaching leaves who have an empty childrenIDs array. (also remove the parent/children id properties)
For the previous input I'd return something like this
[
{
"id": "5c7bf2191d41c810b2ad6186",
"name": "Defense Against The Dark Arts",
"children": [
{
"id": "5a97e047f826a0111b754beb",
"name": "Hogwarts"
}
]
}
]
I can't seem to think of any efficient code for this task, can anyone help?
You could keep a reference to each node by ID in an object and build the tree while you go. Since we may encounter references to entries that we didn't see yet, we'll create stubs in the meantime (consisting only of a children array) and add the rest of their fields later on.
This way we have only a single loop.
It seems you have sort of a vertical double-linked list, saving both the parent ID in the children and the children's IDs in the parent, but we need only one of those to build the tree. We'll use the parent ID stored in each child. (Note that this assumes that your structure is consistent without imbalanced relations or dangling references.)
For simplicity, we create one root node whose children we'll return at the end, so that we don't have to handle the nodes without parent any differently.
Then you could write code like this:
function makeTree (rows) {
// Use a symbol to denote the root ID to avoid clashes with any real IDs
const ROOT = Symbol('ROOT')
// Node index, by ID
// Add a root node from the start
const nodes = { [ROOT]: { children: [] } }
// Helper function to return an existing node or create a stub if not existing
const getNodeOrCreateStub = id => nodes[id] || (nodes[id] = { children: [] })
for (const row of rows) {
// Add current row to index, merging data with existing stub, if any.
// This keeps any existing references in other nodes' children arrays intact
// because Object.assign mutates the first object passed to it and returns the
// same object.
const node = Object.assign(getNodeOrCreateStub(row.id), row)
// Remove unwanted properties.
delete node.parentId
delete node.childrenIds
// Get parent or create parent stub if parent node not already existing
const parent = getNodeOrCreateStub(row.parentId || ROOT)
// Add current node as child to parent
parent.children.push(node)
}
// Return children of root node
return nodes[ROOT].children
}
Note that this code currently also creates empty children arrays in the leaf nodes, differently from your example above. However I think this makes the code simpler because it doesn't have to handle leaf nodes any different, neither in creating the tree nor in reading it later! (You wouldn't have to do children && children.length to check for children, you could always just access children directly.)
To change that and have the result exactly as in your example, you'd change the code as follows:
// Change this...
const getNodeOrCreateStub = id => nodes[id] || (nodes[id] = { children: [] })
// ...to this:
const getNodeOrCreateStub = id => nodes[id] || (nodes[id] = {})
// Also change this...
parent.children.push(node)
// ...to this:
if (!parent.children) parent.children = []
parent.children.push(node)
// ...or to this:
parent.children = [...parent.children || [], node]
I want to get all children of specific parent. I am new to node.js and not able to write recursive function to do this task
var roots = [1,2,6];
var documents = [
{
"parent_id":1
,childerens:[4,5]
}
,{
"parent_id":4
,childerens:[9]
}
,{
"parent_id":9
,childerens:[]
}
,{
"parent_id":5
,childerens:[3]
}
,{
"parent_id":3
,childerens:[]
}
]
roots.forEach(function (rootParentId) {
var allchilderens=getAllchild(rootParentId);
})
Here's an example of what I mean:
var allchilderens = getAllchild(1);
allchilderens == [4,5,9,3]
function findAllChildren(element,is_root,childerens) {
if(is_root==false&&element!=undefined){
childerens.push(element);
}
var doc = documents.find(o => o.parent_id === element);
if(doc["childerens"].length==0){
return [];
}
else{
doc["childerens"].forEach(function (element) {
findAllChildren(element,false,childerens);
})
}
}
var childerens=[];
console.log(findAllChildren(1,true,childerens));
console.log("childerens==>",childerens);
You really want to build a tree structure here as step one.
Each object should, instead of containing a list of children ids, it should contain the actual children. That way, when you want to get all children, you can traverse that tree.
You probably should consider revising your data structure here. Why not use an object, where the user id is the key (since it should be unique). Then you would just need to get those keys directly.
Having everything inside of an array is potentially bad, since you'd have to iterate through the array to find each child, which would have (at worst), a run time equal to the length of the array.
So I guess the title is selfexplanatory. I have some code with nested forEach loops inside it. The loops are iterating over an array of chapter objects. Each object can have multiple child nodes and they again can have multiple child nodes, and so on.
I want to end up with one array which contains nested arrays with the child nodes.
So far my code looks like this:
exports.chapter = function(req, res) {
var chapters = [],
result = [];
chapters = exports.index(req, res);
chapters.forEach(function(chapter) {
if(chapter.orphan){
result.add({
'chapter': chapter,
'children': getChildren(chapter.children)
});
}
});
function getChildren(siblings) {
var children = [];
chapters.forEach(function(chapter) {
if($.inArray(chapter, siblings)){
children.add({
'chapter': chapter,
'children': getChildren(chapter.children)
});
}
});
return children;
};
};
I don't get any errors except for my page not loading. It doesn't write anything in my console. I think it's a problem in the setup but I'm unable to find out where at the moment. Really hope you guys can help.
Most likely problem is here:
if($.inArray(chapter, siblings)){
$.inArray is a horribly misnamed method: It returns an index, or -1 if not found, not a flag as the name implies. -1 is, of course, truthy; and 0 (a valid index), is falsey, so your if probably wants to be
if($.inArray(chapter, siblings) != -1){
// We found it...
}
or possibly
if($.inArray(chapter, siblings) == -1){
// We didn't find it
}
It's a bit strange.. I don't understand why you're using 'add' instead of 'push' method. If I try to "add" an object to an array I get an usual error. Don't you?
I basically want the equivalent to binding to 'add' and 'remove' events in Backbone's Collections. I see basically no way of doing this in AngularJS, and the current workaround we've settled for is $watch()ing the array's length and manually diffing/recalculating the whole thing. Is this really what the cool kids do?
Edit: Specifically, watching the array's length means I don't easily know which element has been changed, I need to manually "diff".
I think using $watch is a good solution, but $watchCollection can be better for you. $watchCollection doesn't perform deep comparison and just watchs for array modification like insert, delete or sort (not item update).
For exemple, if you want to keep an attribut order synchronize with the array order :
$scope.sortableItems = [
{order: 1, text: 'foo'},
{order: 2, text: 'bar'},
{order: 3, text: 'baz'}
];
$scope.$watchCollection('sortableItems', function(newCol, oldCol, scope) {
for (var index in newCol) {
var item = newCol[index];
item.order = parseInt(index) + 1;
}
});
But for your problem, I do not know if there is a better solution than manually browse the array to identify the change.
The way to watch an array in Angular is $watch(array, function(){} ,true)
I would create child scopes and watch them individually.
here is an example:
$scope.myCollection = [];
var addChild = function()
{
var Child = $scope.$new();
Child.name = 'Your Name here';
Child.$watch('name', function(newValue) {
// .... do something when the attribute 'name' is changed ...
});
Child.$on('$destroy', function() {
//... do something when this child gets destroyed
});
$scope.myCollection.push(Child); // add the child to collection array
};
// Pass the item to this method as parameter,
// do it within an ngRepeat of the collection in your views
$scope.deleteButtonClicked = function(item)
{
var index = $scope.myCollection.indexOf(item); //gets the item index
delete $scope.myCollection[index]; // removes the item on the array
item.$destroy(); // destroys the original items
}
Please tell more about your usecase. One of the solutions of tracking element persistance is using ngRepeat directive with custom directive that listening element's $destroy event:
<div ng-repeat="item in items" on-delete="doSomething(item)">
angular.module("app").directive("onDelete", function() {
return {
link: function (scope, element, attrs) {
element.on("$destroy", function () {
scope.$eval(attrs.onDelete);
});
}
}
});
Perhaps the solution is to create the collection class ( like backbone does ) and you can hook into events pretty easily as well.
The solution I have done here isnt really comprehensive, but should give you a general guidance on how this could be done perhaps.
http://beta.plnkr.co/edit/dGJFDhf9p5KJqeUfcTys?p=preview