124

Can any one help me sort a 2 dimensional Array in JavaScript?

It will have data in the following format:

[12, AAA]
[58, BBB]
[28, CCC]
[18, DDD]

It should look like this when sorted:

[12, AAA]
[18, DDD]
[28, CCC]
[58, BBB]

So basically, sorting by the first column.

Cheers

jahroy
  • 22,322
  • 9
  • 59
  • 108
Alex
  • 11,551
  • 13
  • 30
  • 38
  • 4
    Here's everything you need to know: [MDN - Array.sort()](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/sort) – jahroy Apr 19 '13 at 04:52
  • 1
    please accept the answer of @PramodVemulapalli, all those currently high-voted are wrong! – Bergi Jun 06 '14 at 08:55
  • @jahroy: It's not about the type coercion, it's about the requirements for consistent comparison functions. – Bergi Jun 06 '14 at 09:13

15 Answers15

152

It's this simple:

var a = [[12, 'AAA'], [58, 'BBB'], [28, 'CCC'],[18, 'DDD']];

a.sort(sortFunction);

function sortFunction(a, b) {
    if (a[0] === b[0]) {
        return 0;
    }
    else {
        return (a[0] < b[0]) ? -1 : 1;
    }
}

I invite you to read the documentation.

If you want to sort by the second column, you can do this:

a.sort(compareSecondColumn);

