3

I have the following array:

        [{"cod_nivel":"INC2","cod_modelo":"D"},   
        {"cod_nivel":"PRIM1","cod_modelo":"B"},   
        {"cod_nivel":"INC2","cod_modelo":"B"},  
        {"cod_nivel":"INC1","cod_modelo":"D"},
        {"cod_nivel":"PRIM1","cod_modelo":"D"},
        {"cod_nivel":"BAC2","cod_modelo":"B"},    
        {"cod_nivel":"BAC2","cod_modelo":"D"},    
        {"cod_nivel":"BAC2","cod_modelo":"A"}]

I need to order this array of objects by "cod_modelo" ascending grouped by "cod_nivel". So the result should be:

    [{"cod_nivel":"INC1","cod_modelo":"D"},    
    {"cod_nivel":"INC2","cod_modelo":"B"},
    {"cod_nivel":"INC2","cod_modelo":"D"},    
    {"cod_nivel":"PRIM1","cod_modelo":"B"},    
    {"cod_nivel":"PRIM1","cod_modelo":"D"},
    {"cod_nivel":"BAC2","cod_modelo":"A"},    
    {"cod_nivel":"BAC2","cod_modelo":"B"},    
    {"cod_nivel":"BAC2","cod_modelo":"D"}]

Ok I made this code that orders the array first by cod_nivel and then by cod_modelo:

var sortedArray = array.sort(function (a, b) {
    return (a["cod_nivel"] > b["cod_nivel"]) ? 1 : -1;
}).sort(function (a, b) {
    if (a["cod_nivel"] == b["cod_nivel"])
        return (a["cod_modelo"] > b["cod_modelo"]) ? 1 : -1;
    else
        return 0;
});

The thing is that this code is ordering also by "cod_nivel" so the obtained array would be:

    [{"cod_nivel":"BAC2","cod_modelo":"A"},    
    {"cod_nivel":"BAC2","cod_modelo":"B"},    
    {"cod_nivel":"BAC2","cod_modelo":"D"},
    {"cod_nivel":"INC1","cod_modelo":"D"},    
    {"cod_nivel":"INC2","cod_modelo":"B"},
    {"cod_nivel":"INC2","cod_modelo":"D"},    
    {"cod_nivel":"PRIM1","cod_modelo":"B"},    
    {"cod_nivel":"PRIM1","cod_modelo":"D"}]

Note that BAC2 cod_nivel objects are at the beginning.

What I need is to order first by cod_nivel but in a given order, which is:

  1. INC1
  2. INC2
  3. PRIM1
  4. PRIM2
  5. BAC1
  6. BAC2

I assume I need an array with the fixed order of "cod_nivel" and use it while ordring but I don't know how to use it so I did not include in my approach.

var order_arr = ['INC1', 'INC2', 'PRIM1', 'PRIM2', 'BAC1', 'BAC2']

And after that, order then by cod_modelo (grouped by each cod_nivel).

I hope I have explained myself clearly and someone can help me.

isherwood
  • 58,414
  • 16
  • 114
  • 157
