title: Knockout mapping - Uniquely identifying nested objects using keys
body: <p><a href="http://knockoutjs.com/documentation/plugins-mapping.html" rel="nofollow">The Knockout mapping plugin documentation</a> has a section entitled "Uniquely identifying objects using “keys”". This describes how to update part of an object and then only update that part of the display rather than completely replacing the display of all properties of a partially-modified object. That all works splendidly in their simple example, which I have slightly modified here to make my question more clear. My modifications were to:</p>
  
  <ol>
  <li><p>Replace the object with a corrected name after a 2 second delay.</p></li>
  <li><p>Highlight the unchanging part of the display, so you can see that it is actually not replaced when the update happens.</p></li>
  </ol>
  
  <p><strong>1. Simple object</strong> (<a href="http://jsfiddle.net/nEaSJ/" rel="nofollow">jsFiddle</a>)</p>
  
  <pre><code>&lt;h1 data-bind="text: name"&gt;&lt;/h1&gt;
  &lt;ul data-bind="foreach: children"&gt;
      &lt;li&gt;&lt;span class="id" data-bind="text: id"&gt;&lt;/span&gt; &lt;span data-bind="text: name"&gt;&lt;/span&gt;&lt;/li&gt;
  &lt;/ul&gt;
  
  &lt;script&gt;
  var data = {
      name: 'Scot',
      children: [
          {id : 1, name : 'Alicw'}
      ]
  };
  var mapping = {
      children: {
          key: function(data) {
              console.log(data);
              return ko.utils.unwrapObservable(data.id);
          }
      }
  };
  var viewModel = ko.mapping.fromJS(data, mapping);
  ko.applyBindings(viewModel);
  
  var range = document.createRange();
  range.selectNode(document.getElementsByClassName("id")[0]);
  window.getSelection().addRange(range);
  
  setTimeout(function () {
      var data = {
          name: 'Scott',
          children: [
              {id : 1, name : 'Alice'}
          ]
      };
      ko.mapping.fromJS(data, viewModel);
  }, 2000);
  &lt;/script&gt;
  </code></pre>
  
  <p>But what isn't clear to me is how I would achieve the same behavior for a more complex nested data structure. In the following example, I took the above code and wrapped the data in a list. I would like this to behave the same as above, but it doesn't. The whole display is redone because of the change in one property. You can see this because, unlike the above example, the highlighting is lost after the data is updated.</p>
  
  <p><strong>2. More complex nested object</strong> (<a href="http://jsfiddle.net/tar4w/" rel="nofollow">jsFiddle</a>)</p>
  
  <pre><code>&lt;!-- ko foreach: parents --&gt;
      &lt;h1 data-bind="text: name"&gt;&lt;/h1&gt;
      &lt;ul data-bind="foreach: children"&gt;
          &lt;li&gt;&lt;span class="id" data-bind="text: id"&gt;&lt;/span&gt; &lt;span data-bind="text: name"&gt;&lt;/span&gt;&lt;/li&gt;
      &lt;/ul&gt;
  &lt;!-- /ko --&gt;
  
  &lt;script&gt;
  var data = {
      parents: [
          {
              name: 'Scot',
              children: [
                  {id : 1, name : 'Alicw'}
              ]
          }
      ]
  };
  var mapping = {
      children: {
          key: function(data) {
              console.log(data);
              return ko.utils.unwrapObservable(data.id);
          }
      }
  };
  var viewModel = ko.mapping.fromJS(data, mapping);
  ko.applyBindings(viewModel);
  
  var range = document.createRange();
  range.selectNode(document.getElementsByClassName("id")[0]);
  window.getSelection().addRange(range);
  
  setTimeout(function () {
      var data = {
          parents: [
              {
                  name: 'Scott',
                  children: [
                      {id : 1, name : 'Alice'}
                  ]
              }
          ]
      };
      ko.mapping.fromJS(data, viewModel);
  }, 2000);
  &lt;/script&gt;
  </code></pre>
  
  <p>So basically what I'm asking is, how can I make the second example work like the first, given the more nested data structure? You can assume that ids are unique for each child (so if I added another parent besides Scott, his children would start with id=2, etc.).</p>