简体   繁体   中英

About a sort function for javascript

Background:

As needed in some task, I need a simple sort function. For simplicity, I wrote another function to wrap the built-in sort function as:

function sortBy(obj, extra, func){
    if(typeof func == 'function'){
        f = func;
    } else if(typeof extra != 'function'){
        eval('function f(a, b, ai, bi, e){return ' + func + '}');
    } else {
        var f = extra;
        extra = null;
    }

    var res = [];
    for(var i in obj){
        if(obj.hasOwnProperty(i)){
            obj[i]._k_ = i;
            res.push(obj[i]);
        }
    }

    res.sort(function(a, b){
        if(f(a, b, a._k_, b._k_, extra)){
            return 1;
        } else {
            return -1;
        }
    })

    return res;
}

My attempts are:

  1. Make it possible to sort a object directly
  2. Keep the original object as the hash table
  3. Allow some simple syntax

For instance,

var data ={
    12: {age:27, name:'pop', role: 'Programmer'},
    32: {age:25, name:'james', role: 'Accontant'},
    123:{age:19, name:'jerry', role:'Sales Representative'},
    15:{age:22, name:'jerry', role:'Coder'},
    17:{age:19, name:'jerry', role:'Tester'},
    43:{age:14, name:'anna', role: 'Manager'},
    55: {age:31, name:'luke', role:'Analyst'}
};

There are several usages:

var b = sortBy(data, '', 'a.age < b.age'); // a simple sort, order by age
var b = sortBy(data, 19, 'b.age == e');    // pick up all records of age 19, and put them in the beginning
var b = sortBy(data, function(a, b){return a.name > b.name});  // anonymous sort function is also allowed

QUESTION

Though it works as expected in our code, I would like to raise some question:

  1. Is there any potiential problem about using eval to create sort function from string?
  2. Is there any story about sort function returning -1(nagative), 0 and 1(positive)? Can we change the code as "return if(f(a, b, a. k , b. k , extra)", instead of returning 1 or -1? We found it works in our firefox and chrome, but not sure whether it is safe to do so.

1. Is there any potiential problem about using eval to create sort function from string?

Not per se, but it does suffer all the same deficiencies as calling other eval-style functions with strings, eg setTimeout() or the Function() constructor. As long as you trust the source of the string there's no real problem. However, I would consider using the Function constructor instead:

f = new Function(a, b, ai, bi, e, 'return ' + func);

It's more manageable and it's definitely more appropriate than evaluating a function declaration.

2. Is there any story about sort function returning -1(nagative), 0 and 1(positive)?

Not really understanding this part of your question, but your function doesn't appear to tackle what to do if two items are the same from the comparison. You should be returning less than 0, 0 or more than 0 depending on the result. The best approach for this is to use String.prototype.localeCompare() :

return String.prototype.localeCompare.call(a, b);

Try making it so you only do eval on a small part:

(fiddle: http://jsfiddle.net/maniator/SpJbN/ )

function sortBy(obj, extra, func){
    var f = null;
    if(typeof func == 'function'){
        f = func;
    } else if(typeof extra != 'function'){
        f = function(a, b, ai, bi, e){
            return eval(func); // <-- smaller part
        }
    } else {
        f = extra;
        extra = null;
    }

    var res = [];
    for(var i in obj){
        if(obj.hasOwnProperty(i)){
            obj[i]._k_ = i;
            res.push(obj[i]);
        }
    }

    res.sort(function(a, b){
        if(f(a, b, a._k_, b._k_, extra)){
            return 1;
        } else {
            return -1;
        }
    })

    return res;
}
  1. Thanks to Andy, we change the code to

    var f = new Function('a, b, ai, bi, e', 'return ' + func);

Note that the arguments should be passed in as string, check out : https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function

  1. About the second question, I think it is because we were trying to make the sort function more explicitly. For instance,

    sort([1, 2, 3, 5, 1], '', 'a < b') ==> [1, 1, 2, 3, 5]

Literal meaning of 'a < b' to us is "the latter item is greater than the former item", so the array is sorted to [1, 1, 2, 3, 5].

Another example, 'a.age < b.age' will return the records in the order that younger person comes before olders.

That's the reason I am asking can we use true or false instead of -1, 0, 1.

We keep doing some more small tests, and figure out something, would like to share with everyone.

For example:

var b = [
    {age:27, name:'pop 2', role: 'Programmer'},
    {age:19, name:'james', role: 'Accontant'},
    {age:19, name:'jerry', role:'Sales Representative'},
    {age:22, name:'jerry', role:'Coder'},
    {age:19, name:'jerry', role:'Tester'},
    {age:14, name:'anna', role: 'Manager'},
    {age:19, name:'luke', role:'Analyst'},
    {age:27, name:'pop', role: 'Programmer'},
    {age:14, name:'anna 2', role: 'Manager'}
];

b.sort(function(a, b) {return a.age - b.age > 0? 1: -1}); // #1
b.sort(function(a, b) {return a.age > b.age});  // #2
b.sort(function(a, b) {return a.age - b.age});  // #3

Although the above sorts return the same result, try this one :

b.sort(function(a, b) {return a.age - b.age < 0? -1: 1});  // #4

In this statement, the records are still ordered by age, but the order is reversed within a same age group.

#1 and #2 is same as #3 by chance. If the browser use different algorithm to implement the sort function, it is possible that #1 and #2 will behave like #4. If you are strict with the order of the result, you need to explicitly return 0, when 'a' equals to item 'b'.

Besides, as Andy pointed out, in certain cases(eg, #4), it is possible that unnecessary swaps are done if we don't return 0 explicitly, which could affect the performance.

The reason we didn't notice this before is because we didn't care the order within a group, providing the record is sorted on certain property.

I think it would be appropriate to pass a function that does the comparing. so something like

sort(somedata, function(a, b) {
    return a < b;
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM