views:

193

answers:

3

I have an array with arrays in it, where I want to sort the outer arrays based on values in a specific column in the inner.

I bet that sounded more than a bit confusing, so I'll skip straight to an example.

Initial data:

var data = [
  [
    "row_1-col1",
    "2-row_1-col2",
    "c-row_1-coln"
  ],
  [
    "row_2-col1",
    "1-row_2-col2",
    "b-row_2-coln"
  ],
  [
    "row_m-col1",
    "3-row_m-col2",
    "a-row_m-coln"
  ]
];

Sort data, based on column with index 1

data.sortFuncOfSomeKind(1);

where the object then would look like this;

var data = [
  [
    "row_2-col1",
    "1-row_2-col2",
    "b-row_2-coln"
  ],
  [
    "row_1-col1",
    "2-row_1-col2",
    "c-row_1-coln"
  ],
  [
    "row_m-col1",
    "3-row_m-col2",
    "a-row_m-coln"
  ]
];

Sort data, based on column with index 2

data.sortFuncOfSomeKind(2);

where the object then would look like this;

var data = [
  [
    "row_m-col1",
    "3-row_m-col2",
    "a-row_m-coln"
  ],
  [
    "row_2-col1",
    "1-row_2-col2",
    "b-row_2-coln"
  ],
  [
    "row_1-col1",
    "2-row_1-col2",
    "c-row_1-coln"
  ]
];

The big Q

Is there an existing solution to this that you know of, or would I have to write one myself? If so, which would be the easiest sort algorithm to use? QuickSort?

_L

A: 

Yes, there's a function sort that take a compare-function as an input parameter. So for any array, you can call sort and pass a function of your own to instruct how to compare the items.

var column = 1;
return data.sort(function(x,y) { return x[column] > y[column] });
David Hedlund
D'oh. I didn't know you could just return x[a] > y[a] for a comparison value like that. That'll save me a few lines of code.
awgy
@David: You're returning `true` or `false`. You need to return -1, 0, or 1. (I did check, in case there was some Really Cool Thing I was missing, but the above fails to sort correctly in all cases.)
T.J. Crowder
@awgy: You can't: http://pastie.org/951302
T.J. Crowder
@T.J.Crowder: when i run that code, it correctly outputs 'one - three - two', 'two - three - one', 'one - three - two'. just the output you'd expect from an alphabetical sort. is that not the result you're seeing? http://jsbin.com/ovoze3
David Hedlund
@T.J. Crowder: furthermore: mine - http://jsbin.com/ovoze3/edit yours - http://jsbin.com/ovoze3/2/edit . hit "preview" > "go" in them, and they'll produce exactly the same result
David Hedlund
@David: That's not the result I'm seeing (and perhaps more to the point, isn't the result I *should* see). My original test was on Chrome ("two three one", "three one two", "one two three"). It also doesn't work on IE7 ("one three two", "two one three", "two one three"). It does *seem* to work on Firefox, which I'm taking as a side-effect of how it implements `sort` (e.g., it just happens to flip at a time when the others done) rather than a reliable interpretation of the code. Running your jsbin tests on Chrome shows the same rseult, with mine showing the correct result.
T.J. Crowder
@David: See section 15.4.4.11 of the 5th ed. spec (http://www.ecma-international.org/publications/standards/Ecma-262.htm): The compare function *"...should be a function that accepts two arguments x and y and returns a negative value if x < y, zero if x = y, or a positive value if x > y."* and section 11.8.5 (about `>`): *"The comparison...produces true, false, or undefined (which indicates that at least one operand is NaN)."* Returning `true` or `false` out of the function therefore results in undefined behavior, and as the function *must* have three possible outcomes, incorrect behavior.
T.J. Crowder
Well, bleh then. I had typed up a solution differentiating between -1/0/1, but saw I collided with @David and thought his solution was more elegant. So, I deleted mine. I guess I should've left mine up. And here I was thinking I'd been missing out on some weird JavaScript concept.
awgy
@T.J. Crowder: curious, i ran all my tests on chrome 5, and opened up firefox just for verification. still seeing the same results in chrome, but i'll write that off as an implementation curiosity, then. thanks for the pointers
David Hedlund
@David: Weird! I'm really shocked to hear it worked in Chrome 5 beta. My tests were in Chrome 4.1. (And again, it *shouldn't* work, regardless, which is the more salient point.)
T.J. Crowder
this is due to get further downvoted i guess, but i'd rather not delete it, as the discussion that followed seems worth saving...
David Hedlund
@David: I expect if you edit it, it won't get downvoted further.
T.J. Crowder
The answer to this is probably that true/false with type coercion corresponds to 0/1, that is, two of the expected values.Now, depending on the sort algorithm used, and the array to be sorted, these two results (equal or x>y) might actually give the correct result. And as different browsers use different algorithms for sorting, I'm guessing this is whats happening here..
Sean Kinsey
+2  A: 

Array#sort (see section 15.4.4.11 of the spec, or MDC) accepts an optional function parameter which will be used to compare two entries for sorting purposes. The function should return -1 if the first argument is "less than" the second, 0 if they're equal, or 1 if the first is "greater than" the second. So:

outerArray.sort(function(a, b) {
    var valueA, valueB;

    valueA = a[1]; // Where 1 is your index, from your example
    valueB = b[1];
    if (valueA < valueB) {
        return -1;
    }
    else if (valueA > valueB) {
        return 1;
    }
    return 0;
});

(You can obviously compress that code a bit; I've kept it verbose for clarity.)

T.J. Crowder
A: 

Here is a solution not needing a separate variable to contain the index

var arr = [.....]
arr.sort((function(index){
    return function(a, b){
        return (a[index] === b[index] ? 0 : (a[index] < b[index] ? -1 : 1));
    };
})(2)); // 2 is the index

This sorts on index 2

Sean Kinsey
You should change that `=` to `===`. Comparisons don't like to be mistaken for assignments.
awgy
Well, change it to `==` or `===` depending on your needs.
T.J. Crowder
yep, a small bug there - fixed now
Sean Kinsey
I tend to suggest `===` as a result of some advice from Douglas Crockford: "It is almost always better to use the `===` and `!==` operators. The `==` and `!=` operators do type coercion. In particular, do not use `==` to compare against falsy values." But I agree that there are instances where the type coercion may be desired. (See: http://javascript.crockford.com/code.html)
awgy
@awgy: In this case, though, it probably makes more sense to use `==` -- because `>` and `<` do type conversion, too, so if you use `===` in his code above, you'll return `1` incorrectly if the values are `==` but not `===`.
T.J. Crowder
But in this case all the values are strings, so it really does't matter. And that > and < has to coerce as its pretty hard say which is 'before' the other between the string "2" and the number 3.
Sean Kinsey