41

I was wondering how I can sort an array on a custom order, not alphabetical. Imagine you have this array/object:

var somethingToSort = [{
    type: "fruit",
    name: "banana"
}, {
    type: "candy",
    name: "twix"
}, {
    type: "vegetable",
    name: "broccoli"
}, {
    type: "vegetable",
    name: "carrot"
}, {
    type: "fruit",
    name: "strawberry"
}, {
    type: "candy",
    name: "kitkat"
}, {
    type: "fruit",
    name: "apple"
}];

In here we have 3 different types: fruit, vegetable and candy. Now I want to sort this array, and make sure that all fruits are first, candies come after fruits, and vegetables be last. Each type need their items to be sorted on alphabetical order. We will use a function like sortArrayOnOrder ( ["fruit","candy","vegetable"], "name" ); So basically, you would end up with this array after sorting:

var somethingToSort = [{
    type: "fruit",
    name: "apple"
}, {
    type: "fruit",
    name: "banana"
}, {
    type: "fruit",
    name: "strawberry"
}, {
    type: "candy",
    name: "kitkat"
}, {
    type: "candy",
    name: "twix"
}, {
    type: "vegetable",
    name: "broccoli"
}, {
    type: "vegetable",
    name: "carrot"
}];

Anyone an idea how to create a script for this?

Deltanic
  • 1,009
  • 4
  • 11
  • 17

4 Answers4

66

Improved version of Cerbrus' code:

var ordering = {}, // map for efficient lookup of sortIndex
    sortOrder = ['fruit','candy','vegetable'];
for (var i=0; i<sortOrder.length; i++)
    ordering[sortOrder[i]] = i;

somethingToSort.sort( function(a, b) {
    return (ordering[a.type] - ordering[b.type]) || a.name.localeCompare(b.name);
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    Hm, good idea to build a object out of the sortOrder, removes the need to use `indexOf` each time the function is called. +1 – Cerbrus Feb 14 '13 at 10:35
  • @ItaiSpector `map` is not a proper way of iterating if one does not want to create another array. The idiomatic modern syntax would be `for (const [index, value] of sortOrder.entries()) ordering[value] = index;`, or maybe a `reduce`. Even better though: `const ordering = new Map(sortOrder.map((v, i) => [v, i]))` and `ordering.get(a.type) - ordering.get(b.type)`. – Bergi Aug 10 '20 at 15:48
  • thanks for the idea, I merged it into: `const ordering = { fruit: 0, candy: 1, vegetable: 2 };` – riezebosch Jun 09 '23 at 13:07
  • 1
    @riezebosch Sure, it it's static then that's easier, but OP did receive the order as an array input – Bergi Jun 09 '23 at 17:05
14

Try this:

var sortOrder = ['fruit','candy','vegetable'];   // Declare a array that defines the order of the elements to be sorted.
somethingToSort.sort(
    function(a, b){                              // Pass a function to the sort that takes 2 elements to compare
        if(a.type == b.type){                    // If the elements both have the same `type`,
            return a.name.localeCompare(b.name); // Compare the elements by `name`.
        }else{                                   // Otherwise,
            return sortOrder.indexOf(a.type) - sortOrder.indexOf(b.type); // Substract indexes, If element `a` comes first in the array, the returned value will be negative, resulting in it being sorted before `b`, and vice versa.
        }
    }
);

Also, your object declaration is incorrect. Instead of:

{
    type = "fruit",
    name = "banana"
}, // etc

Use:

{
    type: "fruit",
    name: "banana"
}, // etc

So, replace the = signs with :'s.

Cerbrus
  • 70,800
  • 18
  • 132
  • 147
  • +1, nice one. See my answer for an effieciency-improved variant – Bergi Feb 14 '13 at 10:32
  • Wups, sorry about the = instead of :. Just wrote an example here, didn't copy pasta anything and forgot about the : to assign values. Im too used to use the = operator to assign values haha :P – Deltanic Feb 14 '13 at 11:26
2

For people looking to simply sort an array of strings in a custom order, try this function below:

// sorting fn
const applyCustomOrder = (arr, desiredOrder) => {
  const orderForIndexVals = desiredOrder.slice(0).reverse();
  arr.sort((a, b) => {
    const aIndex = -orderForIndexVals.indexOf(a);
    const bIndex = -orderForIndexVals.indexOf(b);
    return aIndex - bIndex;
  });
}

// example use
const orderIWant = ['cat', 'elephant', 'dog'];
const arrayToSort = ['elephant', 'dog', 'cat'];


applyCustomOrder(arrayToSort, orderIWant);

This will sort the array in the order specified. Two sample input / outputs to this function:

Example 1:

const orderIWant = ['cat', 'elephant', 'dog'] 
const arrayToSort = ['mouse', 'elephant', 'dog', 'cat'];
applyCustomOrder(arrayToSort, orderIWant); 
console.log(arrayToSort); // ["cat", "elephant", "dog", "mouse"]

Example 2:

const orderIWant = ['cat', 'elephant', 'dog'];
const arrayToSort = ['mouse', 'elephant', 'rabbit', 'dog', 'cat'];
applyCustomOrder(arrayToSort, orderIWant); 
console.log(arrayToSort); /* ["cat", "elephant", "dog", "mouse", 
"rabbit"] */
rishikarri
  • 3,462
  • 2
  • 13
  • 13
-3

Array.sort accepts a sort function where you can apply custom sorting logic.

Matt Randle
  • 1,885
  • 2
  • 17
  • 22