Link to home
Start Free TrialLog in
Avatar of Narusegawa
Narusegawa

asked on

USort with Multi Lveel Array

I'm trying to sort an array by a field several levels deep, but I just can't get it to work.

My array is
Array
(
    [0] => Array
        (
            [standingType] => agents
            [standings] => Array
                (
                    [0] => Array
                        (
                            [fromID] => 3009559
                            [fromName] => Ancevence Achelasse
                            [standing] => 2.36
                            [standingType] => agents
                        )
 
                    [1] => Array
                        (
                            [fromID] => 3009560
                            [fromName] => Gryncelbois Erris
                            [standing] => -0.01
                            [standingType] => agents
                        ) 
                )
 
        )
 
    [1] => Array
        (
            [standingType] => NPCCorporations
            [standings] => Array
                (
                    [0] => Array
                        (
                            [fromID] => 1000005
                            [fromName] => Hyasyoda Corporation
                            [standing] => 0.21
                            [standingType] => NPCCorporations
                        )
 
                    [1] => Array
                        (
                            [fromID] => 1000047
                            [fromName] => Krusual Tribe
                            [standing] => 0.18
                            [standingType] => NPCCorporations
                        )
 
                )
 
        )
 
    [2] => Array
        (
            [standingType] => factions
            [standings] => Array
                (
                    [0] => Array
                        (
                            [fromID] => 500001
                            [fromName] => Caldari State
                            [standing] => -2.26
                            [standingType] => factions
                        )
 
                    [1] => Array
                        (
                            [fromID] => 500002
                            [fromName] => Minmatar Republic
                            [standing] => 3.03
                            [standingType] => factions
                        )
 
                )
 
        )
 
)

Open in new window


And I'm trying to sort using this function. $standings is the array from above.

    usort($standings, 'standingStandingSort');

    function standingStandingSort($a, $b) {
        if ($a['standings']['standing'] < $b['standings']['standing'])
            return 0;
        return ($a['standings']['standing'] < $b['standings']['standing']) ? -1 : 1;
    }

Open in new window


However I just can't get it to work, I can sort simple arrays but this one is starting to get to me.

I want to get the child arrays sorted by the "Standing" field which is a simple float field. (includes negative)

I'd be appreciative of any quick answer to this. Thanks!
Avatar of andreizz
andreizz
Flag of Romania image

$standings['standings'][0]['standing']
$standings['standings'][1]['standing']
this is how you access the standing value and not $a['standings']['standing'] cause this is a array
you should use something like this:
    foreach($standings)
                 usort($standings['standings'], 'standingStandingSort');

    function standingStandingSort($a, $b) {
        if ($a['standing'] < $b['standings'])
            return 0;
        return ($a['standings'] < $b['standings']) ? -1 : 1;
    }

Open in new window


i am not sure that i got it right, you should double check. this will sort each on in turn.
Avatar of Narusegawa
Narusegawa

ASKER

I think I've been a little silly. I just realized that it's an Array of Objects.

If I do this
    function standingStandingSort($a, $b) {
        print_r($a);
        if ($a->standings[0]['standing'] < $b->standings[0]['standing'])
            return 0;
        return ($a->standings[0]['standing'] < $b->standings[0]['standing']) ? -1 : 1;
    }

Open in new window


The print_r($a) returns this :

