title: Javascript sort array of objects using nested attributes where some attributes are undefined
body: <p>I'm trying to implement a sorting function that takes in an array of objects and sorts them by an attribute or a nested attribute. I've looked at a ton of questions on here with good answers to this problem, but so far as I have seen, none of them account for when a nested attribute is missing.</p>
  
  <p>For example, if I sort by <code>some_attribute</code> and <code>some_attribute = undefined</code> for one of my array elements, the element in question is bumped to the end/beginning of the array, as I would expect. If I sort by <code>some_attribute.something_else</code>, the functions I've seen don't respond well when <code>some_attribute.something_else = undefined</code>.</p>
  
  <p>Here is a sample of my data structure I'm trying to sort:</p>
  
  <pre><code>results = [
      {
          id: 233,
          post: "Test 944 AM http://frmply.co/1mA9G3L",
          twitter_favorite_count: 0,
          twitter_retweet_count: 2,
          twitter_link_click_count: {
              minute: null,
              hour: null,
              day: null,
              week: null,
              month: null,
              total: 3
          },
          twitter_last_updated: "2015-12-28T21:11:27.425Z",
          facebook_like_count: null,
          facebook_share_count: 1,
          facebook_comment_count: null,
          facebook_link_click_count: {
              minute: null,
              hour: null,
              day: null,
              week: null,
              month: null,
              total: 1
          },
          facebook_last_updated: "2015-12-28T21:11:29.232Z",
          linkedin_like_count: null,
          linkedin_comment_count: null,
          linkedin_link_click_count: {
              minute: null,
              hour: null,
              day: null,
              week: null,
              month: null,
              total: 0
          },
          linkedin_last_updated: "2015-12-28T21:11:29.905Z"
      },
      {
          id: 232,
          post: "Test 944 AM http://frmply.co/1mA9G3L",
          twitter_favorite_count: null,
          twitter_retweet_count: null,
          twitter_link_click_count: {
              minute: null,
              hour: null,
              day: null,
              week: null,
              month: null,
              total: 5
          },
          twitter_last_updated: null,
          facebook_like_count: null,
          facebook_share_count: null,
          facebook_comment_count: null,
          facebook_link_click_count: { },
          facebook_last_updated: null,
          linkedin_like_count: null,
          linkedin_comment_count: null,
          linkedin_link_click_count: { },
          linkedin_last_updated: null
      },
      {
          id: 234,
          post: "localhost test 1106am http://frmply.co/1RAYalE",
          twitter_favorite_count: 0,
          twitter_retweet_count: 0,
          twitter_link_click_count: {
              minute: null,
              hour: null,
              day: null,
              week: null,
              month: null,
              total: 2
          },
          twitter_last_updated: "2016-01-06T18:40:21.388Z",
          facebook_like_count: null,
          facebook_share_count: null,
          facebook_comment_count: null,
          facebook_link_click_count: {
              minute: null,
              hour: null,
              day: null,
              week: null,
              month: null,
              total: 0
          },
          facebook_last_updated: "2015-12-29T16:07:39.042Z",
          linkedin_like_count: null,
          linkedin_comment_count: null,
          linkedin_link_click_count: {
              minute: null,
              hour: null,
              day: null,
              week: null,
              month: null,
              total: 0
          },
          linkedin_last_updated: "2015-12-29T16:07:39.489Z"
      },
      {
          id: 231,
          post: "test",
          twitter_favorite_count: null,
          twitter_retweet_count: null,
          twitter_link_click_count: { },
          twitter_last_updated: null,
          facebook_like_count: null,
          facebook_share_count: null,
          facebook_comment_count: null,
          facebook_link_click_count: { },
          facebook_last_updated: null,
          linkedin_like_count: null,
          linkedin_comment_count: null,
          linkedin_link_click_count: { },
          linkedin_last_updated: null
      }
  ]
  </code></pre>
  
  <p>Now if I run the code below I get <code>[2, 3, 5, undefined]</code> as a result when I would expect <code>[undefined, 2, 3, 5]</code>.</p>
  
  <pre><code>results.sortBy('twitter_link_click_count.total').map(function(x){return x.twitter_link_click_count.total})
  </code></pre>
  
  <p>In the snippet above, <code>sortBy</code> is a function from <a href="https://stackoverflow.com/a/31106445/4139179">this post</a>, however I've also tried using Underscore's <code>_.sortBy</code> function with identical results.</p>
  
  <p><strong>Working Code</strong></p>
  
  <p>Here is the solution I came up with based off Oleg's answer:</p>
  
  <pre><code>var sortKey = attribute.split('.');
  var sortBy = function(results, sortKey) {
      return _.sortBy(results, function(item) {
          if(sortKey.length === 1) {
              return item[sortKey[0]] || 0;
          } else {
              return item[sortKey[0]][sortKey[1]] || 0;
          };
      });
  };
  </code></pre>
  
  <p>The key difference here is that it generates a <code>sortKey</code> array, which can be used an an if/else statement or iterated over. I used an if/else since my <code>sortKey</code> is only ever 1 or 2 layers deep inside my object. I also handled cases where my attribute is <code>null</code> by returning <code>0</code> instead of <code>true</code>. Hope this helps someone else.</p>