r3plica
r3plica

Reputation: 13367

JavaScript sort function not working as expected

I am trying to sort an array:

var mapped = [{
    wiFi: true,
    megapixel: '20 MP'
},{
    wiFi: false,
    megapixel: '25 MP'
},{
    wiFi: true,
    megapixel: '25 MP'
},{
    wiFi: true,
    megapixel: '21 MP'
}];

I would like to sort it on both properties (wiFi first). This is a really simple version of what I am doing, but the properties can be decided by the user, so I have a function that looks a bit like this:

var fields = ['wiFi', 'megapixel'];

// Sort our mapped array
mapped.sort(function (a, b) {

    // Loop through our properties
    fields.forEach(function (field) {

        // Get our value (skip the first)
        var x = a[field.name];
        var y = b[field.name];

        // Switch on the type
        switch (typeof x) {

            // Boolean
            case "boolean":

                // Return our value
                return x === y ? 0 : x ? -1 : 1;

            // Default
            default:

                // Return our value
                return x === y ? 0 : x < y ? -1 : 1;

        }
    });
});

but my array is not being sorted at all. Does anyone know why?

Upvotes: 1

Views: 148

Answers (4)

r3plica
r3plica

Reputation: 13367

Thanks to all your suggestions, I have tried to combine them all and come up with this:

// Sort our mapped array
mapped.sort(function (a, b) {

    // Loop through our properties
    for (var i = 0; i < fields.length; i++) {

        // Get our field
        var field = fields[i];

        // Get our value (skip the first)
        var x = a[field.name];
        var y = b[field.name];

        // If our values are the same, go to the next compare
        if (x === y)
            continue;

        // Switch on the type
        switch (field.type) {

            // Boolean
            case "boolean":

                // Return our value
                return x ? -1 : 1;

            // Integer
            case "float":

                // Update our values
                x = parseFloat(x);
                y = parseFloat(y);

                // Return our value
                return x < y ? -1 : 1;

            // Default (string)
            default:

                // Return our value
                return x.localeCompare(y);
        }
    }

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386578

You can use sort with a helper object.

var mapped = [{ wiFi: true, megapixel: '20 MP' }, { wiFi: false, megapixel: '25 MP' }, { wiFi: true, megapixel: '25 MP' }, { wiFi: true, megapixel: '21 MP' }],
    fields = ['wiFi', 'megapixel'],
    types = {
        wiFi: function (o) { return +!o.wiFi; }, // if true sort top
        megapixel: function (o) { return parseFloat(o.megapixel) || 0; },
    };

mapped.sort(function (a, b) {
    var r = 0;
    fields.some(function (c) {				
        return (r = types[c](a) - types[c](b));
    });
    return r;
});

document.write('<pre>' + JSON.stringify(mapped, 0, 4) + '</pre>');

Upvotes: 0

Rajesh
Rajesh

Reputation: 24915

You can try to compute rank and based on rank, you can sort values:

logic:

  • For boolean, if true, add 10 in self and remove 10 from other.
  • For others, you can use a generic logic, add 10 in bigger value and reduce 10 from smaller value.
  • Sort based on this rank.

var mapped = [{ wiFi: true, megapixel: '20 MP'}, {  wiFi: false,  megapixel: '25 MP'}, {  wiFi: true,  megapixel: '25 MP'}, {  wiFi: true,  megapixel: '21 MP'}];

var fields = ['wiFi', 'megapixel'];

// Sort our mapped array
mapped.sort(function(a, b) {
  var a_rank = 0;
  var b_rank = 0;
  // Loop through our properties
  fields.forEach(function(field) {

    // Get our value (skip the first)
    var x = a[field];
    var y = b[field];

    // Switch on the type
    switch (typeof x) {
      // Boolean
      case "boolean":
        a_rank += x ? -10 : 10;
        b_rank += y ? -10 : 10;
        break;
        // Default
      default:
        if (x > y) {
          a_rank += 10;
          b_rank -= 10;
        } else if (y > x) {
          a_rank -= 10;
          b_rank += 10
        }
        break;
    }
  });
  return a_rank > b_rank ? 1 : a_rank < b_rank ? -1 : 0
});
document.write("<pre>" + JSON.stringify(mapped,0,4)+ "</pre>")

Upvotes: 0

Niet the Dark Absol
Niet the Dark Absol

Reputation: 324640

Try a manual iteration instead of one that uses an inner function.

mapped.sort(function(a,b) {
    var x, y, i, l = fields.length;
    for( i=0; i<l; i++) {
        x = a[fields[i]];
        y = b[fields[i]];
        if( x === y) continue; // proceed to next field to compare
        switch(typeof x) {
        case "boolean":
            return x ? -1 : 1;
        // could do other things, eg. case "string": return x.localeCompare(y);
        default:
            return x < y ? -1 : 1;
        }
    }
    return 0; // all fields compared equal
});

Upvotes: 1

Related Questions