Stiger
Stiger

Reputation: 1199

Multi-sorting in underscore

I have Array object ArrObject

object = {
    Active: true, // type: boolean
    Code: '12345'   // type: string
}

I want to sort this array by Active field then Code. I need help to do that with underscoreJs.

UPDATE

My data:

data = [
{
    Code: "Code0",
    Description: "Description0",
    IsActive: true,
    id: 0
},
{
    Code: "Code1",
    Description: "Description1_edit",
    IsActive: true,
    id: 1
},
{
    Code: "Code5",
    Description: "Description5_edit",
    IsActive: false,
    id: 2
}]

Upvotes: 0

Views: 4840

Answers (4)

mu is too short
mu is too short

Reputation: 434665

I'd just use Array#sort with a simple comparator function:

function cmp_bool(a, b) {
    return a == b ?  0
         : a      ? -1
         :          +1
}
function cmp_str(a, b) {
    return a == b ?  0
         : a <  b ? -1
         :          +1
}
function cmp(a, b) {
    return cmp_bool(a.IsActive, b.IsActive)
        || cmp_str( a.Code,     b.Code);
}

Then you could simply do this:

var sorted = data.sort(cmp);

If you need to be able to switch the Code sort order then you just need a reversed version of cmp_str (say cmp_str_reverse) and a version of cmp that uses cmp_str_reverse instead of cmp_str.

If you must use _.sortBy then you just need to come up with a value to sort by, something like this:

function combined(obj) {
    return (obj.IsActive ? 'a' : 'b')
         +  obj.Code;
}
var sorted = _(data).sortBy(combined);

The problem with this is that it is much harder to reverse the Code ordering. I suppose you could do a big mess of bit twiddling on the string's characters but that would just leave you wonder just what you were doing when you look at the code in six months. _.sortBy is a notational convenience, you don't have to force everything to fit whatever conveniences you have on hand.

Demo: https://jsfiddle.net/ambiguous/6tfcQ/9/

Upvotes: 3

Timothy C. Quinn
Timothy C. Quinn

Reputation: 4485

Following mu is to shorts answer, here is a utility function that can greatly simplify the use of JavaScripts Array.prototype.sort().

Comparator builder utility:

function comparator(){
    var spec=Array.from(arguments)
    return function(a, b){
        var v = 0, r
        for(var i in spec){
            r = spec[i]
            if(typeof r === 'string'){
                if(r.length>0 && r[0] === '~')
                    r = {key: r.substring(1), desc: true}
                else
                    r = {key: r}
                
            }
            if( (v=__c(r)) ) break
        }
        return v

        function __c(r){
            var va, vb, k = r.key
            if ((va=a[k]) !== (vb=b[k])){
                if(va === null || va === undefined) return r.null_top?-1:1
                if(vb === null || vb === undefined) return r.null_top?1:-1
                if (va===true) return r.desc?1:-1;
                if(va<vb) return r.desc?1:-1;
                return r.desc?-1:1;
            }
            return 0
        }
    }                
}

Usage:

var sorted = data.sort(comparator( {key: 'Code', desc: true, null_top: true}
                                 , {key: 'IsActive'}))

This will sort the data by Code (desc) and Is Active and null / undefined will be put at top (where default is bottom).

Here is a shorthand that this utility also supports:

var sorted = data.sort(comparator('~Code', 'IsActive'))

Where ~ signifies descending.

Here is a jsfiddle.

Upvotes: 0

Eduardo Portal Geroy
Eduardo Portal Geroy

Reputation: 374

A little late, but maybe someone need this answer if underscore is really needed.

data = [
{
    Code: "Code0",
    Description: "Description0",
    IsActive: true,
    id: 0
},
{
    Code: "Code1",
    Description: "Description1_edit",
    IsActive: true,
    id: 1
},
{
    Code: "Code5",
    Description: "Description5_edit",
    IsActive: false,
    id: 2
}];

var sortedData = _(data).chain()
    .sortBy('Code')
  .sortBy(function(item){
        return item.IsActive ? 0 : 1;
    });

console.log(sortedData);

Check this fiddle.

Upvotes: 1

zs2020
zs2020

Reputation: 54514

The comparator can be based on the concatenated 2 fields like this

var sorted = _.sortBy(ListObject, function (o) {
    return o.Active.toString() + '_' + o.Code;
});

(Based on "True" > "False" <=> true > false)

Upvotes: 2

Related Questions