21

I have the following array:

Array
(
    [0] => stdClass Object
        (
            [timestamp] => 1
            [id] => 10
        )

    [1] => stdClass Object
        (
            [timestamp] => 123
            [id] => 1
        )

    [2] => stdClass Object
        (
            [timestamp] => 123
            [id] => 2
        )

) 

I'm currently using the following code to sort the array by the timestamp property:

function sort_comments_by_timestamp(&$comments, $prop)
{
    usort($comments, function($a, $b) use ($prop) {
        return $a->$prop < $b->$prop ? 1 : -1;
    });
}

How can I also sort id by id descending when timestamp is the same?

2
  • 1
    Is there a reason why you call it _by_timestamp, but also have a $prop parameter? Commented Dec 22, 2011 at 0:37
  • 1
    @Matthew other than I was doing some tests and bad naming? No :) Already fixed it btw Commented Dec 22, 2011 at 0:40

8 Answers 8

23

Suggestion is to send in an array with $props

function sort_comments_by_timestamp(&$comments, $props)
{
    usort($comments, function($a, $b) use ($props) {
        if($a->$props[0] == $b->$props[0])
            return $a->$props[1] < $b->$props[1] ? 1 : -1;
        return $a->$props[0] < $b->$props[0] ? 1 : -1;
    });
}

And then call it with

sort_comments_by_timestamp($unsorted_array,array("timestamp","id"));

If you want it to work with X number of $props you can make a loop inside the usort always comparing a property with its preceding property in the array like this:

function sort_comments_by_timestamp(&$comments, $props)
{
    usort($comments, function($a, $b) use ($props) {
        for($i = 1; $i < count($props); $i++) {
            if($a->$props[$i-1] == $b->$props[$i-1])
                return $a->$props[$i] < $b->$props[$i] ? 1 : -1;
        }
        return $a->$props[0] < $b->$props[0] ? 1 : -1;
    });
}

Cheers!

Sign up to request clarification or add additional context in comments.

1 Comment

If you get error: Array to string conversion you need to replace all $a->$props[X] to $a->{$props[X]}
4
function sort_comments_by_timestamp(&$comments, $prop)
{
    usort($comments, function($a, $b) use ($prop) {
        if ($a->$prop == $b->$prop)
          return $b->id - $a->id;
        else
          return $a->$prop < $b->$prop ? 1 : -1;
    });
}

The above sorts first by the $prop parameter and then secondary by id.

Comments

3

You can do it with ouzo goodies (I know you've heard about it :P).

$result = Arrays::sort($array,
    Comparator::compound(
        Comparator::compareBy('timestamp'),
        Comparator::compareBy('id')
    )
);

Comments

3

I know this is fairly old question however didn't find much info for when you want to sort by multiple properties kinda like mySQL ORDERBY does so here's a function from a personal framework

Option 1: order($key, $direction='asc') where $key is a property of the objects and $direction is 'asc' or 'desc'

Option 2: order(array($key => $direction, $key => $direction)) This is similar to option 1 however when 2 objects have the same value for $key, the second sort option from the array passed is used.

public function order($arr, $key=null, $direction='ASC'){
    if(!is_string($key) && !is_array($key))
        throw new InvalidArgumentException("order() expects the first parameter to be a valid key or array");

    $props = array();

    if(is_string($key)) {
        $props[$key] = strtolower($direction) == 'asc' ? 1 : -1;
    }else{
        $i = count($key);
        foreach($key as $k => $dir){
            $props[$k] = strtolower($dir) == 'asc' ? $i : -($i);
            $i--;
        }
    }

    usort($arr, function($a, $b) use ($props){
        foreach( $props as $key => $val ){
            if( $a->$key == $b->$key ) continue;
            return $a->$key > $b->$key ? $val : -($val);
        }
        return 0;
    });

    return $arr;

}

2 Comments

Very nice little function. I was using the accepted answer for my sorting needs (with some personal tweaks) and it decided to stop working after a CMS update of the website. This saved me a bunch of time trying to debug what the problem is/was.
Glad it helped out :)
2
$result = -1;
if ($a->timestamp < $b->timestamp) {
   $result = 1;
} else if ($a->timestamp === $b->timestamp) {
   if ($a->id < $b->id) $result = 1;
}
return $result;

Put this within the usort closure. You can also get rid of the $prop argument and the use ($prop) part.

Comments

2
      function compare_city($a, $b)
      {
        // sort by state
        $retval = strnatcmp($a->state, $b->state);
        // if identical, sort by city
        if(!$retval) $retval = strnatcmp($a->city, $b->city);
        return $retval;
      }

      // sort alphabetically by state and city
      usort($sortable, __NAMESPACE__ . '\compare_city');

This function worked with me! Answer found at: Sorting SimpleXMLElement Object arrays

1 Comment

A number of the answers above (including the accepted answer!) seem to miss the 'equal' outcome for comparisons. Nice to see use of strnatcmp to handle this properly, in particular for string properties. Also don't forget strnatcasecmp for the case insensitive equivalent.
2

The accepted answer has a loop variant that will not always work.

if($a->$props[$i-1] == $b->$props[$i-1])
     return $a->$props[$i] < $b->$props[$i] ? 1 : -1;

obj1 prop1:foo, prop2: bar, prop3: test

obj2 prop1:foo, prop2: bar, prop3: apple

At $i = 1, the first comparison is true, and as bar is not less than bar, it will return -1 rather than continue with the loop to evaluate prop3.

Just an FYI.

I have a variant that also takes direction, so each property can also be sorted by ascending or descending order using -1 and 1, respectively.

function sortByProps(&$anArray, $props) {
    usort($anArray, function($a, $b) use ($props) {
        for($i = 0; $i < count($props); $i++) {
            if( $a->{$props[$i][0]} < $b->{$props[$i][0]}) {
                return $props[$i][1];
            }
            else if( $a->{$props[$i][0]} > $b->{$props[$i][0]}) {
                return -1* $props[$i][1];
            }
        }
        return 0;
    });
}

sortByProps($someArray,(array(['name',-1],['address',-1],['occupation',-1],['favorite_food',-1])));

Comments

1

Here's how to sort by an arbitrary number of criteria, one after the other, breaking ties. It works a lot like an sql order by col1, col2 clause.

I always forget if $a - $b vs $b - $a sorts ascending or descending. Adjust as needed.

$comparatorSequence = array(
    function($a, $b) { return $a->timestamp - $b->timestamp; }
  , function($a, $b) { return $a->id - $b->id; }
);

usort($theArray, function($a, $b) use ($comparatorSequence) {
    foreach ($comparatorSequence as $cmpFn) {
        $diff = call_user_func($cmpFn, $a, $b);
        if ($diff !== 0) {
            return $diff;
        }
    }
    return 0;
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.