function compareSecondColumn(a, b) {
    if (a[1] === b[1]) {
        return 0;
    }
    else {
        return (a[1] < b[1]) ? -1 : 1;
    }
}
jahroy
  • 22,322
  • 9
  • 59
  • 108
  • 6
    Please actually test your code. http://jsfiddle.net/DuR4B/2/ . Straight from the documentation link you posted: "If compareFunction is not supplied, elements are sorted by converting them to strings and comparing strings in lexicographic ("dictionary" or "telephone book," not numerical) order. For example, "80" comes before "9" in lexicographic order, but in a numeric sort 9 comes before 80." – Ian Apr 19 '13 at 04:43
  • 3
    @Ian - You're right. Good point. I guess I got over-excited to prove a point about simplicity. I did test it, but not completely. Now I'll fix it... I wish the sample data had proven your point before I smeared that egg all over my face! – jahroy Apr 19 '13 at 04:47
  • Haha I know I know, I hate when that kind of thing happens. It looks so right but something internally changes it that doesn't do as expected. Kinda like comparing strings with `<` or `>`. Anyways, I like the update :) – Ian Apr 19 '13 at 04:54
  • 6 upvotes for a blatantly wrong solution? I cannot believe this. Please, read about [comparison functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) and understand when they need to return negative values. – Bergi Jun 06 '14 at 08:54
  • @Bergi - I'm pretty shocked that I didn't use `a[0] - b[0]` like usual, but this code should still work... Have you tried it? The order of the elements is unchanged if the compare function returs zero or a nevative number (or `false`) and will swap the elements if the return value is positive (or `true`). This works because of the way JavaScript converts booleans to numbers. – jahroy Jun 06 '14 at 09:02
  • Well, your function does consider `12` and `58` as equal - because it returns `0` (assuming the obvious type casting). Ignoring the [requirements for comparison functions](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.11) can seriously mess up the sort, and does not guarantee to yield correct results. I wonder how much luck you need for this to work… – Bergi Jun 06 '14 at 09:11
  • @Bergi - Well, it's late at night and I'm typing on my phone, so I'll assume you're right and will change my `<`s to `-` (which is how most of us always write their comparators anyways). It would be great if you could explain briefly why this was wrong, rather than just shouting about how crappy everyone's answers are, though... – jahroy Jun 06 '14 at 09:17
  • @jahroy: [I've written an extensive explanation](http://stackoverflow.com/q/24080785/1048572) in an own question&answer now. Feedback welcome :-) You're right that when a column does not contain numbers, `function compareSecondColumn(a, b) { return a[1] - b[1]; }` is of course incorrect. – Bergi Jun 06 '14 at 11:35
  • @jahroy `sortFunction(a, b)` takes two arguments yet when used as `a.sort(sortFunction)` nothing is passed in. How does JS know what `a` and `b` stand for? I'm a little lost on this bit and not sure what to search for to a useful answer. If there is an article existing a link to it would be appreciated. – Ash Aug 05 '15 at 04:11
  • 1
    @Ash - The best place to look is the documentation. I like Mozilla's documentation, so when I have a question about a JS function I always google _"mdn {{function_name}}._" In this case the search term would be _"mdn array.sort"_ which brings you [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort). – jahroy Aug 07 '15 at 03:32
  • 1
    ... as you'll see in the documentation, the _array.sort()_ method takes a function as an argument, which is fairly common in JavaScript. The _array.sort()_ method is designed in a way that it knows what to do with the function passed to it: it uses it to compare its elements. Here's a [really lame fiddle](http://jsfiddle.net/3tfbsnop/) I made to try to demonstrate how you pass functions as references... sorry it's so bad. – jahroy Aug 07 '15 at 03:47
  • Works perfectly for me.=D – cs1349459 Oct 27 '20 at 13:35
  • is there a way to make this function column dynamic? like pass the column I want to sort by as A paramater? –  Jan 13 '22 at 22:22
83

The best approach would be to use the following, as there may be repetitive values in the first column.

var arr = [[12, 'AAA'], [12, 'BBB'], [12, 'CCC'],[28, 'DDD'], [18, 'CCC'],[12, 'DDD'],[18, 'CCC'],[28, 'DDD'],[28, 'DDD'],[58, 'BBB'],[68, 'BBB'],[78, 'BBB']];

arr.sort(function(a,b) {
    return a[0]-b[0]
});
YakovL
  • 7,557
  • 12
  • 62
  • 102
  • 1
    This is correct answer, it takes into consideration both digits in the number . Thanks! – nick Jun 02 '15 at 16:53
66

Try this:

//WITH FIRST COLUMN
arr = arr.sort((a, b) => a[0] - b[0]);

//WITH SECOND COLUMN
arr = arr.sort((a, b) => a[1] - b[1]);

Note: Original answer used a greater than (>) instead of minus (-) which is what the comments are referring to as incorrect.

Det
  • 3,640
  • 5
  • 20
  • 27
PSR
  • 39,804
  • 41
  • 111
  • 151
  • 9
    8 upvotes for a blatantly wrong solution? I cannot believe this. Please, read about [comparison functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) and understand when they need to return negative values. – Bergi Jun 06 '14 at 08:53
  • 6
    As Bergi stated, this is not the right solution. While it may work in many cases, there will be times where it won't work as expected and you're left scratching your head (it has happened to me). The crux of the problem is that the comparison function in this solution only returns two states (true/1, false/0), but it should be returning three states (zero, greater than zero, and less than zero). – thdoan May 06 '15 at 04:08
  • 1
    Did not work for me. @jahroy is the man with the correct answer – erdomester Jul 24 '16 at 13:03
  • 1
    Just a note for those reading the comments: The answer was corrected Jan 5. It is correct now (the compare function returns three possible states). – marlar Feb 15 '17 at 20:19
  • @Bergi, thank you for pointing this out. (for me, it doesn't work properly in IE11) and I was not able to understand (why it works in chrome) until I've seen your comment. Thanks! – Aleksandr Ianevski Mar 15 '17 at 10:15
  • `arr = arr.sort((a, b) => a[1] - b[1]);` – Det Oct 07 '22 at 18:09
16

Using the arrow function, and sorting by the second string field

var a = [[12, 'CCC'], [58, 'AAA'], [57, 'DDD'], [28, 'CCC'],[18, 'BBB']];
a.sort((a, b) => a[1].localeCompare(b[1]));
console.log(a)
Dinesh Rajan
  • 2,376
  • 23
  • 19
12

If you're anything like me, you won't want to go through changing each index every time you want to change the column you're sorting by.

function sortByColumn(a, colIndex){

    a.sort(sortFunction);

    function sortFunction(a, b) {
        if (a[colIndex] === b[colIndex]) {
            return 0;
        }
        else {
            return (a[colIndex] < b[colIndex]) ? -1 : 1;
        }
    }

    return a;
}

var sorted_a = sortByColumn(a, 2);
Charles Clayton
  • 17,005
  • 11
  • 87
  • 120
  • I just saw your answer, after drafting an answer myself with the exact same reasoning - with a small difference - I actually return the function to sort directly. – olamotte Apr 05 '17 at 21:20
8

in one line:

var cars = [
  {type:"Volvo", year:2016},
  {type:"Saab", year:2001},
  {type:"BMW", year:2010}
]


function myFunction() {
  return cars.sort((a, b)=> a.year - b.year)
}
Jar
  • 1,766
  • 1
  • 21
  • 27
6

If you want to sort based on first column (which contains number value), then try this:

arr.sort(function(a,b){
  return a[0]-b[0]
})

If you want to sort based on second column (which contains string value), then try this:

arr.sort(function(a,b){
  return a[1].charCodeAt(0)-b[1].charCodeAt(0)
})

P.S. for the second case, you need to compare between their ASCII values.

vector
  • 1,032
  • 1
  • 4
  • 16
Sabbir Ahmed
  • 1,468
  • 2
  • 16
  • 21
3

Nothing special, just saving the cost it takes to return a value at certain index from an array.

function sortByCol(arr, colIndex){
    arr.sort(sortFunction)
    function sortFunction(a, b) {
        a = a[colIndex]
        b = b[colIndex]
        return (a === b) ? 0 : (a < b) ? -1 : 1
    }
}
// Usage
var a = [[12, 'AAA'], [58, 'BBB'], [28, 'CCC'],[18, 'DDD']]
sortByCol(a, 0)
console.log(JSON.stringify(a))
// "[[12,"AAA"],[18,"DDD"],[28,"CCC"],[58,"BBB"]]"
Vikas Gautam
  • 1,793
  • 22
  • 21
  • How is this answer different from mine [here](http://stackoverflow.com/a/23959673/2374028)? – Charles Clayton Feb 22 '17 at 18:32
  • 1
    1. you are using `a[colIndex]` again and again but I am catching it here `a = a[colIndex]`. It's more efficient. 2. I am using different flavor of `if`, making it more shorter. 3. I am not returning `arr` as a result of `sortByCol` function which means my function can't be use to create another reference. Hope it helps! – Vikas Gautam Feb 22 '17 at 21:28
2

I use sort to sort the array

This is my code :

const sortArr = (arr) => {
    arr.sort((valA, valB) => valA[0] - valB[0])
    return arr
}

const arr = [
    [12, 'AAA'],
    [58, 'BBB'],
    [28, 'CCC'],
    [18, 'DDD']
]
console.log(sortArr(arr))
Fauzan DP
  • 51
  • 4
  • Thanks, this works. But note to others: this code is for number sorting only. For example on sorting by 3rd column see https://jsfiddle.net/oasjgvmc/ – bobt Jan 22 '23 at 13:23
1

Standing on the shoulders of charles-clayton and @vikas-gautam, I added the string test which is needed if a column has strings as in OP.

return isNaN(a-b) ? (a === b) ? 0 : (a < b) ? -1 : 1 : a-b  ;

The test isNaN(a-b) determines if the strings cannot be coerced to numbers. If they can then the a-b test is valid.

Note that sorting a column of mixed types will always give an entertaining result as the strict equality test (a === b) will always return false. See MDN here

This is the full script with Logger test - using Google Apps Script.

function testSort(){

function sortByCol(arr, colIndex){
    arr.sort(sortFunction);
    function sortFunction(a, b) {
        a = a[colIndex];
        b = b[colIndex];
       return isNaN(a-b) ? (a === b) ? 0 : (a < b) ? -1 : 1 : a-b  ;  // test if text string - ie cannot be coerced to numbers.
       // Note that sorting a column of mixed types will always give an entertaining result as the strict equality test will always return false
       // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness

       }
}
// Usage
var a = [ [12,'12', 'AAA'],
          [12,'11', 'AAB'],
          [58,'120', 'CCC'],
          [28,'08', 'BBB'],
          [18,'80', 'DDD'],
        ]
    var arr1 = a.map(function (i){return i;}).sort();  // use map to ensure tests are not corrupted by a sort in-place.

    Logger.log("Original unsorted:\n     " + JSON.stringify(a));
    Logger.log("Vanilla sort:\n     " + JSON.stringify(arr1));
    sortByCol(a, 0);
    Logger.log("By col 0:\n     " + JSON.stringify(a));
    sortByCol(a, 1);
    Logger.log("By col 1:\n     " + JSON.stringify(a));
    sortByCol(a, 2);
    Logger.log("By col 2:\n     " + JSON.stringify(a));

/* vanilla sort returns " [
                            [12,"11","AAB"],
                            [12,"12","AAA"],
                            [18,"80","DDD"],
                            [28,"08","BBB"],
                            [58,"120","CCC"]
                          ]
   if col 0 then returns "[
                            [12,'12',"AAA"],
                            [12,'11', 'AAB'],
                            [18,'80',"DDD"],
                            [28,'08',"BBB"],
                            [58,'120',"CCC"]
                          ]"
   if col 1 then returns "[
                            [28,'08',"BBB"],
                            [12,'11', 'AAB'],
                            [12,'12',"AAA"],
                            [18,'80',"DDD"],
                            [58,'120',"CCC"],

                          ]"
   if col 2 then returns "[
                            [12,'12',"AAA"],
                            [12,'11', 'AAB'],
                            [28,'08',"BBB"],
                            [58,'120',"CCC"],
                            [18,'80',"DDD"],
                          ]"
*/

}
DeeKay789
  • 353
  • 2
  • 4
  • 8
  • Update of possible interest - 2nd July, 2019. Sort is now 'stable'. Read here. (via Mathias BynensVerified @mathias) https://v8.dev/features/stable-sort – DeeKay789 Jul 07 '19 at 00:38
1
Solution vary depend on column value is numeric or string. 

To sort by first column if value is numeric,
array.sort( (a, b) => a[0] - b[0]);

To sort by second column if value is numeric,
array.sort( (a, b) => a[1] - b[1]);

To sort by first column if value is string/letter,
array.sort( function(a, b) {
    const nameA = a[0].toUpperCase(); // to avoid case while sort
    const nameB = b[0].toUpperCase();
    if(nameA > nameB)
        return 1;
    else if(nameB > nameA)
        return -1;
    else
        return 0;     
})
Mayur
  • 9
  • 1
0

As my usecase involves dozens of columns, I expanded @jahroy's answer a bit. (also just realized @charles-clayton had the same idea.)
I pass the parameter I want to sort by, and the sort function is redefined with the desired index for the comparison to take place on.

var ID_COLUMN=0
var URL_COLUMN=1

findings.sort(compareByColumnIndex(URL_COLUMN))

function compareByColumnIndex(index) {
  return function(a,b){
    if (a[index] === b[index]) {
        return 0;
    }
    else {
        return (a[index] < b[index]) ? -1 : 1;
    }
  }
}
olamotte
  • 917
  • 9
  • 20
0

Good idea Sabbir Ahmed, but only order by the first character, for three:

array.sort((a, b) => (a[n].charCodeAt(0)*1000000 + a[n].charCodeAt(1)*1000 + a[n].charCodeAt(2)) - (b[n].charCodeAt(0)*1000000 + b[n].charCodeAt(1)*1000 + b[n].charCodeAt(2)));
Michael Rovinsky
  • 6,807
  • 7
  • 15
  • 30
0

Sorting two dimensional array specifying the sort order and column. Default order 0 means no change hence order must be specified: 1 => Ascending, -1 => descending. Default column is 0.

let sort2d = (arr2d = [], order = 0, column = 0) => {
    if (column < 0 || column > 1) {
        if (order == 1) 
            return arr2d.sort((a, b) => a[column] - b[column])
        if (order == -1) 
            return arr2d.sort((a, b) => b[column] - a[column])
    }
    return arr2d
}

let arr2d = [ [5, 3], [2, 5], [9, 1], [4, 1] ]
console.log(sort2d(arr2d, 1, -1)) //  [ [ 2, 5 ], [ 5, 3 ], [ 9, 1 ], [ 4, 1 ] ]

0

If you want a solution to sort multi-dimensional integer array where sub-array have a fixed length [ [ -6, 1, 5 ], [ -8, 3, 5 ], [ -8, 2, 6 ], [ -8, 3, 6 ] ]

const array = [ [ -6, 1, 5 ], [ -8, 3, 5 ], [ -8, 2, 6 ], [ -8, 3, 6 ] ]
const multiArraySort = (array, num) => {
  return array = array.sort((a,b) => {
    let boolean = true
    let i = 0 
    while(boolean){      
      if(a[i] === b[i]){        
        if(a[i+1] === b[i+1]){          
          i < num - 1 ? i++ : boolean = false
        }else {          
          return a[i+1] - b[i+1]          
        }        
      } else {                
        return a[i] - b[i]
      }
    }
    return 0
  })
}
// 3 represents the fixed length of sub-array
let sarray = multiArraySort(array, 3) //cant' assign in const array

Answer: [ [ -8, 2, 6 ], [ -8, 3, 5 ], [ -8, 3, 6 ], [ -6, 1, 5 ] ]

Realms AI
  • 101
  • 4