355

Is it possible to sort and rearrange an array that looks like this:

itemsArray = [ 
    ['Anne', 'a'],
    ['Bob', 'b'],
    ['Henry', 'b'],
    ['Andrew', 'd'],
    ['Jason', 'c'],
    ['Thomas', 'b']
]

to match the arrangement of this array:

sortingArr = [ 'b', 'c', 'b', 'b', 'a', 'd' ]

Unfortunately, I don’t have any IDs to keep track on. I would need to priority the items-array to match the sortingArr as close as possible.

Update:

Here is the output I’m looking for:

itemsArray = [    
    ['Bob', 'b'],
    ['Jason', 'c'],
    ['Henry', 'b'],
    ['Thomas', 'b']
    ['Anne', 'a'],
    ['Andrew', 'd'],
]

Any idea how this can be done?

KARTHIKEYAN.A
  • 18,210
  • 6
  • 124
  • 133
user1448892
  • 3,561
  • 2
  • 14
  • 6
  • If you don't want to do everything manually, take a look at the array function sin [PHP.js](http://phpjs.org/functions/). – Adi Nov 09 '12 at 08:36
  • 15
    Where multiple arrays have the same sorting value (i.e. 'b') how do you decide which item goes where in the sorted array? With 'Bob', 'Henry' and 'Thomas' which all have the value 'b' - how do you decide which goes first, third and fourth? – Mitch Satchwell Nov 09 '12 at 08:52
  • Related: [Sorting objects by property values](/q/2466356/4642212), [Sorting arrays numerically by object property value](/q/7889006/4642212). – Sebastian Simon Apr 10 '22 at 12:02

27 Answers27

606

One-Line answer.

itemsArray.sort(function(a, b){  
  return sortingArr.indexOf(a) - sortingArr.indexOf(b);
});

Or even shorter:

itemsArray.sort((a, b) => sortingArr.indexOf(a) - sortingArr.indexOf(b));
luiscla27
  • 4,956
  • 37
  • 49
Durgpal Singh
  • 11,481
  • 4
  • 37
  • 49
  • 45
    This will mutate `itemsArray`. Depending on the performance requirement, it would be a lot safer to do `itemsArray.slice().sort(...)`. – Kevin Ghadyani Mar 06 '18 at 19:09
  • Important to have the 'return' keyword! – doyz Oct 11 '18 at 17:10
  • 2
    sort method return an array. see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort – Durgpal Singh Oct 12 '18 at 06:26
  • 14
    It does return the array, but it also does the sort in place and mutates the original. – mynameistechno Nov 13 '18 at 00:22
  • 5
    this should be real answer – urmurmur Jul 17 '19 at 15:56
  • 1
    Can some-one explain why / how this works. I have tried it with data that is similar to the OPs, an array of objects of which one member is a GUID, and a sorting array of those GUIDs. I was dubious before I tried it and discovered that .. as I expected this had no effect on the order. – Morvael Sep 13 '19 at 12:58
  • 21
    @Morvael, this is due to this answer requiring `sortingArr` to contain all values in `itemsArray`. The fix is to push items to the back of the array if they don't exist in `sortingArr` : ```allProducts.sort((product1, product2) => { const index1 = manualSort.indexOf(product1.id); const index2 = manualSort.indexOf(product2.id); return ( (index1 > -1 ? index1 : Infinity) - (index2 > -1 ? index2 : Infinity) ); });``` – Freshollie Oct 09 '19 at 09:20
  • 1
    OP should mark this as the best answer @user1448892 – alexrogers Oct 27 '20 at 11:19
  • How about if I have to sort string array "names" based on another character array alpha. alpha = ['h','g','k','r','o','e','a','j','c','n','b'] names = ['jack','rob','josh','john','greg'] The expected result should be ['greg','rob','john','josh','jack'] Tried names.sort(function(a, b){ return alpha.indexOf(a) - alpha.indexOf(b); }); few more above given answers It just returns same array ?? – Mayank Sep 14 '21 at 04:46
  • This code doesn't produce the expected result from the question. `sortingArr.indexOf()` will always give `-1` in this case because neither `a` or `b` arrays from the `.sort()` callback are in `sortingArray`. It should have been `sortingArray.indexOf(a[1])` and the same with `b[1]`. But that still won't produce the expected result as it will sort _all_ `b` items first. With that said, I still think this answer is useful and will work for most people's use-case, just not the use-case that OP had. – Nick Parsons Mar 26 '22 at 05:47
  • It sorts in `descending` order. – Tobi G. Nov 15 '22 at 16:00
  • I asked Chat GPT and it returned exactly the "or even shorter" answer. How interesting. – Haddock-san Apr 21 '23 at 18:41
  • this asnwer returns the wrong result. it sortds the array by the order of first found index and omit all other unused values/indices of following wanted order. for me it's eiter this answer wrong or the question. – Nina Scholz May 07 '23 at 09:37
