0

I'm using the sort array prototype function on my array of objects-- sorting by the 'min' key

function setStatesOrder(stateOrderArr){

   return stateOrderArr.sort((x,y) => x.min - y.min);

}

But right now I want to keep the first object in my array of objects out of the sort. It has a key of "state" and value of "U.S." so I was trying to exclude this object by that but it's not working:

  function setStatesOrder(stateOrderArr){

     return stateOrderArr.sort((x,y) => {if(x.state !=="U.S."){x.min - y.min}});

  }


stateOrderArr  = [
{state: "U.S.", max: 1, min: -14, stateID: "US", final: 0.3},
{state: "Ohio", max: 5, min: -2, stateID: "OH", final: 5},
{state: "Georgia", max: 4, min: -5, stateID: "GA", final: -5},
{state: "Arizona", max: -1.5, min: -2, stateID: "AZ", final: -2}....]
Derek 朕會功夫
  • 92,235
  • 44
  • 185
  • 247
NewToJS
  • 2,011
  • 4
  • 35
  • 62
  • 2
    What do you mean by *exclude this object*? Perhaps you want to do a `filter` first before you sort the list. – Derek 朕會功夫 Oct 27 '16 at 16:54
  • You cannot "keep anything out of the sort". You have to define what position it should have after the sort, which can of course be "in front of everything else". – Bergi Oct 27 '16 at 17:04
  • Do you want to keep the **first** object out of the sort, or do you want to keep the "U.S." object out of the sort, and it just happens to be the first object in this particular sample dataset? –  Oct 27 '16 at 17:54

5 Answers5

0

I believe you're looking for

function setStatesOrder(stateOrderArr){
    return stateOrderArr.sort((x,y) => (y.state=="U.S.")-(x.state=="U.S.") || x.min - y.min);
}

You cannot just "not compare" two items if they are supposed to be ordered somehow. Either don't put them in the array that is getting sorted in the first place, or find a comparison that treats them specially by always treating items that have .state == "U.S." as smaller than everything else.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • @torazaburo It's basically equivalent to your (now deleted) answer, with the added benefit of returning `0` if both `x` and `y` are `U.S.` – Bergi Oct 27 '16 at 17:22
  • The coerced boolean arithmetic is a neat trick, but I think it hurts the readability significantly. – Mic Oct 27 '16 at 17:27
  • @Mic No, actually it mirrors the typical pattern of a comparison function, namely `something(A) - something(B)`, very nicely. –  Oct 27 '16 at 17:28
  • And yet the intent is still going to be unclear to the average maintenance programmer who picks it up later. Because you have expressions that produce booleans that you're then using for arithmetic, and then treating as a boolean again for the purpose of boolean logic. It's academically interesting, but a bad habit to get into for production code. It's also a completely unnecessary complication for the parameters of this question. – Mic Oct 27 '16 at 17:33
  • @Mic `return y.state=="U.S."&&x.state!="U.S." ? 1 : x.state=="U.S."&&y.state!="U.S." -1 : x.min - y.min` isn't exactly a readable alternative either… You'll get quickly accustomed to the trick. – Bergi Oct 27 '16 at 17:44
  • You seem to be taking this personally. There are readable solutions, and it is a fair criticism of yours to say that while clever, it has readability issues and unnecessary complexity for the given question. – Mic Oct 27 '16 at 17:51
  • @Mic I'm not taking anything personally. I even agree on the readability problem, you need to take a moment to understand it or already know the idiom beforehand, but I think it's still the best solution. And it's certainly no unnecessary complexity. – Bergi Oct 27 '16 at 17:54
0
stateOrderArr.sort(
  (x,y) => x.state === "U.S". ? -1 : y.state === "U.S." ? +1 ? x.min - y.min);

In other words, say that U.S. is always less than anything.

However, it might be more semantic to sort first, then move US to the front. Here's a little routine for moving something to the front:

function moveToFront(array, condition) {
  return array.splice(array.findIndex(condition), 1).concat(array);
}

This takes advantage of the fact that Array.splice returns the deleted item(s).

Then

moveToFront(
  array.sort((a, b) => a.min - b.min),
  a => a.state === "U.S.");
0

If you don't mind returning a new array instead of modifying the old one in place, then it's a lot cleaner (in my view) to not mess with the .sort function and just explicitly exclude the first item from the sort:

function setStatesOrder(stateOrderArr) {
  const [firstState, ...restOfStates] = stateOrderArr;
  restOfStates.sort((x,y) => x.min - y.min);
  return [firstState, ...restOfStates];

  //The previous two lines can also be combined, as
  //return [firstState, ...restOfStates.sort((x,y) => x.min - y.min)];
}

const stateOrderArr  = [
{state: "U.S.", max: 1, min: -14, stateID: "US", final: 0.3},
{state: "Ohio", max: 5, min: -2, stateID: "OH", final: 5},
{state: "Georgia", max: 4, min: -5, stateID: "GA", final: -5},
{state: "Arizona", max: -1.5, min: -2, stateID: "AZ", final: -2}];

console.log(setStatesOrder(stateOrderArr))
Retsam
  • 30,909
  • 11
  • 68
  • 90
-1

just filter your array before sorting:

function setStatesOrder(stateOrderArr){
   return stateOrderArr
     .filter(e => e.state !== "U.S.")
     .sort((x,y) => x.min - y.min);
}


stateOrderArr  = [
  {state: "U.S.", max: 1, min: -14, stateID: "US", final: 0.3},
  {state: "Ohio", max: 5, min: -2, stateID: "OH", final: 5},
  {state: "Georgia", max: 4, min: -5, stateID: "GA", final: -5},
  {state: "Arizona", max: -1.5, min: -2, stateID: "AZ", final: -2}]

