0

I am facing some strange behavior when I try to delete an object from an array.

Please have a look at code. I am using recursive function.

var obj = {
    'id': '234567869',
    'name': 'Lao Lao',
    'title': 'general manager',
    'children': [{
        'id': '467876756634',
        'name': 'Bo Miao',
        'title': 'department manager'
    }, {
        'id': '2345666078',
        'name': 'Su Miao',
        'title': 'department manager',
        'children': [{
            'id': '898735342',
            'name': 'Tie Hua',
            'title': 'senior engineer'
        }, {
            'id': '7697347548',
            'name': 'Hei Hei',
            'title': 'senior engineer',
            'children': [{
                'id': '123415640',
                'name': 'Pang Pang',
                'title': 'engineer'
            }, {
                'id': '1237450976',
                'name': 'Xiang Xiang',
                'title': 'UE engineer'
            }]
        }]
    }, {
        'id': '6968756535',
        'name': 'Yu Jie',
        'title': 'department manager'
    }, {
        'id': '236448654',
        'name': 'Chun Miao',
        'title': 'department manager'
    }, {
        'id': '356898765',
        'name': 'Yu Tie',
        'title': 'department manager'
    }]
};

function deleteNode(idToFind, bigObjectToSearch) {
    var i, currentChild, result;
    if (idToFind == bigObjectToSearch.id) {
        //I try to delete the object here but can't :(
        delete bigObjectToSearch;
        return true;
    } else if (bigObjectToSearch.children) {
        for (i = 0; i < bigObjectToSearch.children.length; i += 1) {
            currentChild = bigObjectToSearch.children[i];
            // Search in the current child
            if(deleteNode(idToFind, currentChild)){
                break;
            };
        }
        return false;
    }
    return false; 
}

deleteNode('236448654', obj);

console.log(obj);

function deleteNodeFromArray(idToFind, bigObjectToSearch) {
    var i, currentChild, result;
    if (idToFind == bigObjectToSearch.id) {
        //I try to delete the object here but can't :(
        //delete bigObjectToSearch;
        return true;
    } else if (bigObjectToSearch.children) {
        for (i = 0; i < bigObjectToSearch.children.length; i += 1) {
            currentChild = bigObjectToSearch.children[i];
            // Search in the current child
            if(deleteNodeFromArray(idToFind, currentChild)){
                //If I delete from here, it works.
                delete bigObjectToSearch.children[i];
                break;
            };
        }
        return false;
    }
    return false; 
}

deleteNodeFromArray('236448654', obj);

console.log(obj)

In first function, it doesn't delete the object with id="236448654".

In second function, it deletes the object. In both function code is same but just delete statement has different location.

Can any one please explain me this behavior ?

Thank you.

jasmin_makasana
  • 472
  • 3
  • 11

3 Answers3

1

Talking about the issue in you code, first I have to say that using delete is not quite good since it's has negative effects for the V8 classes

Here's a sample example how delete works :

// 
var keyToDelete = "key1";
var myObj = {
  "test": {
    "key1": "value",
    "key2": "value"
  }
}
delete myObj.test[keyToDelete];
console.log(myObj)

As you see it require the key of the object that will be deleted with is not the case for you since your object is has no keys

For this I had to edit your object from array of objects to objects of objects since I cant set the keys inside the array so that we can use the delete operator, I had also to delete the deleteNodeFromArray function since it has no use anymore without an array.

Here's your solution with the necessary corrections and optimizations :

//
var obj = {
  "234567869": {
    'id': '234567869',
    'name': 'Lao Lao',
    'title': 'general manager',
    'children': {
      "467876756634": {
        'id': '467876756634',
        'name': 'Bo Miao',
        'title': 'department manager'
      },
      "2345666078": {
        'id': '2345666078',
        'name': 'Su Miao',
        'title': 'department manager',
        'children': {
          "898735342": {
            'id': '898735342',
            'name': 'Tie Hua',
            'title': 'senior engineer'
          },
          "7697347548": {
            'id': '7697347548',
            'name': 'Hei Hei',
            'title': 'senior engineer',
            'children': {
              "123415640": {
                'id': '123415640',
                'name': 'Pang Pang',
                'title': 'engineer'
              },
              "1237450976": {
                'id': '1237450976',
                'name': 'Xiang Xiang',
                'title': 'UE engineer'
              }
            }
          }
        }
      },
      "6968756535": {
        'id': '6968756535',
        'name': 'Yu Jie',
        'title': 'department manager'
      },
      "236448654": {
        'id': '236448654',
        'name': 'Chun Miao',
        'title': 'department manager'
      },
      "356898765": {
        'id': '356898765',
        'name': 'Yu Tie',
        'title': 'department manager'
      }
    }
  }
};

function deleteNode(idToFind, bigObjectToSearch) {
  var i, currentChild, result;
  for (var key in bigObjectToSearch) {

    if (idToFind == bigObjectToSearch[key].id) {

      delete bigObjectToSearch[key];

    } else if (bigObjectToSearch[key].children) {

      deleteNode(idToFind, bigObjectToSearch[key].children)

    }

  }

}