95

Something like:

items = [ 
    ['Anne', 'a'],
    ['Bob', 'b'],
    ['Henry', 'b'],
    ['Andrew', 'd'],
    ['Jason', 'c'],
    ['Thomas', 'b']
]

sorting = [ 'b', 'c', 'b', 'b', 'c', 'd' ];
result = []

sorting.forEach(function(key) {
    var found = false;
    items = items.filter(function(item) {
        if(!found && item[1] == key) {
            result.push(item);
            found = true;
            return false;
        } else 
            return true;
    })
})

result.forEach(function(item) {
    document.writeln(item[0]) /// Bob Jason Henry Thomas Andrew
})

Here's a shorter code, but it destroys the sorting array:

result = items.map(function(item) {
    var n = sorting.indexOf(item[1]);
    sorting[n] = '';
    return [n, item]
}).sort().map(function(j) { return j[1] })
georg
  • 211,518
  • 52
  • 313
  • 390
  • 60
    Quadratic complexity! Try it with a big amount of data… – Julien Royer Nov 09 '12 at 09:27
  • 7
    @thg435: complexity has little to do with "optimization", unless the volume of data is guaranteed to be small (which may be the case here). – Julien Royer Nov 09 '12 at 09:36
  • 4
    @georg When it comes to the complexity of algorithms acting on data structures, the optimizing of algorithms with quadratic (or worse) complexities is never premature and is always necessary (unless you can guarantee the size of the data set is going to be small). The difference in performance is (quite literally) expressed in orders of magnitude. – Abion47 Feb 04 '19 at 20:26
49

If you use the native array sort function, you can pass in a custom comparator to be used when sorting the array. The comparator should return a negative number if the first value is less than the second, zero if they're equal, and a positive number if the first value is greater.

So if I understand the example you're giving correctly, you could do something like:

function sortFunc(a, b) {
  var sortingArr = [ 'b', 'c', 'b', 'b', 'c', 'd' ];
  return sortingArr.indexOf(a[1]) - sortingArr.indexOf(b[1]);
}

itemsArray.sort(sortFunc);
David Lewis
  • 802
  • 6
  • 4
32

Case 1: Original Question (No Libraries)

Plenty of other answers that work. :)

Case 2: Original Question (Lodash.js or Underscore.js)

var groups = _.groupBy(itemArray, 1);
var result = _.map(sortArray, function (i) { return groups[i].shift(); });

Case 3: Sort Array1 as if it were Array2

I'm guessing that most people came here looking for an equivalent to PHP's array_multisort (I did) so I thought I'd post that answer as well. There are a couple options:

1. There's an existing JS implementation of array_multisort(). Thanks to @Adnan for pointing it out in the comments. It is pretty large, though.

2. Write your own. (JSFiddle demo)

function refSort (targetData, refData) {
  // Create an array of indices [0, 1, 2, ...N].
  var indices = Object.keys(refData);

  // Sort array of indices according to the reference data.
  indices.sort(function(indexA, indexB) {
    if (refData[indexA] < refData[indexB]) {
      return -1;
    } else if (refData[indexA] > refData[indexB]) {
      return 1;
    }
    return 0;
  });

  // Map array of indices to corresponding values of the target array.
  return indices.map(function(index) {
    return targetData[index];
  });
}

3. Lodash.js or Underscore.js (both popular, smaller libraries that focus on performance) offer helper functions that allow you to do this:

    var result = _.chain(sortArray)
      .pairs()
      .sortBy(1)
      .map(function (i) { return itemArray[i[0]]; })
      .value();

...Which will (1) group the sortArray into [index, value] pairs, (2) sort them by the value (you can also provide a callback here), (3) replace each of the pairs with the item from the itemArray at the index the pair originated from.

Don McCurdy
  • 10,975
  • 2
  • 37
  • 75
  • 1
    Excellent solution, alternatively you can use _.indexBy and remove the shift if your data structure is a little more complex – Frozenfire May 31 '16 at 14:02
22

this is probably too late but, you could also use some modified version of the code below in ES6 style. This code is for arrays like:

var arrayToBeSorted = [1,2,3,4,5];
var arrayWithReferenceOrder = [3,5,8,9];

The actual operation :

arrayToBeSorted = arrayWithReferenceOrder.filter(v => arrayToBeSorted.includes(v));

