Link to home
Start Free TrialLog in
Avatar of jej07
jej07

asked on

Help needed grouping and sorting an array

I tried Googling but can't seem to find the answer or a similar technique that will help solve my problem. I'm not even really sure how to best to accomplish this.

What I'm trying to do group the results from my $distance_array, based on the key [type] from my $curr_res array, Then each group would be ordered by the $distance_array value.

The key [type] has three possible values Retail Store, Drop off, Contractor. However, I want to customize the order, rather than sorting by ascending or descending.

So the final output should look similar to this...

Drop off
Company One
2 Miles

Drop off
Company Two
5 Miles

Retail Store
Company Three
1.5 Miles

Contractor
Company Four
7 Miles

Contractor
Company Five
10 Miles

I'm looping through the $distance_array like this.
$curr_res = array();
$count = 0;

foreach($distance_array as $key => $value)
{
  
	$curr_res = $results[$key];
	
	echo stripslashes($curr_res['type'])."<br />";              
	echo stripslashes($curr_res['company_name'])."<br />";
	echo number_format($value, 2, '.', '') . " Miles <br />";              
	
	$count++; 
}

Open in new window


And my arrays look like this.
$distance_array
Array ( [10066] => 5.3109370772026 [10347] => 8.8816963223926 [66] => 12.837831771854 [10483] => 12.878820381449 [10625] => 15.002565483354 [64] => 15.29246611189 [10438] => 15.388386254936 [10327] => 19.261218858628 [10461] => 22.650257356203 [10243] => 26.26616586252 [10246] => 32.440312706073 [10422] => 45.859312064584 [10529] => 61.744883818327 [10397] => 67.358673239898 [10632] => 67.437024249966 [10547] => 74.253276511624 [10572] => 74.93732142815 [10240] => 97.381361623479 )