Array
(
    [standingType] => NPCCorporations
    [standings] => Array
        (
            [0] => standingItem Object
                (
                    [fromID] => 1000005
                    [fromName] => Hyasyoda Corporation
                    [standing] => 3.21
                    [standingType] => NPCCorporations
                )
 
            [1] => standingItem Object
                (
                    [fromID] => 1000047
                    [fromName] => Krusual Tribe
                    [standing] => 5.18
                    [standingType] => NPCCorporations
                )
)

Open in new window


I tried your suggestion, but then I got told that I can't do that with an Object.
    
    foreach($standings as $k => $v)
                 usort($standings[$k]['standings'], 'standingStandingSort');

    function standingStandingSort($a, $b) {
        if ($a['standing'] < $b['standing'])
            return 0;
        return ($a['standing'] < $b['standing']) ? -1 : 1;
    }

Open in new window


this should work
Thanks for the help, I am getting the same error I had too.

"Fatal error: Cannot use object of type standingItem as array in /var/www/vhosts/plugin.php on line 114"

Line 114 is this line
if ($a['standing'] < $b['standing'])

Open in new window

hang on a second, i am copying the array and trying it myself, faster this way
Thanks, I really appreciate the help. I knew mixing objects and arrays was gonna be a pain.
Can you post a link to some test data - preferably generated in PHP?  Sorting an array of objects is a fairly well understood process.  You're on the right path with usort()
<?php
$standings=Array(Array('standings'=>Array(Array('standing'=>1.5),Array('standing'=>0.5))), Array('standings'=>Array(Array('standing'=>2.5),Array('standing'=>0.6))));
function cmp($a, $b){
	if ($a['standing']==$b['standing'])
        return 0;
    return ($a['standing'] < $b['standing']) ? -1 : 1;
}

foreach($standings as $k => $v){
	usort($standings[$k]['standings'], 'cmp');
}

print_r($standings);
?>

Open in new window


there you have it :D
@andreizz: That looks right - I have not tested it, but the standard usort() function seems to be implemented the usual way.  However Narusegawa indicated that the issue was sorting an array of objects (ID:34933046), and that would have a little bit different notation, not quite the same as an array of arrays.  Maybe if we can get the classes used to create the objects we can give a more precise answer.
I think what i have posted is exactly what he wanted, let's wait for his answer.
Sure.  Look at the example posted here: ID:34932897 -- that would seem to require OOP notation.
I'm just trying to get some code together that'd provide the same data structure that I am working with.
function standingStandingSort($a, $b){
	if ($a['standing']==$b['standing'])
        return 0;
    return ($a['standing'] < $b['standing']) ? -1 : 1;
}

foreach($standings as $k => $v){
	usort($standings[$k]['standings'], 'standingStandingSort');
}

print_r($standings);

Open in new window

I'll just test that now andreizz.

To replicate the same array of objects I am using, this is the code.
<?php

	ini_set('display_errors','1');
	
    class standingItem {
        var $fromID = 0;
        var $standing = 0;
		var $standingType = '';
    }
	
    $standing1 = new standingItem;
    $standing1->fromID = '1000005';
	$standing1->standing = '1.5';
	$standing1->standingType = 'Type1';
    $standing2 = new standingItem;
    $standing2->fromID = '1000006';
	$standing2->standing = '7';
	$standing2->standingType = 'Type1';
	$standing3 = new standingItem;
    $standing3->fromID = '1000007';
	$standing3->standing = '-5';
	$standing3->standingType = 'Type2';
	$standing4 = new standingItem;
    $standing4->fromID = '1000008';
	$standing4->standing = '8';
	$standing4->standingType = 'Type2';
		
	$standings = Array(
			'Type1'=> Array(
				'standingType'=>'Type1',
				'standings'=>Array($standing1,$standing2),
			),
			'Type2'=> Array(
				'standingType'=>'Type2',
				'standings'=>Array($standing3,$standing4),
			)		
	);
	
	print_r( $standings );

?>

Open in new window


You will get something like this
Array
(
    [Type1] => Array
        (
            [standingType] => Type1
            [standings] => Array
                (
                    [0] => standingItem Object
                        (
                            [fromID] => 1000005
                            [standing] => 1.5
                            [standingType] => Type1
                        )
 
                    [1] => standingItem Object
                        (
                            [fromID] => 1000006
                            [standing] => 7
                            [standingType] => Type1
                        )
 
                )
 
        )
 
    [Type2] => Array
        (
            [standingType] => Type2
            [standings] => Array
                (
                    [0] => standingItem Object
                        (
                            [fromID] => 1000007
                            [standing] => -5
                            [standingType] => Type2
                        )
 
                    [1] => standingItem Object
                        (
                            [fromID] => 1000008
                            [standing] => 8
                            [standingType] => Type2
                        )
 
                )
 
        )
 
)

Open in new window


And it's the standings sub-arrays that need sorting, inside their respective sections. Order by decending "standing" float field.

i.e

The result I'm looking to achieve is this :
Array
(
    [Type1] => Array
        (
            [standingType] => Type1
            [standings] => Array
                (
				
                    [0] => standingItem Object
                        (
                            [fromID] => 1000006
                            [standing] => 7
                            [standingType] => Type1
                        )
				
                    [1] => standingItem Object
                        (
                            [fromID] => 1000005
                            [standing] => 1.5
                            [standingType] => Type1
                        )

                )
 
        )
 
    [Type2] => Array
        (
            [standingType] => Type2
            [standings] => Array
                (
				    [0] => standingItem Object
                        (
                            [fromID] => 1000008
                            [standing] => 8
                            [standingType] => Type2
                        )
						
                    [1] => standingItem Object
                        (
                            [fromID] => 1000007
                            [standing] => -5
                            [standingType] => Type2
                        )
                 )
 
        )
 
)

Open in new window

the above script should work if you just paste it in your php script
Fatal error: Cannot use object of type standingItem as array in /var/www/vhosts/plugin.php on line 78

Line 78 : if ($a['standing']==$b['standing'])

Which is the second line in your function :(
i think you need to lose the usort function and do a simple comparison and just switch the values between them.

if you don't manage i'll try again to make a example.
ASKER CERTIFIED SOLUTION
Avatar of andreizz
andreizz
Flag of Romania image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
andreizz you are a star. I was just about to start looking into maybe sorting the data at an earlier stage (when it's still xml). But this works awesome! Thanks!
No problem :D anytime :D

Thanks to you i now have a premium account! :D
This worked for me, but you might want to exercise some caution about comparing floating point values.  It seems that the numeric range of the possible values of "standing" could be important.  See the notes in the man page link given on line 50.  You might want to use sprintf() or something to cast the "standing" into something that avoids the cautionary tales about float values.  Of course, if you are not doing arithmetic with the standings or if you are always setting that field with number_format() to limit the precision you should be OK.
<?php // RAY_temp_narusegawa.php
ini_set('display_errors', TRUE);
error_reporting(E_ALL);
echo "<pre>";

class standingItem
{
    public $fromID = 0;
    public $standing = 0;
    public $standingType = '';
}

$standing1 = new standingItem;
$standing1->fromID = '1000005';
$standing1->standing = '1.5';
$standing1->standingType = 'Type1';

$standing2 = new standingItem;
$standing2->fromID = '1000006';
$standing2->standing = '7';
$standing2->standingType = 'Type1';

$standing3 = new standingItem;
$standing3->fromID = '1000007';
$standing3->standing = '-5';
$standing3->standingType = 'Type2';

$standing4 = new standingItem;
$standing4->fromID = '1000008';
$standing4->standing = '8';
$standing4->standingType = 'Type2';

$standings = array(
        'Type1'=> Array(
            'standingType'=>'Type1',
            'standings'=>Array($standing1,$standing2),
        ),
        'Type2'=> Array(
            'standingType'=>'Type2',
            'standings'=>Array($standing3,$standing4),
        )
);

// SHOW THE ORIGINAL ORDER
var_dump($standings);

// A FUNCTION TO COMPARE BY STANDINGS
function cmp($thing1, $thing2)
{
    // CAST NUMBERS IN A CONSISTENT MANNER BUT SEE WARNING HERE: http://www.php.net/manual/en/language.types.float.php
    $a = (float)$thing1->standing;
    $b = (float)$thing2->standing;
    echo PHP_EOL . "$a $b";
    // COMPARE AND RETURN
    if ($a == $b) return 0;
    return ($a > $b) ? -1 : 1;
}

// ITERATE OVER THE ARRAY OF ARRAYS OF OBJECTS
foreach ($standings as $t => $type)
{
    $new_array = $type["standings"];
    usort($new_array, 'cmp');
    $standings[$t]["standings"] = $new_array;
}

// SHOW THE REVISED ORDER
var_dump($standings);

Open in new window