The actual operation in ES5 :

arrayToBeSorted = arrayWithReferenceOrder.filter(function(v) {
    return arrayToBeSorted.includes(v);
});

Should result in arrayToBeSorted = [3,5]

Does not destroy the reference array.

Sushruth
  • 1,074
  • 1
  • 9
  • 14
  • 4
    What if I the arrayToBeSorted is an Array of Objects ie: {1: {…}, 2: {…}, 3: {…}, 4: {…}, 5: {…}}? but the arrayWithReferenceOrder is just a normal array? – Crystal Nov 28 '17 at 00:16
  • 5
    @sushruth how does this sort the array? – hitautodestruct May 19 '19 at 11:39
  • @Crystal, that's an object, not an array of objects. The elements/items in an object have no order, that is, their order is not set. An array of objects would look something like `[{name: "1"}, {name: "2"}, {name: "3"}, ...]`. – JohnK Mar 20 '20 at 21:54
20

Why not something like

//array1: array of elements to be sorted
//array2: array with the indexes

array1 = array2.map((object, i) => array1[object]);

The map function may not be available on all versions of Javascript

Luca Di Liello
  • 1,486
  • 2
  • 17
  • 34
  • This is the cleanest answer, imo, but you don't need to use both `object` and `i`. Given an array `arr` of elements to be sorted and an array of indices `ind`: `ind.map(index => arr[index])` – rupumped Aug 17 '23 at 19:44
20

function sortFunc(a, b) {
  var sortingArr = ["A", "B", "C"];
  return sortingArr.indexOf(a.type) - sortingArr.indexOf(b.type);
}

const itemsArray = [
  {
    type: "A",
  },
  {
    type: "C",
  },
  {
    type: "B",
  },
];
console.log(itemsArray);
itemsArray.sort(sortFunc);
console.log(itemsArray);
Debabrata Nayak
  • 457
  • 4
  • 5
12

ES6

const arrayMap = itemsArray.reduce(
  (accumulator, currentValue) => ({
    ...accumulator,
    [currentValue[1]]: currentValue,
  }),
  {}
);
const result = sortingArr.map(key => arrayMap[key]);

More examples with different input arrays

Can Rau
  • 3,130
  • 1
  • 23
  • 33
8

I would use an intermediary object (itemsMap), thus avoiding quadratic complexity:

function createItemsMap(itemsArray) { // {"a": ["Anne"], "b": ["Bob", "Henry"], …}
  var itemsMap = {};
  for (var i = 0, item; (item = itemsArray[i]); ++i) {
    (itemsMap[item[1]] || (itemsMap[item[1]] = [])).push(item[0]);
  }
  return itemsMap;
}

function sortByKeys(itemsArray, sortingArr) {
  var itemsMap = createItemsMap(itemsArray), result = [];
  for (var i = 0; i < sortingArr.length; ++i) {
    var key = sortingArr[i];
    result.push([itemsMap[key].shift(), key]);
  }
  return result;
}

See http://jsfiddle.net/eUskE/

Julien Royer
  • 1,419
  • 1
  • 14
  • 27
8

In case you get here needing to do this with an array of objects, here is an adaptation of @Durgpal Singh's awesome answer:

const itemsArray = [
  { name: 'Anne', id: 'a' },
  { name: 'Bob', id: 'b' },
  { name: 'Henry', id: 'b' },
  { name: 'Andrew', id: 'd' },
  { name: 'Jason', id: 'c' },
  { name: 'Thomas', id: 'b' }
]

const sortingArr = [ 'b', 'c', 'b', 'b', 'a', 'd' ]

Object.keys(itemsArray).sort((a, b) => {
  return sortingArr.indexOf(itemsArray[a].id) - sortingArr.indexOf(itemsArray[b].id);
})
user2521295
  • 823
  • 2
  • 12
  • 23
6
var sortedArray = [];
for(var i=0; i < sortingArr.length; i++) {
    var found = false;
    for(var j=0; j < itemsArray.length && !found; j++) {
        if(itemsArray[j][1] == sortingArr[i]) {
            sortedArray.push(itemsArray[j]);
            itemsArray.splice(j,1);
            found = true;
        }
    }
}

http://jsfiddle.net/s7b2P/

Resulting order: Bob,Jason,Henry,Thomas,Anne,Andrew

Mitch Satchwell
  • 4,770
  • 2
  • 24
  • 31
6

For getting a new ordered array, you could take a Map and collect all items with the wanted key in an array and map the wanted ordered keys by taking sifted element of the wanted group.