$curr_res
Array ( [id] => 10066 [type] => Retail Store [company_name] => Joe's Place [address] => Joes Address [city] => Madison [state] => WI [zip] => 53718 [country] => United States [lat] => 43.011803 [lng] => -89.513130 )
Avatar of Gary
Gary
Flag of Ireland image

If I am following you it seems it would be easier to merge the arrays then do a multi sort on the two keys?
Here are the data shown above in a form that will allow testing.  But it looks like this is not enough data to test with.  Is there some more so that we could get something that looks more like the output in the question?

<?php // RAY_temp_jej07.php
error_reporting(E_ALL);
echo '<pre>';


// SEE http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28348173.html


$distance_array = array
( '10066' => 5.3109370772026
, '10347' => 8.8816963223926
, '66'    => 12.837831771854
, '10483' => 12.878820381449
, '10625' => 15.002565483354
, '64'    => 15.29246611189
, '10438' => 15.388386254936
, '10327' => 19.261218858628
, '10461' => 22.650257356203
, '10243' => 26.26616586252
, '10246' => 32.440312706073
, '10422' => 45.859312064584
, '10529' => 61.744883818327
, '10397' => 67.358673239898
, '10632' => 67.437024249966
, '10547' => 74.253276511624
, '10572' => 74.93732142815
, '10240' => 97.381361623479
)
;

$curr_res = array
( 'id'           => '10066'
, 'type'         => 'Retail Store'
, 'company_name' => "Joe's Place"
, 'address'      => 'Joes Address'
, 'city'         => 'Madison'
, 'state'        => 'WI'
, 'zip'          => '53718'
, 'country'      => 'United States'
, 'lat'          => 43.011803
, 'lng'          => -89.513130
)
;

// DO THE ARRAYS LOOK RIGHT?  YES.
print_r($distance_array);
print_r($curr_res);

Open in new window

Avatar of jej07
jej07

ASKER

Will this work?
Array 
(

  'id'           => '10066'
, 'type'         => 'Retail Store'
, 'company_name' => "Joe's Place"
, 'address'      => 'Joes Address'
, 'city'         => 'Madison'
, 'state'        => 'WI'
, 'zip'          => '53718'
, 'country'      => 'United States'
, 'lat'          => 43.011803
, 'lng'          => -89.513130
  )
 

Array
(
 
  'id' => '10347' 
,  'type' => 'Contractor' 
,  'company_name' => 'Landscapes For You' 
,  'address' => 'Address' 
,  'city' => 'Carol Stream' 
,  'state' => 'IL' 
,  'zip' => '60188' 
,  'country' => 'United States' 
,  'lat' => 41.926319
,  'lng' => -88.132423 
  )
 

Array
  (
 
  'id' => '66' 
,  'type' => 'Contractor' 
,  'company_name' => 'GP and Sons' 
,  'address' => 'Address' 
,  'city' => 'Roselle' 
,  'state' => 'IL' 
,  'zip' => '60172' 
,  'country' => 'United States' 
, 'lat' => 41.969658
,  'lng' => -88.064316 
  )
 

Array
  (

  'id' => '10483' 
,  'type' => 'Drop off' 
,  'company_name' => 'VPS' 
,  'address' => 'Address' 
,  'city' => 'Gilberts' 
,  'state' => 'IL' 
,  'zip' => '60136' 
,  'country' => 'United States' 
,  'lat' => 42.100468 
,  'lng' => -88.366783 
  )
 

Array
  (
 
'id' => '10625' 
,  'type' => 'Drop off' 
,  'company_name' => 'Design Group LLC' 
,  'address' => 'Address' 
,  'city' => 'Lombard' 
,  'state' => 'IL' 
,  'zip' => '60148' 
,  'country' => 'United States' 
,  'lat' => 41.894997 
,  'lng' => -88.015114 
  )
 

Array
  (

  'id' => '64' 
,  'type' => 'Drop off' 
,  'company_name' => 'Gardens Unlimited' 
,  'address' => 'Address' 
,  'city' => 'Lombard' 
,  'state' => 'IL' 
,  'zip' => '60148' 
,  'country' => 'United States' 
,  'lat' => 41.880955 
,  'lng' => -88.012215 
  )
 


Array
  (

  'id' => '10438' 
,  'type' => 'Contractor' 
,  'company_name' => 'Along the Way' 
,  'address' => 'Address' 
,  'city' => 'Lombard' 
,  'state' => 'IL' 
,  'zip' => '60148' 
,  'country' => 'United States' 
,  'lat' => 41.849396 
,  'lng' => -88.021149 
)

Open in new window

Maybe so.  Is the structure of the data an array of sub-arrays (for want of a better term)?  The print_r() output would look like this:

Array
(
    [0] => Array
        (
            [id] => 10066
            [type] => Retail Store
            [company_name] => Joe's Place
            [address] => Joes Address
            [city] => Madison
            [state] => WI
            [zip] => 53718
            [country] => United States
            [lat] => 43.011803
            [lng] => -89.51313
        )

    [1] => Array
        (
            [id] => 10347
            [type] => Contractor
            [company_name] => Landscapes For You
            [address] => Address
            [city] => Carol Stream
            [state] => IL
            [zip] => 60188
            [country] => United States
            [lat] => 41.926319
            [lng] => -88.132423
        )

    [2] => Array
        (
            [id] => 66
            [type] => Contractor
            [company_name] => GP and Sons
            [address] => Address
            [city] => Roselle
            [state] => IL
            [zip] => 60172
            [country] => United States
            [lat] => 41.969658
            [lng] => -88.064316
        )

    [3] => Array
        (
            [id] => 10483
            [type] => Drop off
            [company_name] => VPS
            [address] => Address
            [city] => Gilberts
            [state] => IL
            [zip] => 60136
            [country] => United States
            [lat] => 42.100468
            [lng] => -88.366783
        )

    [4] => Array
        (
            [id] => 10625
            [type] => Drop off
            [company_name] => Design Group LLC
            [address] => Address
            [city] => Lombard
            [state] => IL
            [zip] => 60148
            [country] => United States
            [lat] => 41.894997
            [lng] => -88.015114
        )

    [5] => Array
        (
            [id] => 64
            [type] => Drop off
            [company_name] => Gardens Unlimited
            [address] => Address
            [city] => Lombard
            [state] => IL
            [zip] => 60148
            [country] => United States
            [lat] => 41.880955
            [lng] => -88.012215
        )

    [6] => Array
        (
            [id] => 10438
            [type] => Contractor
            [company_name] => Along the Way
            [address] => Address
            [city] => Lombard
            [state] => IL
            [zip] => 60148
            [country] => United States
            [lat] => 41.849396
            [lng] => -88.021149
        )

)

Open in new window

Please see: http://www.laprbass.com/RAY_temp_jej07.php

Please post back if any of this doesn't make sense, thanks.

<?php // RAY_temp_jej07.php
error_reporting(E_ALL);
echo '<pre>';


// SEE http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28348173.html


$distance_array = array
( '10066' => 5.3109370772026
, '10347' => 8.8816963223926
, '66'    => 12.837831771854
, '10483' => 12.878820381449
, '10625' => 15.002565483354
, '64'    => 15.29246611189
, '10438' => 15.388386254936
, '10327' => 19.261218858628
, '10461' => 22.650257356203
, '10243' => 26.26616586252
, '10246' => 32.440312706073
, '10422' => 45.859312064584
, '10529' => 61.744883818327
, '10397' => 67.358673239898
, '10632' => 67.437024249966
, '10547' => 74.253276511624
, '10572' => 74.93732142815
, '10240' => 97.381361623479
)
;

$curr_res = array
(
  array
( 'id'           => '10066'
, 'type'         => 'Retail Store'
, 'company_name' => "Joe's Place"
, 'address'      => 'Joes Address'
, 'city'         => 'Madison'
, 'state'        => 'WI'
, 'zip'          => '53718'
, 'country'      => 'United States'
, 'lat'          => 43.011803
, 'lng'          => -89.513130
)
,
  array
(  'id' => '10347'
,  'type' => 'Contractor'
,  'company_name' => 'Landscapes For You'
,  'address' => 'Address'
,  'city' => 'Carol Stream'
,  'state' => 'IL'
,  'zip' => '60188'
,  'country' => 'United States'
,  'lat' => 41.926319
,  'lng' => -88.132423
)
,
  array
(  'id' => '66'
,  'type' => 'Contractor'
,  'company_name' => 'GP and Sons'
,  'address' => 'Address'
,  'city' => 'Roselle'
,  'state' => 'IL'
,  'zip' => '60172'
,  'country' => 'United States'
, 'lat' => 41.969658
,  'lng' => -88.064316
)
,
  array
(  'id' => '10483'
,  'type' => 'Drop off'
,  'company_name' => 'VPS'
,  'address' => 'Address'
,  'city' => 'Gilberts'
,  'state' => 'IL'
,  'zip' => '60136'
,  'country' => 'United States'
,  'lat' => 42.100468
,  'lng' => -88.366783
)
,
  array
( 'id' => '10625'
,  'type' => 'Drop off'
,  'company_name' => 'Design Group LLC'
,  'address' => 'Address'
,  'city' => 'Lombard'
,  'state' => 'IL'
,  'zip' => '60148'
,  'country' => 'United States'
,  'lat' => 41.894997
,  'lng' => -88.015114
)
,
  array
(  'id' => '64'
,  'type' => 'Drop off'
,  'company_name' => 'Gardens Unlimited'
,  'address' => 'Address'
,  'city' => 'Lombard'
,  'state' => 'IL'
,  'zip' => '60148'
,  'country' => 'United States'
,  'lat' => 41.880955
,  'lng' => -88.012215
)
,
  array
(  'id' => '10438'
,  'type' => 'Contractor'
,  'company_name' => 'Along the Way'
,  'address' => 'Address'
,  'city' => 'Lombard'
,  'state' => 'IL'
,  'zip' => '60148'
,  'country' => 'United States'
,  'lat' => 41.849396
,  'lng' => -88.021149
)
)
;

// ACTIVATE THIS TO SEE IF THE INITIAL ARRAYS LOOK RIGHT
// var_dump($distance_array, $curr_res);


// CONFIGURE A NON-NATURAL LANGUAGE SORT ORDER
$order_keys = array
( 'Drop off'     => 10
, 'Retail Store' => 20
, 'Contractor'   => 30
)
;

// INJECT THE SORT ORDER KEYS INTO THE SUB-ARRAYS
foreach ($curr_res as $key => $sub)
{
    $sub['order'] = $order_keys[$sub['type']];
    $curr_res[$key] = $sub;
}

// USE PHP USORT() TO ARRANGE THE ARRAY ELEMENTS IN THE SORT ORDER
function my_order($a, $b)
{
    if ($a["order"] == $b["order"]) return 0;
    return ($a["order"] < $b["order"]) ? -1 : 1;
}
usort($curr_res, 'my_order');

// SHOW THE NEW ORDER OF THINGS
foreach ($curr_res as $sub)
{
    $distance = $distance_array[$sub['id']];
    echo PHP_EOL . $sub['type'];
    echo PHP_EOL . $sub['company_name'];
    echo PHP_EOL . number_format($distance,1) . ' Miles';
    echo PHP_EOL;
}

Open in new window

Best regards, ~Ray
Avatar of jej07

ASKER

Wow! I'm away from my computer and can't test it but I think you nailed it. I'll have to take a closer look at how you made it work. Based on the output I may also need to change the order within each group so the closest comes up first
The ordering process is done in the callback function for usort().  As written above it's only ordering according to the forced sort order.  It could be made to order within the forced sort order by adding the distance to the forced sort order (and of course making the forced sort order values into very large numbers).

The problem you may run into with this algorithm is directional.  If the only value you check within an order group is the distance, you could find the algorithm routing the driver 1 mile north(A), followed by 3 miles south(B), then 5 miles north(C), then 7 miles south(D).

1+3+5+7 = 16

But a better route would be 1 mile north(A), 2 miles north(C), 5 miles south(B), 2 miles south(D).

1+2+5+2 = 10

This is advanced mathematics.  It's commonly known as the Shortest Path Problem and it's one I encountered in organizing carpools.  My algorithm did not take account of the fact that two drivers each two miles away from a destination might be on opposite sides of the destination.  I took the easy way out and decided to ignore the problem.  My carpools were intended for very long distance trips intercity, so it was not a "real" problem in practice.  But I never implemented the algorithm that solved the problem.
Avatar of jej07

ASKER

I have not been able to get this to work yet, but it's my own fault. Looking back at my original code, and your post referring to sub-arrays, I realized I had made a mistake. I'm finding the distance $key first (eg. 10066), then using that to extract the correct $result key (eg. 10066), which becomes the $curr_res keys and values. So, I think what I actually need to do is order the $results array, based on the new $order_keys array, as done below. Then order that result based on the distance. Maybe by merging the distance_array with the results array?

Below, is what I'm using for testing. I changed some of the type values just to get a better idea how it was ordering each. I also included the correct structure of what's actually the $results array.

$distance_array = array
( '10066' => 5.3109370772026
, '10347' => 8.8816963223926
, '66'    => 12.837831771854
, '10483' => 12.878820381449
, '10625' => 15.002565483354
, '64'    => 15.29246611189
, '10438' => 15.388386254936
, '10000' => 13.388386254936
, '10001' => 20.388386254936
, '10002' => 23.388386254936
, '10003' => 2.3883862549365
)
;

$results = array
	( 
    '10066' => Array ( 
          'id'           => '10066'
        , 'type'         => 'Retail Store'
        , 'company_name' => "Joe's Place"
        , 'address'      => 'Joes Address'
        , 'city'         => 'Madison'
        , 'state'        => 'WI'
        , 'zip'          => '53718'
        , 'country'      => 'United States'
        , 'lat'          => 43.011803
        , 'lng'          => -89.513130
    )
,    
    '10347' => Array ( 
          'id' => '10347'
        ,  'type' => 'Contractor'
        ,  'company_name' => 'Landscapes For You'
        ,  'address' => 'Address'
        ,  'city' => 'Carol Stream'
        ,  'state' => 'IL'
        ,  'zip' => '60188'
        ,  'country' => 'United States'
        ,  'lat' => 41.926319
        ,  'lng' => -88.132423
    )
,    
    '66' => Array ( 
          'id' => '66'
        ,  'type' => 'Contractor'
        ,  'company_name' => 'GP and Sons'
        ,  'address' => 'Address'
        ,  'city' => 'Roselle'
        ,  'state' => 'IL'
        ,  'zip' => '60172'
        ,  'country' => 'United States'
        , 'lat' => 41.969658
        ,  'lng' => -88.064316
    )
,    
    '10483' => Array ( 
          'id' => '10483'
        ,  'type' => 'Contractor'
        ,  'company_name' => 'VPS'
        ,  'address' => 'Address'
        ,  'city' => 'Gilberts'
        ,  'state' => 'IL'
        ,  'zip' => '60136'
        ,  'country' => 'United States'
        ,  'lat' => 42.100468
        ,  'lng' => -88.366783
    )
,    
    '10625' => Array ( 
          'id' => '10625'
        ,  'type' => 'Contractor'
        ,  'company_name' => 'Design Group LLC'
        ,  'address' => 'Address'
        ,  'city' => 'Lombard'
        ,  'state' => 'IL'
        ,  'zip' => '60148'
        ,  'country' => 'United States'
        ,  'lat' => 41.894997
        ,  'lng' => -88.015114
    )
,    
    '64' => Array ( 
          'id' => '64'
        ,  'type' => 'Drop off'
        ,  'company_name' => 'Gardens Unlimited'
        ,  'address' => 'Address'
        ,  'city' => 'Lombard'
        ,  'state' => 'IL'
        ,  'zip' => '60148'
        ,  'country' => 'United States'
        ,  'lat' => 41.880955
        ,  'lng' => -88.012215
    )
,
    '10438' => Array ( 
          'id' => '10438'
        ,  'type' => 'Contractor'
        ,  'company_name' => 'Along the Way'
        ,  'address' => 'Address'
        ,  'city' => 'Lombard'
        ,  'state' => 'IL'
        ,  'zip' => '60148'
        ,  'country' => 'United States'
        ,  'lat' => 41.849396
        ,  'lng' => -88.021149
    )
    , 
    '10000' => Array ( 
          'id' => '10000'
        ,  'type' => 'Contractor'
        ,  'company_name' => 'Test'
    )
    , 	  
    '10001' => Array ( 
          'id' => '10001'
        ,  'type' => 'Drop off'
        ,  'company_name' => 'Test 1'
    )
    , 	  
    '10002' => Array ( 
          'id' => '10002'
        ,  'type' => 'Retail Store'
        ,  'company_name' => 'Test 2'
    )
    , 	  
    '10003' => Array ( 
          'id' => '10003'
        ,  'type' => 'Retail Store'
        ,  'company_name' => 'Test 3'
    )
    , 	  

)
;
/*
echo 'results array <br /><pre>';
print_r($results);
echo ' </pre><br /> end results array <br />';
*/
// CONFIGURE A NON-NATURAL LANGUAGE SORT ORDER
$order_keys = array ( 
	'Drop off'     => 10
	, 'Retail Store' => 20
	, 'Contractor'   => 30
	);
							
	// INJECT THE SORT ORDER KEYS INTO THE SUB-ARRAYS
	
	foreach ($results as $key => $sub)
	{
		$sub['order'] = $order_keys[$sub['type']];
		$results[$key] = $sub;
	}
	
	// USE PHP USORT() TO ARRANGE THE ARRAY ELEMENTS IN THE SORT ORDER
	function my_order($a, $b)
	{
		if ($a["order"] == $b["order"]) return 0;
		return ($a["order"] < $b["order"]) ? -1 : 1;
	}
	usort($results, 'my_order');
	
	
	// SHOW THE NEW ORDER OF THINGS
	foreach ($results as $sub)
	{
		$distance = $distance_array[$sub['id']];
		echo PHP_EOL . '<pre>';
		echo PHP_EOL . $sub['type'];
		echo PHP_EOL . $sub['company_name'];
		echo PHP_EOL . number_format($distance,1) . ' Miles';
		echo PHP_EOL;
		echo PHP_EOL . '</pre>';
	}
 

Open in new window


I would like to get it so the final output looks like this:


Drop off
Gardens Unlimited
15.3 Miles

Drop off
Test 1
20.4 Miles

Retail Store
Test 3
2.4 Miles

Retail Store
Joe's Place
5.3 Miles

Retail Store
Test 2
23.4 Miles

Contractor
Landscapes For You
8.9 Miles

Contractor
GP and Sons
12.8 Miles

Contractor
VPS
12.9 Miles

Contractor
Test
13.4 Miles

Contractor
Design Group LLC
15.0 Miles

Contractor
Along the Way
15.4 Miles
$order_keys array?  I don't follow.
Would I be on firm ground to say that you want to do all of the Drop Off first, Retail Store second and Contractor third?  And within each group you want to do them in ascending distance?
Avatar of jej07

ASKER

I'm sorry for making a mess of this, but I believe it's helping me to get a better grasp. The $order_keys array contains the unnatural sort order.

You're also correct in that it should be Drop Off, then Retail Store and finally Contractor. And each group should be sorted by ascending distance.
I believe that there are some caveats about usort() and preservation of the original order, so two consecutive sorts might not be the best thing.  I don't know this for sure, it's just a feeling I have. Example: I do not know if this user-contributed code is 100% dependable.
http://php.net/manual/en/function.usort.php#105764

So the strategy will be to inject distance into each sub-array, sort by distance, then distribute into the type arrays and merge the resulting arrays.  I'll see if this works as expected and post back in a little while.
ASKER CERTIFIED SOLUTION
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America 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
Avatar of jej07

ASKER

That's it!!! Ray, I wish I could buy you a drink, because 500 points just doesn't seem like enough.

The data should be clean, with very little opportunity for duplicates. However, I will keep in mind the fact that key could cause problems.

Thank you so much for your help!
Ha!  Thanks for the points and thanks for using EE.  All the best, ~Ray