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?

RiggsFolly
  • 93,638
  • 21
  • 103
  • 149
user1616338
  • 557
  • 1
  • 8
  • 24
  • 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?) – Mike 'Pomax' Kamermans Aug 04 '18 at 17:10
  • hi @Mike'Pomax'Kamermans I'm sorry I don't understand how to do what you're suggesting – user1616338 Aug 04 '18 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. – Mike 'Pomax' Kamermans Aug 04 '18 at 17:16

3 Answers3

8

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);
Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
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>";
dognose
  • 20,360
  • 9
  • 61
  • 107
  • This seems overkill for what usort with a two line comparator can already do? – Mike 'Pomax' Kamermans Aug 04 '18 at 17:24
  • Updated `array_multisort` call - made a misstake with the argument order. – dognose Aug 04 '18 at 17:25
  • 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 Aug 04 '18 at 17:26
  • @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. – dognose Aug 04 '18 at 17:28
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

Aldo
  • 730
  • 8
  • 20