1

I have the following response I get from my server:

[
{"key":{"name":"1","kind":"a"},
{"key":{"name":"1","kind":"ap"},
{"key":{"name":"5","kind":"ap"}
]

The responses come in a different order every time as the server does not guarantee order. Thus I need to sort the response like this after I receive it:

First sort so the smallest KIND is first so 'a' should come before 'b'. I then need to make it so name of the username is the first ordered within the 'a'.

var username = '5';

var response = [
{"key":{"name":"1","kind":"a"},
{"key":{"name":"1","kind":"ap"},
{"key":{"name":"5","kind":"ap"}
];

response = entities.sort(function (a, b) {
                        if (a.key.kind != b.key.kind){ return a.key.kind < b.key.kind}
                        else if(a.key.name == username){ return a.key.name < b.key.name }
                        else return a.key.name > b.key.name;
                    });

This is the code I use to sort, but it does not work. It sorts the KIND correctly, but then when it needs to sort by NAME (username should come before other names) it does not work.

The actual result I get is equal to this:

[
{"key":{"name":"1","kind":"ap"},
{"key":{"name":"5","kind":"ap"},
{"key":{"name":"1","kind":"a"}
]

But the result I want is this:

[
{"key":{"name":"5","kind":"ap"},
{"key":{"name":"1","kind":"ap"},
{"key":{"name":"1","kind":"a"}
]

As you can see my username is equal to 5, so {"key":{"name":"5","kind":"ap"} should come before {"key":{"name":"1","kind":"ap"} .

user2924127
  • 6,034
  • 16
  • 78
  • 136
  • possible duplicate of [Sorting in JavaScript: Shouldn't returning a boolean be enough for a comparison function?](http://stackoverflow.com/q/24080785/1048572) – Bergi May 24 '16 at 03:32

3 Answers3

3

return a.key.name < b.key.name is not saying that a < b.
They actually will be compared.
Try to replace it with return -1; to say to comparator:

a is lesser than b in that case. 
vp_arth
  • 14,461
  • 4
  • 37
  • 66
  • Thanks for this! should I then now too change the a.key.kind < b.key.kind to return -1 too and a.key.name > b.key.name to return 1? – user2924127 May 23 '16 at 19:54
  • no, if you don't need to disable sort at all. If you return negative you say `ab`. Actually you want to return `a-b`, but this operation is not defined on strings. `a.key.kindb.key.kind)?1:0` does this work. – vp_arth May 23 '16 at 21:36
1

When you use .sort() methods, you have to pass a compare function which will say who should comes first between two elements.

When you are comparing numbers, you can simply do :

function compare(a, b) {
  return a - b;
}

this will sort an array of numbers ascending.

However, when you are comparing string, you have to define some ordering criterion, in order to tell which element should be comes first.

  • If compare(a, b) is less than 0, sort a to a lower index than b, so a comes first.
  • If compare(a, b) is greater than 0, sort b to a lower index than a, so b comes first.
  • If compare(a, b) is equal to 0, there is no change

So will get something like :

function compare(a, b) {
  if (a is lower than b by your criteria) {
    return -1;
  }
  if (a is greater than b by your criteria) {
    return 1;
  }
  return 0;
}

In your case, you can write function generator that takes the property of the object to sort, and a custom sort function. This is useful when the function needs to be applied in more than one situation.

const username = '5';

const response = [
{"key":{"name":"1","kind":"a"}},
{"key":{"name":"1","kind":"ap"}},
{"key":{"name":"5","kind":"ap"}}
];

//Retrieve the value at 'props' into 'obj'
function retrieve(props, obj) {
  return props.reduce((result, current) => {
      result = result[current];
      return result;
    }, obj);
}



//Custom sort function for Kind properties
function sortKind(a, b) {
   return a < b ? -1 : (a > b) ? 1 : 0;
}



//Custom sort function for name properties
function sortName(a, b) {
  return a === username ? -1 : 1;
}



//Generic sort function
function sortByProp(...props) {
  const callback = props.pop();
  return function(a, b) {
    const v1 = retrieve(props, a);
    const v2 = retrieve(props, b);
    return callback(v1, v2);
  } 
}


//Then you just have to call your sort function, and you can chain them
const res = response
          .sort(sortByProp('key', 'kind', sortKind))
          .sort(sortByProp('key', 'name', sortName));

console.log(res);

You can see here a Working plunker

Paul Boutes
  • 3,285
  • 2
  • 19
  • 22
  • Thanks this works great! Question, since I am using this is node and my version doesn't support spread operators (...) is there an alternative I can use here? – user2924127 May 23 '16 at 21:14
  • Sure, you can look at [arguments](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments). Becareful, `arguments` is not an array, so you have to convert arguments to a real Array, by using `Array.from(arguments)` for example. – Paul Boutes May 23 '16 at 21:31
0

You missed "return" in this line

else if(a.key.name == username){ a.key.name < b.key.name }
amaksr
  • 7,555
  • 2
  • 16
  • 17
  • Ahh, sorry yes in the question I did, but in my actual code I have it in there. Even with this it does not work still. – user2924127 May 22 '16 at 23:45