2

I'm working on a function to do multi-leveled sorting (sort within a sort, for a lack of better term) for a client. Say we have a list of objects with different attributes such as:

  • name - object's name
  • type - object type
  • date - some date attribute

Let's say I wanted to sort the list first chronologically, then by object type, then alphabetically. How would I go about doing that?

currently I am using usort() to pass in my own comparing function, which will convert the above attributes to integers with different weight; eg. If the primary sorting is by date, I convert it to some integer, multiply it by 1000, convert the next tier of sorting to an integer (in this case the type), multiply it by 100, and so on, then add it all together to determine whether an object is < or > another.

Is there a much simpler/elegant solution? Thanks

EDIT: to clarify, is there a better way to do multi-level sorting without converting everything to a 'weight'?

kennypu
  • 5,950
  • 2
  • 22
  • 28
  • possible duplicate http://stackoverflow.com/questions/124266/sort-object-in-php – Teena Thomas Sep 22 '12 at 00:05
  • that's not what i'm asking here, in that thread, it is still sorting at one level. I am asking if there is a way to sort within a sort. – kennypu Sep 22 '12 at 00:11

1 Answers1

4

Basically, what you want to do is use a series of "short circuit" comparisons. A naive example, given your criteria above, might look something like this (untested):

function mySort($a, $b) {
    if ($a->name < $b->name) {
        return -1;
    }

    if ($a->name > $b->name) {
        return 1;
    }

    // If we get this far, then name is equal, so
    // move on to checking type:
    if ($a->type < $b->type) {
        return -1;
    }

    if ($a->type > $b->type) {
        return 1;
    }

    // If we get this far, then both name and type are equal,
    // so move on to checking date:
    if ($a->date < $b->date) {
        return -1;
    }

    if ($a->date > $b->date) {
        return 1;
    }

    // If we get this far, then all three criteria are equal,
    // so for sorting purposes, these objects are considered equal.
    return 0;
}

As I said, though, this is a naive solution, and it's very non-extensible. I'd recommend going with a slightly more robust solution, where your sorts aren't hard-coded into the sort method. Take this approach, for example (untested):

// These are the properties to sort by, and the sort directions.
// They use PHP's native SORT_ASC and SORT_DESC constants.
$this->_sorts = [
    'name' => SORT_ASC,
    'type' => SORT_ASC,
    'date' => SORT_ASC
];

// Implemented as a class method this time.
protected function _mySort($a, $b) {
    foreach ($this->_sorts as $property => $direction) {
        if ($a->{$property} < $b->{$property}) {
            return $direction === SORT_ASC ? -1 : 1;
        }

        if ($a->{$property} > $b->{$property}) {
            return $direction === SORT_ASC ? 1 : -1;
        }
    }

    return 0;
}

Now, adding or removing different sort fields or sort directions is as simple as adding or modifying an array element. No code modification necessary.

FtDRbwLXw6
  • 27,774
  • 13
  • 70
  • 107