var itemsArray = [['Anne', 'a'], ['Bob', 'b'], ['Henry', 'b'], ['Andrew', 'd'], ['Jason', 'c'], ['Thomas', 'b']],
    sortingArr = [ 'b', 'c', 'b', 'b', 'a', 'd' ],
    map = itemsArray.reduce((m, a) => m.set(a[1], (m.get(a[1]) || []).concat([a])), new Map),
    result = sortingArr.map(k => (map.get(k) || []).shift());

console.log(result);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • That's my fav, I do the same yet use `{}` instead of `Map` ‍♂️ – Can Rau Feb 03 '20 at 19:21
  • @Nina Scholz I just scrolled through to find your answer and wasn't disappointed. I knew you would be here. Kudos to you for always helping out :) – camelCase Apr 03 '21 at 14:42
5

I hope that I am helping someone, but if you are trying to sort an array of objects by another array on the first array's key, for example, you want to sort this array of objects:

const foo = [
  {name: 'currency-question', key: 'value'},
  {name: 'phone-question', key: 'value'},
  {name: 'date-question', key: 'value'},
  {name: 'text-question', key: 'value'}
];        

by this array:

const bar = ['text-question', 'phone-question', 'currency-question', 'date-question'];

you can do so by:

foo.sort((a, b) => bar.indexOf(a.name) - bar.indexOf(b.name));
Bullsized
  • 362
  • 4
  • 7
4
let a = ['A', 'B', 'C' ]

let b = [3, 2, 1]

let c = [1.0, 5.0, 2.0]

// these array can be sorted by sorting order of b

const zip = rows => rows[0].map((_, c) => rows.map(row => row[c]))

const sortBy = (a, b, c) => {
  const zippedArray = zip([a, b, c])
  const sortedZipped = zippedArray.sort((x, y) => x[1] - y[1])

  return zip(sortedZipped)
}

sortBy(a, b, c)
  • 3
    Please consider adding a brief explanation/description explaining why/how this code answers the question. – Yannis Jun 12 '18 at 11:47
4

This is what I was looking for and I did for sorting an Array of Arrays based on another Array:

It's On^3 and might not be the best practice(ES6)

function sortArray(arr, arr1){
      return arr.map(item => {
        let a = [];
        for(let i=0; i< arr1.length; i++){
          for (const el of item) {
            if(el == arr1[i]){
              a.push(el);
            }   
            }
          }
          return a;
      });
    }
    
    const arr1 = ['fname', 'city', 'name'];
  const arr = [['fname', 'city', 'name'],
  ['fname', 'city', 'name', 'name', 'city','fname']];
  console.log(sortArray(arr,arr1));
It might help someone
El.
  • 1,217
  • 1
  • 13
  • 23
3
let sortedOrder = [ 'b', 'c', 'b', 'b' ]
let itemsArray = [ 
    ['Anne', 'a'],
    ['Bob', 'b'],
    ['Henry', 'b'],
    ['Andrew', 'd'],
    ['Jason', 'c'],
    ['Thomas', 'b']
]
a.itemsArray(function (a, b) {
    let A = a[1]
    let B = b[1]

    if(A != undefined)
        A = A.toLowerCase()

    if(B != undefined)
        B = B.toLowerCase()

    let indA = sortedOrder.indexOf(A)
    let indB = sortedOrder.indexOf(B)

    if (indA == -1 )
        indA = sortedOrder.length-1
    if( indB == -1)
        indB = sortedOrder.length-1

    if (indA < indB ) {
        return -1;
    } else if (indA > indB) {
        return 1;
    }
    return 0;
})

This solution will append the objects at the end if the sorting key is not present in reference array

JD-V
  • 3,336
  • 1
  • 17
  • 20
2

I had to do this for a JSON payload I receive from an API, but it wasn't in the order I wanted it.

Array to be the reference array, the one you want the second array sorted by:

var columns = [
    {last_name: "last_name"},
    {first_name: "first_name"},
    {book_description: "book_description"},
    {book_id: "book_id"},
    {book_number: "book_number"},
    {due_date: "due_date"},
    {loaned_out: "loaned_out"}
];

I did these as objects because these will have other properties eventually.

Created array:

 var referenceArray= [];
 for (var key in columns) {
     for (var j in columns[key]){
         referenceArray.push(j);
     }
  }

Used this with result set from database. I don't know how efficient it is but with the few number of columns I used, it worked fine.

result.forEach((element, index, array) => {                            
    var tr = document.createElement('tr');
    for (var i = 0; i < referenceArray.length - 1; i++) {
        var td = document.createElement('td');
        td.innerHTML = element[referenceArray[i]];
        tr.appendChild(td);

    }
    tableBody.appendChild(tr);
}); 
johnny
  • 19,272
  • 52
  • 157
  • 259
