1

I have an array of objects that represent hockey rank data:

[
  {
     name: "Russia",
     points: 10,
     iihf: 1
  },
  {
     name: "USA",
     points: 10,
     iihf: 7
  },
  {
     name: "Slovakia",
     points: 8,
     iihf: 6
  }
]

I want to sort the data descending from the highest score. If two or more teams share the same point count, they have to be fine sorted using next dimensions, namely the IIHF rank (ascending order). Is there an elegant solution to this?

Thanks.

Ozrix
  • 3,489
  • 2
  • 17
  • 27
  • 5
    What did you tried/searched? – DontVoteMeDown Dec 30 '13 at 11:59
  • 1
    I find [this answer](http://stackoverflow.com/a/9175783/989121) quite elegant ;] – georg Dec 30 '13 at 12:00
  • 3
    @thg435 - possibly because it's your own? – adeneo Dec 30 '13 at 12:02
  • 1
    possible duplicate of [Javascript sort function. Sort by First then by Second](http://stackoverflow.com/questions/9175268/javascript-sort-function-sort-by-first-then-by-second) – georg Dec 30 '13 at 12:10
  • @DontVoteMeDown I've done a simple fn as sort param that sorted by dimension name, but couldn't wrap my head around how to keep the original sort intact while fine sorting the equals. Thought I had to extract the equals, sort those, and put them back into the original sort. Didn't know if I could do it in a less elaborate way, hence the question. – Ozrix Dec 30 '13 at 12:27

3 Answers3

2

As addition: if you know an upper limit of the iihf score, you can also precompute a compound score, that you only use for sorting. The compound score could look like this:

obj.cscore = obj.points * 10000 + obj.iihf;

After that, you can just sort after the cscore:

arr.sort(function(a,b) { 
    return a.cscore - b.cscore;
});

If you don't want to pollute your original objects, you can use a temporary map (as described here):

var map = arr.map(function (e, i) {
    return {index: i, cscore: e.points * 1000 + e.iihf};
});

map.sort(function (a,b) {
    return a.cscore - b.cscore;
});

var result = map.map(function(e){
  return arr[e.index]
});

FIDDLE

basilikum
  • 10,378
  • 5
  • 45
  • 58
  • `return a.cscore < b.cscore ? 1 : -1` is not correct - you're missing the equal case. An elegant way to compare numbers is to return their difference: `return a.score - b.score`. – georg Dec 30 '13 at 14:13
  • @thg435 thx, you're right. I actually mindlessly copied this part from the answer of adeneo (which also doesn't return zero). Anyway, I fixed it. – basilikum Dec 30 '13 at 14:34
  • 1
    Great! Now the next step: since both sort fields are numbers, you can combine them using boolean or: `return b.points - a.points || a.iihf - b.iihf` - no need for a compound key. – georg Dec 30 '13 at 14:44
  • @thg435 that's a really beautiful and simple way to solve this problem. Why didn't you post it as an answer? My goal was to introduce the general concept of a compound key. I realize that in this particular case, it might not be necessary but it is a way to solve the problem. – basilikum Dec 30 '13 at 16:09
  • Yes, schwartzian transform is useful technique worth knowing about. Good job! – georg Dec 30 '13 at 16:37
1

Use Array.sort(), if the values of points are equal, sort on iihf instead

arr.sort(function(a,b) {
    if (a.points == b.points) return a.iihf > b.iihf ? 1 : -1;    
    return a.points < b.points ? 1 : -1;
});

FIDDLE

adeneo
  • 312,895
  • 29
  • 395
  • 388
0

Try this

function comparator(a, b) {
   if (a.points == b.points) return a.iihf > b.iihf ? 1 : -1;    
return a.points < b.points ? 1 : -1;
}

var json = { "homes": 
             [
  {
     name: "Russia",
     points: 10,
     iihf: 1
  },
  {
     name: "USA",
     points: 10,
     iihf: 7
  },
  {
     name: "Slovakia",
     points: 8,
     iihf: 6
  }
]};
console.log(json["homes"].sort(comparator));

DEMO

Sridhar R
  • 20,190
  • 6
  • 38
  • 35