2

I have an array of objects returning from an API call which I need to sort into a specific format.

I'm trying to organise the destination_country_id alphabetically except for the first three and last items. For example, like so:

  1. "Ireland"
  2. "United Kingdom"
  3. "United States"
  4. ...other countries, alphabetically...
  5. "Everywhere Else"

I have considered using array.sort(), which I understand I can easily use to sort them alphabetically, but I've so far been unsuccessful in figuring out how I can achieve the desired output.

API Response

[
    {
        "destination_country_id":null,
        "primary_cost":"9.50",
        "region_id":null,
        "destination_country_name":"Everywhere Else",
    },
    {
        "destination_country_id":105,
        "primary_cost":"8.00",
        "region_id":null,
        "destination_country_name":"United Kingdom",
    },
    {
        "destination_country_id":209,
        "primary_cost":"9.50",
        "region_id":null,
        "destination_country_name":"United States",
    },
    {
        "destination_country_id":123,
        "primary_cost":"5.00",
        "region_id":null,
        "destination_country_name":"Ireland",
    },
    {
        "destination_country_id":185,
        "primary_cost":"5.00",
        "region_id":null,
        "destination_country_name":"France",
    },
    {
        "destination_country_id":145,
        "primary_cost":"5.00",
        "region_id":null,
        "destination_country_name":"Spain",
    }
]
6
  • 1
    Can you show us what you tried wrt sort so we can advise you how to correct it? Commented May 11, 2014 at 22:36
  • Remove the elements from the array that should be fixed, sort the rest of the array according to Sorting an array of JavaScript objects, and add the fixed elements back. Or white your comparison function in such a way that it treats the fixed elements differently. Commented May 11, 2014 at 22:37
  • Did you mean you want to organise alphabetically by destination_country_name rather than by destination_country_id? Commented May 11, 2014 at 23:40
  • Will Everywhere Else, the UK, Ireland, and the US always be the first four options? If so, you can splice the array, sort the second piece, and then rejoin them. Commented May 12, 2014 at 0:16
  • @Xotic750 Yes, sorting alphabetically by destination_country_name rather than country_destination_id. An error on my part. I'm relatively new to JavaScript, I've been teaching myself the syntax and working with small examples, but it's the new way of thinking that I'm having the most trouble with. As such, I didn't write any code yet, mostly thinking about how I could do this. As Felix mentions, I was thinking of removing the desired items, sorting the remaining, and adding them back, but it's the entire process that I have issue with as I can't wrap my head around how I would do that. Commented May 12, 2014 at 9:45

5 Answers 5

4

Possibly not the most efficient method but it is ES3, doesn't require any libraries, and is fairly easy to understand. Also assuming you wanted to sort alphabetically on destination_country_name

Javascript

// where x is your array of objects
x.sort(function (a, b) {
    // sorts everything alphabetically
    return a.destination_country_name.localeCompare(b.destination_country_name);
}).sort(function (a, b) {
    // moves only this to country to top
    return +(!b.destination_country_name.localeCompare('United States'));
}).sort(function (a, b) {
    // moves only this to country to top
    return +(!b.destination_country_name.localeCompare('United Kingdom'));
}).sort(function (a, b) {
    // moves only this to country to top
    return +(!b.destination_country_name.localeCompare('Ireland'));
}).sort(function (a, b) {
    // moves only this to country to bottom
    return +(!a.destination_country_name.localeCompare('Everywhere Else'));
});

console.log(JSON.stringify(x, ['destination_country_name']));

Output

[{"destination_country_name":"Ireland"},
 {"destination_country_name":"United Kingdom"},
 {"destination_country_name":"United States"},
 {"destination_country_name":"France"},
 {"destination_country_name":"Spain"},
 {"destination_country_name":"Everywhere Else"}]

On jsFiddle

We could even go a step further and use the above example to make a reusable function, like.

Javascript

