Reputation: 615
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:
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",
}
]
Upvotes: 2
Views: 6597
Reputation: 23482
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.
Upvotes: 4
Reputation: 104780
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
}
]
*/
Upvotes: 1
Reputation: 11171
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
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.
Upvotes: 1
Reputation: 2719
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;
});
Upvotes: -1
Reputation: 24590
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"}]
Upvotes: -3