2
  const result = sortingArr.map((i) => {
    const pos = itemsArray.findIndex(j => j[1] === i);
    const item = itemsArray[pos];
    itemsArray.splice(pos, 1);
    return item;
  });
Pacuraru Daniel
  • 1,207
  • 9
  • 30
  • 56
1

You could try this method.

const sortListByRanking = (rankingList, listToSort) => {
  let result = []

  for (let id of rankingList) {
    for (let item of listToSort) {
      if (item && item[1] === id) {
        result.push(item)
      }
    }
  }

  return result
}
1

with numerical sortingArr:

itemsArray.sort(function(a, b){  
  return sortingArr[itemsArray.indexOf(a)] - sortingArr[itemsArray.indexOf(b)];
});
niko
  • 100
  • 4
1

This seems to work for me:

var outputArray=['10','6','8','10','4','6','2','10','4','0','2','10','0'];
var template=['0','2','4','6','8','10'];
var temp=[];

for(i=0;i<template.length;i++) {
  for(x=0;x<outputArray.length;x++){
    if(template[i] == outputArray[x]) temp.push(outputArray[x])
  };
}

outputArray = temp;
alert(outputArray)
Omar Omeiri
  • 1,506
  • 1
  • 17
  • 33
Howard
  • 471
  • 4
  • 3
1

This approach features a sorting algoritm where for each item of the order array, the data is searched for a corresponding item and this item is swapped by the left neighbor until the item is at the right position.

 a   b   b   d   c   b   data
<b>  a   b   d   c   b   right order of <>
 b  <c>  a   b   d   b
 b   c  <b>  a   d   b
 b   c   b  <b>  a   d
 b   c   b   b  <a>  d
 b   c   b   b   a  <d>

const
    data = [['Anne', 'a'], ['Bob', 'b'], ['Henry', 'b'], ['Andrew', 'd'], ['Jason', 'c'], ['Thomas', 'b']],
    order = ['b', 'c', 'b', 'b', 'a', 'd'];
    
let i = 0;

for (const o of order) {
    let j = i;
    while (data[j][1] !== o) j++;
    while (i !== j) {
        [data[j], data[j - 1]] = [data[j - 1], data[j]];
        j--;
    }
    i++;
}

console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

this should works:

var i,search, itemsArraySorted = [];
while(sortingArr.length) {
    search = sortingArr.shift();
    for(i = 0; i<itemsArray.length; i++) {
        if(itemsArray[i][1] == search) {
            itemsArraySorted.push(itemsArray[i]);
            break;
        }
    } 
}

itemsArray = itemsArraySorted;
Luca Rainone
  • 16,138
  • 2
  • 38
  • 52
0
this.arrToBeSorted =  this.arrToBeSorted.sort(function(a, b){  
  return uppthis.sorrtingByArray.findIndex(x => x.Id == a.ByPramaeterSorted) - uppthis.sorrtingByArray.findIndex(x => x.Id == b.ByPramaeterSorted);
});
integrater
  • 79
  • 7
-1

Use the $.inArray() method from jQuery. You then could do something like this

var sortingArr = [ 'b', 'c', 'b', 'b', 'c', 'd' ];
var newSortedArray = new Array();

for(var i=sortingArr.length; i--;) {
 var foundIn = $.inArray(sortingArr[i], itemsArray);
 newSortedArray.push(itemsArray[foundIn]);
}
toxicate20
  • 5,270
  • 3
  • 26
  • 38
-1

Use intersection of two arrays.

Ex:

var sortArray = ['a', 'b', 'c',  'd', 'e'];

var arrayToBeSort = ['z', 's', 'b',  'e', 'a'];

_.intersection(sortArray, arrayToBeSort) 

=> ['a', 'b', 'e']

if 'z and 's' are out of range of first array, append it at the end of result

Joe.CK
  • 339
  • 3
  • 2
-5

You can do something like this:

function getSorted(itemsArray , sortingArr ) {
  var result = [];
  for(var i=0; i<arr.length; i++) {
    result[i] = arr[sortArr[i]];
  }
  return result;
}

You can test it out here.

Note: this assumes the arrays you pass in are equivalent in size, you'd need to add some additional checks if this may not be the case.

refer link

refer

Community
  • 1
  • 1
Gadde
  • 1,451
  • 15
  • 37
  • 2
    This badly needs editing. not only does the jfiddle return the wrong result, the function argument names don't match the inner content? – twobob May 05 '17 at 10:47