1

I have a large-ish dataset (from 400 - 4,000 objects stored in an array), and I'm trying to filter them by a user-selected field.

Right now I'm using this function, found on another SO question:

var sort = function (prop, arr) {
    prop = prop.split('.');
    var len = prop.length;

    arr.sort(function (a, b) {
        var i = 0;
        while( i < len ) {
            a = a[prop[i]];
            b = b[prop[i]];
            i++;
        }
        if (a < b) {
            return -1;
        } else if (a > b) {
            return 1;
        } else {
            return 0;
        }
    });
    return arr;
};

Sample data - I want to sort the objects by the friends count:

var data = [
    {
        name: 'Jim',
        friends: {
            count: 20,
            url: 'http://foo.com'
        }
    },{
        name: 'Lucy',
    },{
        name: 'Phil',
        friends: {
            count: 450,
            url: 'http://moo.com'
        }
    }
];

Notice how "Lucy" doesn't have a friends object - so when I run sort('friends.count', data);, the script breaks.

Ideally I'd like the objects which don't have the property that I'm sorting by to be put at the end of the array. Any ideas on how this can be achieved?

1
  • The sample array is already sorted, right? Commented Mar 15, 2015 at 8:12

2 Answers 2

3

For example,

var data = [
    {
        name: 'Jim',
        friends: {
            count: 20,
            url: 'http://foo.com'
        }
    },{
        name: 'Lucy',
    },{
        name: 'Phil',
        friends: {
            count: 450,
            url: 'http://moo.com'
        }
    }
];

safeGet = function(obj, prop, defaultValue) {
  try {
    return obj[prop]
  } catch(e) {
    return defaultValue
  }
}


data.sort(function(x, y) {
  return (
    safeGet(x.friends, 'count', Infinity) - 
    safeGet(y.friends, 'count', Infinity));
});

document.write("<pre>" + JSON.stringify(data,0,3));

If the whole property chain (friends.count) is dynamic, change safeGet so that it iterates the list of props:

var data = [
    {
        name: 'Jim',
        friends: {
            count: 20,
            url: 'http://foo.com'
        }
    },{
        name: 'Lucy',
    },{
        name: 'Phil',
        friends: {
            count: 450,
            url: 'http://moo.com'
        }
    }
];

safeGet = function(obj, props, defaultValue) {
  try {
    return props.split('.').reduce(function(obj, p) {
      return obj[p];
    }, obj);
  } catch(e) {
    return defaultValue
  }
}


data.sort(function(x, y) {
  return (
    safeGet(x, 'friends.count', Infinity) - 
    safeGet(y, 'friends.count', Infinity));
});

document.write("<pre>" + JSON.stringify(data,0,3));

If you want people with no friends to go first, not last, change Infinity to -Infinity.

Sign up to request clarification or add additional context in comments.

2 Comments

@zaynetro: it's cleaner this way.
if(obj && obj.hasOwnProperty(prop)) can work. Try-catch causes performance issues
1

Your function can be modified to check for the existence of a property:

var sort = function (prop, arr) {
    prop = prop.split('.');
    var len = prop.length;

    arr.sort(function (a, b) {
        var i = 0;
        var key;

        while( i < len ) {
            key = prop[i];

            if(!a.hasOwnProperty(key)) return 1;
            if(!b.hasOwnProperty(key)) return -1;

            a = a[key];
            b = b[key];
            i++;
        }
        if (a < b) {
            return -1;
        } else if (a > b) {
            return 1;
        } else {
            return 0;
        }
    });
    return arr;
};

This way it will be working. I made a jsbin for the example.

@georg's answer wouldn't work with property selected dynamically.

2 Comments

This one worked, except when I tried to sort descending it put "Lucy" first. I changed if(!a.hasOwnProperty(key)) to return -1 and it worked - I'm assuming this is correct, as I want any object which doesn't have the property to go to the end of the array.
True, you mentioned that elements without the property should be put in the end

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.