function sorter(array, funcs, orders) {
    funcs = funcs || {};
    orders = orders || {};
    array.sort(funcs.general);
    if (Array.isArray(orders.top)) {
        orders.top.slice().reverse().forEach(function(value) {
            array.sort(funcs.top.bind(value));
        });
    }

    if (Array.isArray(orders.bottom)) {
        orders.bottom.forEach(function(value) {
            array.sort(funcs.bottom.bind(value));
        });
    }

    return array;
}

sorter(x, {
    general: function (a, b) {
        return a.destination_country_name.localeCompare(b.destination_country_name);
    },
    top: function (a, b) {
        return +(!b.destination_country_name.localeCompare(this));
    },
    bottom: function (a, b) {
        return +(!a.destination_country_name.localeCompare(this));
    }
}, {
    top: ['Ireland', 'United Kingdom', 'United States'],
    bottom: ['Everywhere Else']
});

On jsFiddle

And now you can easily sort on different attributes by parsing in different compare functions, and define values that should be at the top or bottom.

I used ECMA5 methods but you could just as easily make it with ECMA3.

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

2 Comments

This is very nice, thank you very much. What are the implications of using ES5 methods over ES3? I assume browser support?
Yes, but you can load es5 shim on your page and all the methods that I have used are well supported and to specification (except bind, but it is very close and the differences are not used in my example).
1

I think the most efficient way to sort your array is to first find where "Everywhere Else", the "UK", "Ireland", and the "US" are in your array, remove them, and then sort the rest of the array. This is simpler than it sounds

fiddle

var data = [
    {"destination_country_name": "Everywhere Else"},
    {"destination_country_name": "United Kingdom"},
    {"destination_country_name": "United States"},
    {"destination_country_name": "Ireland"},
    {"destination_country_name": "France"},
    {"destination_country_name": "Spain"}     ];
//removed the other elements just to make the example cleaner
var keep = ["Everywhere Else", "Ireland", "United Kingdom", "United States"];
//keep is the elements you want in the front; order them exactly at you want them ordered
var top = [];
//this is our holder array to hold the objects for the strings in keep

for (var i = 0; i < keep.length; i++) {
    var index = function () {
        for (var j = 0; j < data.length; j++){    //loop through data
            if (data[j].destination_country_name == keep[i])
                return data[j];    //return the object if it's name matches the one in keep
        }
    }
    if (index > -1){        //if the object is in the array (index != -1)
        top.push(data[index]);    //push the object to our holder array
        data.splice(index, 1);    //splice the object out of the original array
    }
}
//after this loop, those other objects will have been removed
//sort the rest of that array of objects
data.sort(function (a, b) {    //use a callback function to specify which parts of
                               //the object need to be sorted
    //basic sorting/compare algorithm (-1, 0, or 1)
    if (a.destination_country_name > b.destination_country_name)
        return 1;        //if greater
    if (a.destination_country_name < b.destination_country_name)
        return -1;        //if lesser
    return 0;    //otherwise
})

var sorted = top.concat(data), //combine data to the holder array and assign to sorted
    extra = sorted.shift();    //grab the first element ("Everywhere Else") and remove it
    sorted.push(extra);        //add that element to the end of the array
console.log(sorted);

Alternatively, if you know those four places (EE, UK, US, and Ireland) will always be the first 4 elements in your array, you can do the following:

var data = [
    {"destination_country_name": "Everywhere Else"},
    {"destination_country_name": "United Kingdom"},
    {"destination_country_name": "United States"},
    {"destination_country_name": "Ireland"},
    {"destination_country_name": "France"},
    {"destination_country_name": "Spain"}     ];

var top = data.slice(0,4);
data.sort(function (a, b) {
    if (a.destination_country_name > b.destination_country_name)
        return 1;
    if (a.destination_country_name < b.destination_country_name)
        return -1;
    return 0;
})

var sorted = top.concat(data),
        extra = sorted.shift();
        sorted = sorted.push(extra);    //put "Everywhere Else" at the end of the array

Note how this is much more efficient (and much simpler!) because you don't need to locate those four elements.

3 Comments

