Daniel Bonnell
Daniel Bonnell

Reputation: 4997

Javascript sort array of objects using nested attributes where some attributes are undefined

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.

For example, if I sort by some_attribute and some_attribute = undefined 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 some_attribute.something_else, the functions I've seen don't respond well when some_attribute.something_else = undefined.

Here is a sample of my data structure I'm trying to sort:

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
    }
]

Now if I run the code below I get [2, 3, 5, undefined] as a result when I would expect [undefined, 2, 3, 5].

results.sortBy('twitter_link_click_count.total').map(function(x){return x.twitter_link_click_count.total})

In the snippet above, sortBy is a function from this post, however I've also tried using Underscore's _.sortBy function with identical results.

Working Code

Here is the solution I came up with based off Oleg's answer:

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;
        };
    });
};

The key difference here is that it generates a sortKey array, which can be used an an if/else statement or iterated over. I used an if/else since my sortKey is only ever 1 or 2 layers deep inside my object. I also handled cases where my attribute is null by returning 0 instead of true. Hope this helps someone else.

Upvotes: 0

Views: 97

Answers (2)

Jack
Jack

Reputation: 1941

Solution using plain JS filter & map

results = results.filter(function (a) {
    return !!((a.twitter_link_click_count || {}).total)
}).sort(function(a, b){
    return (a.twitter_link_click_count || {}).total > (b.twitter_link_click_count || {}).total
})

Upvotes: 0

oleh.meleshko
oleh.meleshko

Reputation: 4795

Please try the following script (used Underscore there):

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
    }
];

document.getElementById('output').innerHTML = JSON.stringify(_.sortBy(results, function(item) {
  return item.twitter_link_click_count.total || true;  
}).map(function(x){return x.twitter_link_click_count.total}));
<script src="http://underscorejs.org/underscore-min.js"></script>
<pre id="output"></pre>

Upvotes: 1

Related Questions