Egidi
  • 1,736
  • 8
  • 43
  • 69
  • possible duplicate of [Javascript sort function. Sort by First then by Second](http://stackoverflow.com/questions/9175268/javascript-sort-function-sort-by-first-then-by-second) – Liam Apr 13 '15 at 09:20
  • Put your sorting code in one callback: First compare the `code_nivel` properties and just, if those are the same sort by `cod_modelo`. – Sirko Apr 13 '15 at 09:20
  • Did you try http://underscorejs.org/ , it's really easy to do such stuff with Underscore.js – Alaa-GI Apr 13 '15 at 09:21
  • Possible duplicate of [Sort array of objects in specific order](http://stackoverflow.com/questions/23598610/sort-array-of-objects-in-specific-order) – Gajus Nov 20 '15 at 14:40

2 Answers2

10

You can assign indexes in an associative array, like this

var cod_nivel_order = {
    'INC1': 0,
    'INC2': 1,
    'PRIM1': 2,
    'PRIM2': 3,
    'BAC1': 4,
    'BAC2': 5
};

and then you can sort like this

function compare(a, b) {
    if (a === b) {
        return 0;
    }
    return a < b ? -1 : 1;
}

var sortedArray = array.sort(function (a, b) {

    // First compare corresponding values of `cod_nivel` from `cod_nivel_order`
    var index_result = compare(cod_nivel_order[a.cod_nivel],
        cod_nivel_order[b.cod_nivel]);

    // If they are equal
    if (index_result === 0) {

        // Return the result of comparing `cod_modelo`s
        return compare(a.cod_modelo, b.cod_modelo);
    }

    return index_result;
});

and now the result will be

[ { cod_nivel: 'INC1', cod_modelo: 'D' },
  { cod_nivel: 'INC2', cod_modelo: 'B' },
  { cod_nivel: 'INC2', cod_modelo: 'D' },
  { cod_nivel: 'PRIM1', cod_modelo: 'B' },
  { cod_nivel: 'PRIM1', cod_modelo: 'D' },
  { cod_nivel: 'BAC2', cod_modelo: 'A' },
  { cod_nivel: 'BAC2', cod_modelo: 'B' },
  { cod_nivel: 'BAC2', cod_modelo: 'D' } ]
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
  • but this is not ordering the cod_modelos specifically with the order I need, isn't it? 1. INC1 2. INC2 3. PRIM1 4. PRIM2 5. BAC1 6. BAC2 – Egidi Apr 13 '15 at 09:30
  • 1
    @Egidi That is exactly the point. You don't have to sort it twice (first by `cod_modelo` and then by `cod_nivel`). It will give you the expected result. I explained the rules of comparison also. Please check. – thefourtheye Apr 13 '15 at 09:31
  • I am testing it and it is not giving the nedeed result. I can understand that i don't need to sort it twice, but as told, I need to be in the array first the objects "cod_nivel"='INC1' then 'INC2' objects then 'PRIM1'...with your code is ordering 'BAC1' objects at the beginning of the array because it is ordering by cod_nivel ascending and BAC1 is before INC1... am i wrong? – Egidi Apr 13 '15 at 09:38
  • @Egidi I don't see a pattern in your sorting. When you sort based on `cod_modelo` first and then `cod_nivel` then, why `D` appears after `B` in the expected result? – thefourtheye Apr 13 '15 at 09:40
  • Yes, the cod_modelo has to be sorted ascending (a,b,c,d,e,f,g...) but the cod_nivel has to be sorted in a specific way ( 1. INC1 2. INC2 3. PRIM1 4. PRIM2 5. BAC1 6. BAC2)...note that it is not ascening...it's a fixed order. Maybe I haven't explained myself well...I appreciate a lot your help by the way. – Egidi Apr 13 '15 at 09:44
  • I assume I need an array with the fixed order of "cod_nivel" and use it while ordring but I don't know how to use it so I did not include in my approach. – Egidi Apr 13 '15 at 09:46
  • @Egidi Ah, I get it now. Give me a moment, I ll fix it. – thefourtheye Apr 13 '15 at 09:46
  • Ok, I edited my explanation, hope it is clearer now. – Egidi Apr 13 '15 at 09:48
  • @Egidi You have `PRIM` in the input? Are you sure its not `PRIM1` or `PRIM2`? – thefourtheye Apr 13 '15 at 09:55
  • you are right should be PRIM1 or PRIM2, I'll fix it. – Egidi Apr 13 '15 at 09:57
  • @Egidi Okay, I fixed it now. Please check. – thefourtheye Apr 13 '15 at 10:00
3
var data = [{
    "cod_nivel": "INC2",
    "cod_modelo": "B"
}, {
    "cod_nivel": "INC2",
    "cod_modelo": "D"
}, {
    "cod_nivel": "INC2",
    "cod_modelo": "B"
}, {
    "cod_nivel": "PRIM1",
    "cod_modelo": "B"
}, {
    "cod_nivel": "INC2",
    "cod_modelo": "B"
}, {
    "cod_nivel": "INC1",
    "cod_modelo": "D"
}, {
    "cod_nivel": "INC2",
    "cod_modelo": "B"
}, {
    "cod_nivel": "PRIM2",
    "cod_modelo": "D"
}, {
    "cod_nivel": "BAC2",
    "cod_modelo": "B"
}, {
    "cod_nivel": "BAC2",
    "cod_modelo": "D"
}, {
    "cod_nivel": "BAC2",
    "cod_modelo": "A"
}];
var sortOrder = ['INC1', 'INC2', 'PRIM1', 'PRIM2', 'BAC1', 'BAC2'];
data.sort(function (a, b) {
    // first have a look at cod_nivel
    // get the indices of the given sortOrder
    // take the difference of the indices
    var delta = sortOrder.indexOf(a.cod_nivel) - sortOrder.indexOf(b.cod_nivel);
    // test, if the indices are the same
    if (delta === 0) {
        // the comparison should usually return 0,
        // but this will here not work. we need
        // the comparison of the second sort order of cod_modelo.
        // take the variables and make a real comparison,
        // while we expecting strings to be compared
        return a.cod_modelo === b.cod_modelo ? 0 : (a.cod_modelo < b.cod_modelo ? -1 : 1);
    }
    // otherwise return the delta
    return delta;
});

as thefortheye mentioned, the former version does not compute very well. so here is the type safe version for number/number or string/string comparison.

Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Could do with some explanation of what your doing and why. – Liam Apr 13 '15 at 10:59
  • **Note:** This will **NOT** work in all environments as JavaScript's sort is not guaranteed to be stable in all the environments. It is left to the implementations. – thefourtheye Apr 13 '15 at 11:34