const ordered = setStatesOrder(stateOrderArr);
ordered.forEach(e => console.log(e.state));

Georgia
Ohio
Arizona

you can run it here: https://runkit.com/arthur/5812319a2e7a3c0014799825

Arthur Cinader
  • 1,547
  • 1
  • 12
  • 22
-1

The sort predicate always needs to return a value. I think this is a decent place to use the ternary operator. The form is "[condition] ? [if true] : [if false]"

function setStatesOrder(stateOrderArr){

  return stateOrderArr.sort((x,y) => x.state === "U.S." ? -1 : x.min - y.min);

}

var stateOrderArr  = [
{state: "U.S.", max: 1, min: -14, stateID: "US", final: 0.3},
{state: "Ohio", max: 5, min: -2, stateID: "OH", final: 5},
{state: "Georgia", max: 4, min: -5, stateID: "GA", final: -5},
{state: "Arizona", max: -1.5, min: -2, stateID: "AZ", final: -2}]

document.write(setStatesOrder(stateOrderArr).map((x) => x.state));

Another valid approach would be to remove the elements you don't want to sort and put them back when you're done. For one element you could use Array.prototype.shift to take off the first element before you sort and then Array.prototype.unshift to put it back on. I don't think there's a measurable difference in your case.

function setStatesOrder(stateOrderArr){
  let first = stateOrderArr.shift();
  stateOrderArr = stateOrderArr.sort((x,y) => x.min - y.min);
  stateOrderArr.unshift(first);
  return stateOrderArr;

}

var stateOrderArr  = [
{state: "U.S.", max: 1, min: -14, stateID: "US", final: 0.3},
{state: "Ohio", max: 5, min: -2, stateID: "OH", final: 5},
{state: "Georgia", max: 4, min: -5, stateID: "GA", final: -5},
{state: "Arizona", max: -1.5, min: -2, stateID: "AZ", final: -2}]

document.write(setStatesOrder(stateOrderArr).map((x) => x.state));
Mic
  • 3,810
  • 1
  • 13
  • 16
  • 1
    You cannot just return `-1` only if the first argument is `U.S.`. What if it's in `y`? What if the predicate matches both? – Bergi Oct 27 '16 at 17:16
  • It's in the first position, according to the question. Not only can I do it, but I have done it, and it works as intended. – Mic Oct 27 '16 at 17:16
  • A comparison function is not supposed to return the desired index, just whether the the two items are bigger, equal to or smaller than each other. Have a look [over here](http://stackoverflow.com/q/24080785/1048572) about comparison functions that are wrong but work in edge cases (and of course, in the example the U.S. will always be sorted first if only the `min` values are compared) – Bergi Oct 27 '16 at 17:18
  • Which is represented by a numeric value. – Mic Oct 27 '16 at 17:19
  • Yes, and I'm trying to tell you that you are returning the wrong numeric value for `compare({state:"", min:1}, {state:"U.S.", min:2})` – Bergi Oct 27 '16 at 17:23
  • Then I suppose it's a good thing those aren't the parameters outlined in the question. – Mic Oct 27 '16 at 17:24
  • Well, you want the US to go first, right? Then that call should return `1`, not `-1`. – Bergi Oct 27 '16 at 17:25
  • It might work on that particular example, yes, probably because the US has the lowest `min` value anyway so it would come first even if you didn't check the state at all. But that proves nothing. In the general case, your code does not work, and that is what matters. – Bergi Oct 27 '16 at 17:41
  • 1
    You're mistaken. You're insistent that there's something wrong here, and there isn't. As long as US is the first element in the array - which the question states it is - it will never be y unless it moves, which that -1 is preventing. It will never be sorted out of the first position, which was the ask. – Mic Oct 27 '16 at 17:44
  • You're making assumptions about the internal implementation of the `sort` method which you shouldn't do. It might work in your current browser, but then won't in some other that still implements JS according to the spec. I have to admit that most algorithms won't pass the first array element to the second parameter, but still this is a very lame excuse for writing an inconsistent comparison function. You cannot just negate the result to get the reverse order, for example. Or it might run havoc on arrays that contain two `U.S` elements. – Bergi Oct 27 '16 at 17:50
  • 1
    It is also unreasonable to make the assumption that US will always come first, even if that happens to be the case in the sample dataset. –  Oct 27 '16 at 17:52
  • @torazaburo It is stated in the question that it will. "But right now I want to keep the first object in my array of objects out of the sort" .. this first object. – Mic Oct 27 '16 at 17:53
  • @Bergi That's a straw man argument. In the theoretical situation where there's a malformed dataset outside the parameters given in the question, the output would also be malformed. But that's a case of broken input. If the objects didn't have state property at all, it also wouldn't work. There's always broken input that will cause any given solution to fail. – Mic Oct 27 '16 at 17:56
  • @Mic It's more an argument about code quality. I understand now (thanks to your comment, this info was missing in the answer) why this works under the circumstances, but I prefer to write code that works under all circumstances (the OP even alludes to them changing, given that only "*right now*" he wants to keep the element in that position). At least, your code is missing a comment or maybe even an assert statement that documents your assumptions. – Bergi Oct 27 '16 at 18:01
  • @Bergi That's fair position to take. I'll add a little detail to document that the answer is specific to the exact parameters of the question. I took the "right now" to mean it is the current problem he's trying to solve, not as an allusion that there is an expectation for change. Even if there was going to be a change, none of us have sufficient information to predict how it would change. And there are more ways it could change where every solution here requires alteration than ways it could change where some solutions here still work. – Mic Oct 27 '16 at 18:18