6

I have an array

Array
(
[0] => stdClass Object
    (
        [tab_option_name_selector] => 2
        [fieldtype] => notes
        [order] => 12
    )

[1] => stdClass Object
    (
        [tab_option_name_selector] => 2
        [fieldtype] => notes
        [order] => 8
    )

[2] => stdClass Object
    (
        [tab_option_name_selector] => 1
        [order] => 2
        [fieldtype] => selectbox
    )

[3] => stdClass Object
    (
        [tab_option_name_selector] => 2
        [order] => 3
        [fieldtype] => selectbox
    )
)

I'm trying to get this usort function to work

function osort(&$array, $props) 
{ 
    if(!is_array($props)) 
        $props = array($props => true); 

    $me = usort($array, function($a, $b) use ($props) { 
        foreach($props as $prop => $ascending) 
        { 
            if($a->$prop != $b->$prop) 
            { 
                if($ascending) 
                    return $a->$prop > $b->$prop ? 1 : -1; 
                else 
                    return $b->$prop > $a->$prop ? 1 : -1; 
            } 
        } 
        return -1; //if all props equal        
    });    
    print_r($props);
    return ($me);
} 


$tab = osort($objectArray, "tab_option_name_selector", "order"); 

so sorting by the tab then order.

$tab is empty - any ideas what I'm doing wrong?

3
  • why the extra level of indirection and making things more confusing? Why not $sorted = usort($objectArray, "sortObjects"); with sortObjects returning -1/0/1 based on the tab and order values? (if tabs differ, return their cmp, if they're the same, return the order cmp, done?) Commented Aug 4, 2018 at 17:10
  • hi @Mike'Pomax'Kamermans I'm sorry I don't understand how to do what you're suggesting Commented Aug 4, 2018 at 17:14
  • what are you talking about, your code does this already. You just instead of doing the sorting in a function called osort, only have your function do the comparison, and then just call usort` directly with your custom compare function. Commented Aug 4, 2018 at 17:16

3 Answers 3

9

Why the extra level of indirection and making things more confusing? Why not usort directly with usort($objectArray, "sortObjects"); using a sortObjects($a,$b) function that does what any comparator does: return negative/0/positive numbers based on the input?

If the tabs differ, return their comparison, if they're the same, return the order comparison; done.

$array = array(
    (object)array(
        'tab_option_name_selector' => 2,
        'fieldtype' => 'notes',
        'order' => 12
    ),
    (object)array(
        'tab_option_name_selector' => 2,
        'fieldtype' => 'notes',
        'order' => 8
    ),
    (object)array(
        'tab_option_name_selector' => 1,
        'order' => 2,
        'fieldtype' => 'selectbox'
    ),
    (object)array(
        'tab_option_name_selector' => 2,
        'order' => 3,
        'fieldtype' => 'selectbox'
    )
);

function compareTabAndOrder($a, $b) {
    // compare the tab option value
    $diff = $a->tab_option_name_selector - $b->tab_option_name_selector;
    // and return it. Unless it's zero, then compare order, instead.
    return ($diff !== 0) ? $diff : $a->order - $b->order;
}

usort($array, "compareTabAndOrder");
print_r($array);
Sign up to request clarification or add additional context in comments.

Comments

1

Why don't you use array_multisort? http://php.net/manual/de/function.array-multisort.php

$data = //your array

//Create independent arrays
foreach ($data as $row) {
  foreach ($row as $key => $value){
    ${$key}[]  = $value; 
    //Creates $tab_option_name_selector, $fieldtype and $order array
    //in order to use them as independent arrays in array_multisort.
  }  
}

array_multisort($tab_option_name_selector, SORT_ASC, $order, SORT_ASC, $data);

//$data sorted as expected.
echo "<pre>";
print_r($data);
echo "</pre>";

4 Comments

This seems overkill for what usort with a two line comparator can already do?
Updated array_multisort call - made a misstake with the argument order.
as a counterpoint: that would be incredibly bad programming. If you rely on that many properties for your comparisons, your objects should just be real classed objects with compare functions, stored in a proper data structure that understands insert and in-place sorting.
@Mike'Pomax'Kamermans well, if you have a Table, which you are going to display and allow for "multi-sort" (first id, second name, third date ... and so on) the "native" compare functions doesn't help you at all. array_multisort just needs to be feed with one more array to achieve what is desired.
0

An example for unlimited number of properties:

$data = [
    (object)['volume' => '1', 'edition'  => '1'],
    (object)['volume' => '2', 'edition'  => '1'],
    (object)['volume' => '3', 'edition'  => '10'],
    (object)['volume' => '3', 'edition'  => '50'],
    (object)['volume' => '3', 'edition'  => '20'],
    (object)['volume' => '4', 'edition'  => '3'],
];

// sorting list by properties
$sorting = ['volume' => SORT_DESC, 'edition' => SORT_ASC];

$arrays = [];
foreach ($sorting as $key => $sort) {
    $column = array_column($data, $key);
    if (!empty($column)) {
        $arrays[] = $column;
        $arrays[] = $sort;
    }
}

if (!empty($arrays)) {
    $arrays[] = $data;
    if (!array_multisort(...$arrays)) {
        var_dump('some error');
        die();
    }
    // get last array, that is the sorted data
    $data = ($arrays[array_key_last($arrays)]);
}

Example partially from php.net - array_multisort

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.