Your jsFiddle gave me [{"destination_country_name":"Everywhere Else"},{"destination_country_name":"France"},{"destination_country_name":"Ireland"},{"destination_country_name":"Spain"},{"destination_country_name":"United Kingdom"},{"destination_country_name":"United States"}]
@Xotic750 I said it would. You can add all the other object details and it'll have the same effect.
@Xotic750 Oh, I see. He wanted Everywhere Else last. That's an easy fix. One second
1

You can give every object a 'sort-order' property. Specify the known first 3 and the last, and give all the others the same value, greater than the first three and less than the last. Then sort the array- first by sort-order, and then alphabetically;

var arr= [{ "destination_country_id": null, "primary_cost": "9.50", "region_id": null, "destination_country_name": "Everywhere Else", },{ "destination_country_id": 105, "primary_cost": "8.00", "region_id": null, "destination_country_name": "United Kingdom", },{ "destination_country_id": 209, "primary_cost": "9.50", "region_id": null, "destination_country_name": "United States", },{ "destination_country_id": 123, "primary_cost": "5.00", "region_id": null, "destination_country_name": "Ireland", },{ "destination_country_id": 185, "primary_cost": "5.00", "region_id": null, "destination_country_name": "France", },{ "destination_country_id": 145, "primary_cost": "5.00", "region_id": null, "destination_country_name": "Spain", }]

var s= "destination_country_name",
order= ["Ireland", "United Kingdom", 
"United States", "Everywhere Else"];

arr.forEach(function(itm){
    var i= order.indexOf(itm[s]);
    if(i!= -1) itm.sort_order= i== 3? 1e50: i;
    else itm.sort_order= 10;
});

arr.sort(function(a, b){
    var d= a.sort_order- b.sort_order;
    if(d===0){
        if(a[s]=== b[s]) return 0;
        return a[s]>b[s]? 1: -1;
    }
    return d;
});
JSON.stringify(arr)
/*  returned value: (String)[{
        "destination_country_id": 123, "primary_cost": "5.00", "region_id": null, 
        "destination_country_name": "Ireland", "sort_order": 0
    },{
        "destination_country_id": 105, "primary_cost": "8.00", "region_id": null, 
        "destination_country_name": "United Kingdom", "sort_order": 1
    },{
        "destination_country_id": 209, "primary_cost": "9.50", "region_id": null, 
        "destination_country_name": "United States", "sort_order": 2
    },{
        "destination_country_id": 185, "primary_cost": "5.00", "region_id": null, 
        "destination_country_name": "France", "sort_order": 10
    },{
        "destination_country_id": 145, "primary_cost": "5.00", "region_id": null, 
        "destination_country_name": "Spain", "sort_order": 10
    },{
        "destination_country_id": null, "primary_cost": "9.50", "region_id": null, 
        "destination_country_name": "Everywhere Else", "sort_order": 1e+50
    }
]
*/

Comments

-1

If your provided array is called list, you can sort it as you want using the following call:

list.sort(function (item1, item2) {
    if (item1.destination_country_name < item2.destination_country_name) {
        return -1;
    }
    return 1;
});

1 Comment

And how does that compare to the OPs specification?
-3

you can use underscore sortBy method:

a=[{obj:'first3'},{obj:'first2'},{obj:'first1'},{obj:'z'},{obj:'m'},{obj:'c'},{obj:'end3'},{obj:'end2'},{obj:'end1'}]
a=_.sortBy(a,function (t,i){if (i<=2) return String.fromCharCode(0);if(i>=a.length-3) return String.fromCharCode(255);return t.obj })

console.log(JSON.stringify(a))

[{"obj":"first3"},{"obj":"first2"},{"obj":"first1"},{"obj":"c"},{"obj":"m"},{"obj":"z"},{"obj":"end3"},{"obj":"end2"},{"obj":"end1"}]

http://jsfiddle.net/43Q8h/

9 Comments

What in the world is going on here.
"You cannot sort objects in JS, only arraies." Good thing that the OP has an array then.
The answer is exactly what @Coogan request, (sory an array except for the first three and last items)
How come the first three and the last three elements changed their order then?
Please look at the edited answer. This is what @Coogan request. Sorting an array of objects.
|

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.