1

I have a deeply nested immutable structure with maps and lists. I'm not sure about the best way to update a nested map within the structure (the node with id:356).

I create a function to search the list, I find the target map, I update it. But the structure stays intact!! any idea what I'm doing wrong?

https://jsbin.com/sadoni/1/edit?js,console

    var structure = Immutable.fromJS(
        {
            someKey:{id:1},
            links:[
                {id:123, c:false, chd:[]},
                {id:134, c:false, chd:[
                    {id:212, c:false, chd:[
                        {id:245, c:false, chd:[]},
                        {id:256, c:false, chd:[]}
                    ]},
                    {id:145, c:false, chd:[]},
                    {id:156, c:false, chd:[]},
                    {id:213, c:false, chd:[
                        {id:313, c:false, chd:[]},
                        {id:314, c:false, chd:[
                            {id:345, c:false, chd:[]},
                            {id:356, c:false, chd:[]}
                        ]}
                    ]}
                ]}

            ]
        }
    );


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _findNode
    function _findNode(nodes, func, cb){
        let found = false;
            var findInTree = (nodes, func) => {
                if(nodes && nodes.size > 0){
                    nodes.forEach(node => {
                        if(found === false){
                            if (func(node) === true) {
                                found = true;
                                cb(node, nodes);
                            } else {
                                let chd = node.get('chd');
                                if(chd && chd.size > 0 && found === false){
                                    findInTree(chd, func);
                                }
                            }
                        }
                    });
                }
            };
            findInTree(nodes, func, cb);
    }


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _filter function
function filter(link){ return link.get('id')===356; }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _find the links array inside the tree
var links = structure.get('links');

function changeTheNode(node, nodes){
    console.log(nodes.get(1).toJS()); // log nodes array before the update
   var index = nodes.findIndex(function(n){ return n === node; });
    nodes = nodes.update(index, function(itm){ return itm.set('c', true); });
    // nodes array changed
    console.log(nodes.get(1).toJS());

    // structure tree is still the same :(
    //console.log(structure.toJS());
}

_findNode(links, filter, changeTheNode);
Ashraf Fayad
  • 1,433
  • 4
  • 17
  • 31

1 Answers1

0

Recursion!

var data = Immutable.fromJS({
    id:134, c:false, chd:[
        {id:212, c:false, chd:[
            {id:245, c:false, chd:[]},
            {id:256, c:false, chd:[]}
        ]},
        {id:145, c:false, chd:[]},
        {id:156, c:false, chd:[]},
        {id:213, c:false, chd:[
            {id:313, c:false, chd:[]},
            {id:314, c:false, chd:[
                {id:345, c:false, chd:[]},
                {id:356, c:false, chd:[]}
            ]}
        ]}
    ]
});

function search(val, target, changeTheNode) {
    if (val.get('id') === target) {
        return changeTheNode(val);
    }

    return val.set('chd',
      val.get('chd')
         .map((v) => search(v, target, changeTheNode))
    );
}

var updatedData = search(data, 356, (node) => node.set('c', true))

The result is

{
  "id": 134,
  "c": false,
  "chd": [
    {
      "id": 212,
      "c": false,
      "chd": [
        {
          "id": 245,
          "c": false,
          "chd": []
        },
        {
          "id": 256,
          "c": false,
          "chd": []
        }
      ]
    },
    {
      "id": 145,
      "c": false,
      "chd": []
    },
    {
      "id": 156,
      "c": false,
      "chd": []
    },
    {
      "id": 213,
      "c": false,
      "chd": [
        {
          "id": 313,
          "c": false,
          "chd": []
        },
        {
          "id": 314,
          "c": false,
          "chd": [
            {
              "id": 345,
              "c": false,
              "chd": []
            },
            {
              "id": 356,
              "c": true,
              "chd": []
            }
          ]
        }
      ]
    }
  ]
}
Luqmaan
  • 2,052
  • 27
  • 34
  • 1
    Note that the difference between this and the original is that the changed value is being passed back up the stack. If you're using an immutable structure, you can't mutate it. You must create a new one and pass that around if you need to make changes. The original creates the new object by calling `set`, but then drops it on the floor. – John Tyree Jan 18 '16 at 01:07
  • Thanks. But this solutions always returns a new Immutable structure even if no value is changed. var updatedData = search(data, 356, (node) => node.set('c', false)); console.log(updatedData === data); // always false even when nothing changed – Ashraf Fayad Jan 18 '16 at 06:28
  • 2
    @AshrafFayad yes of course. That's the point of immutable structures. They do not mutate. If you want mutable structures do not use ImmutableJS. – John Tyree Jan 18 '16 at 17:16
  • @JohnTyree I know that immutable structures don't mutate but they create new copies of themselves and apply structural sharing to save memory. But I also know that they are smart enough not to create unnecessary duplicates if nothing changed. Please try my Jsbin code to know what I mean. Unlike your code, This line produces a new structure ONLY IF SOMETHING CHANGED var copy = immutableTree.mergeDeepIn(['links','bbb','children','bbb.bb','children','bbb.bb.3'], {active:false}); https://jsbin.com/colusa/2/edit?js,console – Ashraf Fayad Jan 18 '16 at 19:24
  • 1
    @AshrafFayad https://facebook.github.io/immutable-js/docs/#/Map/set always returns a new copy, regardless of whether or not the value has changed. If you don't want that, then check the value and only call `set()` if the new value is different. – John Tyree Jan 18 '16 at 23:29
  • The problem with @luqmann's code is not '.set' . The problem is '.map' it always returns a new list. – Ashraf Fayad Jan 19 '16 at 05:03
  • 1
    http://jlongster.com/Using-Immutable-Data-Structures-in-JavaScript#Reference---Value-Eq – Luqmaan Jan 19 '16 at 17:31
  • https://cloud.githubusercontent.com/assets/1275831/12426619/3771208c-bea0-11e5-95bf-9ecdedc01bf0.png – Luqmaan Jan 19 '16 at 17:32