deleteNode('7697347548', obj);
console.log(obj)

And this is my old (working) solution from the first edit is which I used the filter function so I can generate new object that satisfy my needs:

//i changed the type of obj to array to it will be more easier to run the recursion
var obj = [{
  'id': '234567869',
  'name': 'Lao Lao',
  'title': 'general manager',
  'children': [{
    'id': '467876756634',
    'name': 'Bo Miao',
    'title': 'department manager'
  }, {
    'id': '2345666078',
    'name': 'Su Miao',
    'title': 'department manager',
    'children': [{
      'id': '898735342',
      'name': 'Tie Hua',
      'title': 'senior engineer'
    }, {
      'id': '7697347548',
      'name': 'Hei Hei',
      'title': 'senior engineer',
      'children': [{
        'id': '123415640',
        'name': 'Pang Pang',
        'title': 'engineer'
      }, {
        'id': '1237450976',
        'name': 'Xiang Xiang',
        'title': 'UE engineer'
      }]
    }]
  }, {
    'id': '6968756535',
    'name': 'Yu Jie',
    'title': 'department manager'
  }, {
    'id': '236448654',
    'name': 'Chun Miao',
    'title': 'department manager'
  }, {
    'id': '356898765',
    'name': 'Yu Tie',
    'title': 'department manager'
  }]
}];

// the recursive funtion that will call itself
function looperRec(obj, id) {

  //delete the object that has the passed id from the array
  var obj = obj.filter(function(item) {
    return item.id !== id;
  });
  //loop the array in order to find the children
  for (i in obj) {
    // if children exist call the function again
    if (obj[i].hasOwnProperty('children')) {

      //replace the old data with the new one only when the attribute children exist
      obj[i].children = looperRec(obj[i].children, id)
    }

  }
  //return the result of each step in the recursion
  return obj;
}

//display the result
console.log(looperRec(obj, "898735342"));
Neji Soltani
  • 1,522
  • 4
  • 22
  • 41
1

pure recursion

delete is very misunderstood, but because it produces a side effect (imperative style), using it with recursion (function style) causes a bit of a clash.

If you write a pure function, the original data is not mutated, and instead the "next" state of the data is returned. The primary advantage here is that you don't have to concern yourself with side effects. This naturally encourages a recursive pattern where a node's children are the result of the removeNode called on each child.

const identity = x =>
  x

const removeNode = (match, { id, name, title, children = [] }) =>
  match === id
    ? null
    : { id : id
      , name : name
      , title : title
      , children : children.map (n => removeNode (match, n)) .filter (identity)
      }

Here's a full working demo

const identity = x =>
  x
  
const removeNode = (match, { id, name, title, children = [] }) =>
  match === id
    ? null
    : { id : id
      , name : name
      , title : title
      , children : children.map (n => removeNode (match, n)) .filter (identity)
      }

const node =
    { id : "234567869"
    , name : "Lao Lao"
    , title : "general manager"
    , children :
        [ { id : "467876756634"
          , name : "Bo Miao"
          , title : "department manager"
          }
        , { id : "2345666078"
          , name : "Su Miao"
          , title : "department manager"
          , children :
                [ { id : "898735342"
                  , name : "Tie Hua"
                  , title : "senior engineer"
                  }
                , { id : "7697347548"
                  , name : "Hei Hei"
                  , title : "senior engineer"
                  , children :
                        [ { id : "123415640"
                          , name : "Pang Pang"
                          , title : "engineer"
                          }
                        , { id : "1237450976"
                          , name : "Xiang Xiang"
                          , title : "UE engineer"
                          }
                        ]
                  }
                ]
          }
        , { id : "6968756535"
          , name : "Yu Jie"
          , title : "department manager"
          }
        , { id : "236448654"
          , name : "Chun Miao"
          , title : "department manager"
          }
        , { id : "356898765"
          , name : "Yu Tie"
          , title : "department manager"
          }
        ]
    }
      
console.log (removeNode ("236448654", node))
Mulan
  • 129,518
  • 31
  • 228
  • 259
0

It's about garbage collector. You cannot remove any object in JS, all what you can - it is remove reference to object at some place. When you invoke delete bigObjectToSearch; or delete bigObjectToSearch.children[i]; it don't delete object, it try to delete reference to that object - and don't touch object itself.

Also, in first function (here delete bigObjectToSearch;) you tried to delete entire object, while 'delete' work only with object properties, so you just can't

In second function you successfully removed object property, which in fact not object itself, just link to them. So, in fact "deleted" object still exists. And it will exist until some link to them exists, and if no - it would be garbage collected at some moment.

In JS you cannot force delete any object, you must to control all referenced to do not create memory leaks.

  • I agree with you about 'delete' removes the reference. But I have a query. if bigObjectToSearch is not reference of bigObjectToSearch.children[i], then if I make any change in bigObjectToSearch, then it also reflects to bigObjectToSearch.children[i]. I am confused here :( – jasmin_makasana Jan 24 '18 at 10:51
  • At first - they are both references. At second, you cannot use 'delete' for object itself, only for properties – Yurii Kovalenko Jan 24 '18 at 11:00