Multi-sorting a complex array in PHP - php

I have a relatively complex array in PHP that needs to be sorted in a specific way but the number of filters and sorts is a bit on the complex side that a simple solution seems to be escaping me.
The Problem: We have products in an order that need to be filtered into "shipto" groups based on the following criteria: Date the product can be shipped, if the product can be grouped with other products, and in which warehouse(s) the product can be shipped from. If 2 products have the same date, warehouse, and allow grouping then they are placed together in the same "shipto".
Current Solution (Not fully realized)
When I extract an order I traverse the items in it and build an array with the following information (FYI, this probably contains more info than I need to solve this problem)
$allProducts = array();
$today = date("Ymd");
/* If the product is being shipped now, put in the Today ShipTo dated 00000000 */
if (($today > $start) && ($today < $end)) {
array_push($allProducts, array('ship' => '00000000', 'defaultwarehouse' => $defaultwarehouse, 'warehouse' => $warehouse, 'group' => $group,'product' => $product, 'start_month' => $start_month, 'start_day' => $start_day, 'end_month' => $end_month, 'end_day' => $end_day, 'description' => $description, 'qty_ordered' => $qty_ordered, 'row_total' => $row_total, 'price' => $price, 'tax_amount' => $tax_amount, 'shipping_group' => $shippingGroup, 'today' => $today, 'end' => $end, 'start' => $start ) );
} else {
/* If the product is not being shipped today, put in a Group that matches the starts date for the ShipTo */
array_push($allProducts, array('ship' => $start, 'defaultwarehouse' => $defaultwarehouse, 'warehouse' => $warehouse, 'group' => $group, 'product' => $product, 'start_month' => $start_month, 'start_day' => $start_day, 'end_month' => $end_month, 'end_day' => $end_day, 'description' => $description, 'qty_ordered' => $qty_ordered, 'row_total' => $row_total, 'price' => $price, 'tax_amount' => $tax_amount, 'shipping_group' => $shippingGroup, 'today' => $today, 'end' => $end, 'start' => $start ));
}
endif;
Where $defaultwarehouse is a number representing the default warehouse (i.e. 1), $warehouse is all of the possible warehouses a product can be in (i.e. 1,2), $start is the date a product can first be shipped (i.e. 20121201 for Dec 1, 2012; Ymd, 00000000 represents today so they can be numerically sorted) and $group is a boolean saying if the product can or can not be shipped with other items (i.e. 1 for yes, 0 for no)
My original idea here was to sort the array based on group, then on warehouse, then on start date. I would then traverse the array to build the "shipto"s and when certain components of the array change (like Warehouse or Start) I would close the previous shipto and start a new one. If I take Group and Multiple Warehouses out of the equation I can get this logic to work. But my task requires I have them in so I am running into the following complications.
First, after I sort by Group, I really want to do 2 sub-sorts of Group=Yes and Group=No for the rest of the filters. This lead me to think that maybe I should be separating the array into 2 arrays and sorting them similarly but separately. It seems inefficient to do it this way but I am not sure.
Second, since warehouse can be a comma separated value, how can I filter on that and get the proper matches. i.e. if I have 3 products where their warehouse value is 1:1,2:3 this should group into 2 shiptos. One for 1:1,2 and a Second for 3.
Thoughts on the problem
My feeling is the way I am thinking about this problem with filtering the items into a traversable array may not be the best way to tackle it. It may be that I need to push the order items individually into a new "Shipments" array that checks each item against all current "Shipments". But I am also not exactly sure on how that would work. Or there may be a different way I am not thinking about at all about how to achieve this.
Example Array Data (Extra Data removed for simplicity):
[0]=>
'ship' => 00000000
'defaultwarehouse' => 1
'warehouse' => 1
'group' => 1
'sku' => 'ABC123'
[1]=>
'ship' => 00000000
'defaultwarehouse' => 1
'warehouse' => 1,2
'group' => 1
'sku' => 'DEF234'
[2]=>
'ship' => 00000000
'defaultwarehouse' => 2
'warehouse' => 1,2
'group' => 1
'sku' => 'GHI567'
[3]=>
'ship' => 20121220
'defaultwarehouse' => 1
'warehouse' => 1,2
'group' => 1
'sku' => 'JKL890'
[4]=>
'ship' => 20121220
'defaultwarehouse' => 1
'warehouse' => 1,2
'group' => 1
'sku' => 'MNO123'
[5]=>
'ship' => 20130401
'defaultwarehouse' => 1
'warehouse' => 1
'group' => 1
'sku' => 'PQR456'
[6]=>
'ship' => 20130401
'defaultwarehouse' => 1
'warehouse' => 1
'group' => 0
'sku' => 'STU789'
This should result in 5 "shipto" groups:
shipto[1] => ABC123, DEF234 (Base "group" for all other comparisons)
shipto[2] => GHI567 (Default warehouse does not match, previous shiptos)
shipto[3] => JKL890, MNO123 (Different shipping date)
shipto[4] => PRQ456 (Different shipping date from all others)
shipto[5] => STU789 (Can not be grouped with other shipments)

Is it possible for you to store the data in a database or somewhere similar? It sounds like your data is pretty complex, and storing it in a database would make the sorting you're looking to do a lot easier.

In the end this problem got even more complex and I generally threw away the concept of being able to sort logically and applying sub-sorts to a single array or SQL query. The business logic changed so much that keeping this all straight made no sense in one dimension.
The final logic applies 4 levels of filtering so what I did was isolate each step of logic and filter the previous array into a new array with the orders in their proper place. It is NOT the most efficient way to handle this but it is very readable and allows me to clearly see how each order is getting sorted at each step of the process. The "sorted" array is 4 levels deep but allows me to cherry pick each shipment for proper display.

Related

Parsing information from PHP Object

I need some help trying to parse informaiton ti display in a form. Right now when using the basic array an address is returned as "3 Columbus PlaceApt1" where Address 1 and Address 2 are returned joined together. I created an object that returns this with print_r
a:2:{
s:18:"_gravity_form_lead";a:16:{
s:4:"19.1";s:28:"Stamford PD Extra Duty Order";s:4:"19.2";s:5:"$0.00";s:4:"19.3";s:0:"";s:3:"9.1";s:16:"3 Columbus Place";s:3:"9.2";s:5:"Apt 1";s:3:"9.3";s:0:"";s:3:"9.4";s:11:"Connecticut";s:3:"9.5";s:0:"";s:3:"9.6";s:13:"United States";i:22;s:14:"(203) 123-4567";i:12;s:10:"2016-05-24";i:13;s:8:"12:35 am";i:20;s:8:"8|584.64";i:11;s:9:"MH6151684";i:21;s:1:"3";i:18;s:7:"1753.92";}s:18:"_gravity_form_data";a:15:{s:2:"id";s:1:"6";s:13:"display_title";b:0;s:19:"display_description";b:0;s:25:"disable_woocommerce_price";s:3:"yes";s:12:"price_before";s:0:"";s:11:"price_after";s:0:"";s:20:"disable_calculations";s:2:"no";s:22:"disable_label_subtotal";s:3:"yes";s:21:"disable_label_options";s:3:"yes";s:19:"disable_label_total";s:2:"no";s:14:"disable_anchor";s:2:"no";s:14:"label_subtotal";s:8:"Subtotal";s:13:"label_options";s:7:"Options";s:11:"label_total";s:5:"Total";s:8:"use_ajax";s:2:"no";
}
}
I want to parse our s:16 and s:5 but im not sure how to do it. Any help is appreciated
EDIT
Unserialized string:
array ( '_gravity_form_lead' => array ( '19.1' => 'Stamford PD Extra Duty Order', '19.2' => '$0.00', '19.3' => '', '9.1' => '3 Columbus Place', '9.2' => 'Apt 1', '9.3' => '', '9.4' => 'Connecticut', '9.5' => '', '9.6' => 'United States', 22 => '(203) 123-4567', 12 => '2016-05-24', 13 => '12:35 am', 20 => '8|584.64', 11 => 'MH6151684', 21 => '3', 18 => '1753.92', ), '_gravity_form_data' => array ( 'id' => '6', 'display_title' => false, 'display_description' => false, 'disable_woocommerce_price' => 'yes', 'price_before' => '', 'price_after' => '', 'disable_calculations' => 'no', 'disable_label_subtotal' => 'yes', 'disable_label_options' => 'yes', 'disable_label_total' => 'no', 'disable_anchor' => 'no', 'label_subtotal' => 'Subtotal', 'label_options' => 'Options', 'label_total' => 'Total', 'use_ajax' => 'no', ), )
You're doing this the hard way!
This is WordPress. You don't need to get a record, unserialize it, try and guess at the structure, etc.
Instead, use the get_option or get_postmeta functions to get the information you want.
Further, there's documentation for both WooCommerce and Gravity Forms that will explain how to get the information you're looking for - BUT, this is a bit of an XY Problem - you've asked how to parse an array (Serialized) - but REALLY you are trying to do something else. You should ask THAT question, rather than the details of this question.
If you must do this manually, do not use unserialize. Instead, use the WordPress function maybe_unserialize
Lastly, the way Gravity Forms stores records is a major nuisance. It's very difficult to map the way it is stored to the "form field" that you set up in the form - unless you use the Gravity Forms API, which makes it somewhat better.
You have to unserialize the array to get the data you need. Like this:
$formData = unserialize($var);
// This is Address1
echo $formData['_gravity_form_lead']['9.1'];
echo "<br/>";
// This is Address2
echo $formData['_gravity_form_lead']['9.2'];

Pull count of records in a table that have the same value with Codeigniter Active Record

I am working on a site where I have a table with "catches" (people have caught fish with a particular lure). The lure_used category is an integer.
I want to find the users "best lure". So i want to loop through all the user_catches rows and return an array that has each lure and how many times it was used.
So for example we might have
catch_1 | lure_used = 5,
catch_2 | lure_used = 3,
catch_3 | lure_used = 6,
catch_4 | lure_used = 3,
so from that i would like to know that 5 and 6 occur 1 time while 3 occurs twice. Therefore 3 is the most successful lure.
I know that I could probably use "MAX" in mysql queries, but i'm not really farmiliar with it and attempts have failed.
I have been able to create an array of lures used like:
$lures = array(5, 3, 6, 3);
So maybe i could just loop through that and output something like...
5 = 1, 3 = 2, 6 = 1.
I guess i'm grasping at straws haha, anyone have a better idea on how to get this done?
Here are all the fields in the "user_catches" table:
'id' => $row->id,
'user_id' => $row->user_id,
'species' => $row->species,
'season' => $row->season,
'lure_used' => $row->lure_used,
'depth' => $row->depth,
'exact_depth' => $row->exact_depth,
'presentation' => $row->presentation,
'catch_weight' => $row->catch_weight,
'length' => $row->length,
'fishing_from' => $row->fishing_from,
'moon_phase' => $row->moon_phase,
'water_temp' => $row->water_temp,
'water_quality' => $row->water_quality,
'notes' => $row->notes,
'rod' => $row->rod,
'reel' => $row->reel,
'line' => $row->line,
'rig' => $row->rig,
'catch_image' => $row->catch_image,
'weather' => $row->weather,
'temp' => $row->temp,
'humidity' => $row->humidity,
'wind' => $row->wind,
'pressure' => $row->pressure,
'visibility' => $row->visibility,
'location' => $row->location,
'weather_icon' => $row->weather_icon,
'catch_date' => $row->catch_date,
SELECT lure, count (*) as count
from user_catches
GROUP BY lures
ORDER BY count;

Sorting PHP Array by ISO Date

I am trying to sort an array in PHP by date and time which is in ISO 8601 format. I am still trying to grasp PHP and have tried many of the solutions on stack overflow and I am just not able to nail down the right function. Hopefully this is an easy answer and it will be helpful to others.
FYI, this array was generated by the Citrix API for GoToMeeting. I would like to sort the array based on startTime in the soonest time first in the list.
Here is what the array looks like using var_export with two results presented:
array (
0 => stdClass::__set_state(
array(
'createTime' => '2012-07-03T19:36:58.+0000',
'status' => 'INACTIVE',
'subject' => 'Client 1',
'startTime' => '2012-07-10T14:00:00.+0000',
'conferenceCallInfo' => 'United States: xxxxx Access Code: xxxxx',
'passwordRequired' => 'false',
'meetingType' => 'Scheduled',
'maxParticipants' => 26,
'endTime' => '2012-07-10T15:00:00.+0000',
'uniqueMeetingId' => 12345678,
'meetingid' => 123456789,
)
),
1 => stdClass::__set_state(
array(
'createTime' => '2012-07-02T21:57:48.+0000',
'status' => 'INACTIVE',
'subject' => 'Client 2',
'startTime' => '2012-07-06T19:00:00.+0000',
'conferenceCallInfo' => 'United States: xxxxx Access Code: xxxxx',
'passwordRequired' => 'false',
'meetingType' => 'Scheduled',
'maxParticipants' => 26,
'endTime' => '2012-07-06T20:00:00.+0000',
'uniqueMeetingId' => 12345678,
'meetingid' => 123456789,
)
),
)
My goal is to then output the array into html div's using a foreach loop, this code is complete and works well but my sort is off :-)
Thank you in advance for any help!
Steve
You can implement any sorting technique you can think of if you wrap it in a callback and use usort() docs here
inside your callback, you can use strtotime or similar, and do simple int comparisons.
$myDateSort = function($obj1, $obj2) {
$date1 = strtotime($obj1->startTime);
$date2 = strtotime($obj2->startTime);
return $date1 - $date2; // if date1 is earlier, this will be negative
}
usort($myArray, $myDateSort);

Retrieve Child Objects

I want a table of comments like so
id | comment | parent_id
--------------------------
1 text1 0
2 text2 1
3 text3 2
4 text4 3
5 text5 3
6 text6 5
I want to construct an array displaying the hierarchy of the parents and children. The tree should go back a undetermined number of generations. I don't want to use nesting foreach loops as I'm not sure how deep it goes. That is why I'm here, I'm not sure of the best practice for a problem like this. I also want to display the depth in the array. Below is an example. It doesn't really relate to table above, but hopefully gives you an idea of what I need.
array(
"depth"=> 4
"parent" => array(
"id"=> 1,
"comment" => "sometext1"
"child_count" => 2,
"children" => array(
0 => array(
"id" => 2
"comment" => "sometext2",
"child_count" => 0,
"children" => null
),
1 => array(
"id" => 3
"comment" => "sometext3"
"child_count" => 1,
"children" => array(
0 => array(
"id" => 2
"comment" => "sometext2",
"child_count" => 2,
"children" => array(
0 => array(
"id" => 2
"comment" => "sometext2",
"child_count" => 0,
"children" => null
),
1 => array(
"id" => 2
"comment" => "sometext2",
"child_count" => 1,
"children" => array(
"id" => 2
"comment" => "sometext2",
"child_count" => 0,
"children" => null
)
)
)
)
)
)
)
)
I was going to use foreach and do a SQL statement to retrive that parent/childs children. ie
$sql = "SELECT * FROM comments WHERE parent = $parent_id";
Im not really looking for the code for all this, just a pseudo code solution.
This can be easily done in PHP... For this you need two arrays and a two while loops.
This code will make a tree the way you wanted and for an undetermined depth and number of children.
Pastebin to the working code.
Using references, lets imagine everything is saved in an array $data with this structure: (id, comment, parent_id) where parent_id points to an id.
Code to build the tree.
$tree = array();
reset($data);
while (list($k, $v) = each($data))
if (0 == ($pid = $v['parent_id']))
$tree[$k] =& $data[$k]; else
$data[$pid]['children'][$k] =& $data[$k];
And to generate the depth and child count.
reset($data);
while (list($k, $v) = each($data))
if (0 != $v['parent_id'])
{
$ref =& $data[$k];
$depth = 0;
do
{
if ($depth) $ref =& $data[$ref['parent_id']];
$dre =& $ref['depth'];
if (!isset($dre) || $dre <= $depth) $dre = $depth++;
if (isset($ref['children']))
$ref['child_count'] = count($ref['children']);
else
{
$ref['child_count'] = 0;
$ref['children'] = null;
}
}
while ($ref['parent_id']);
}
All my code has been written on the fly and not even tested, so if there are any errors please forgive meeeeeeeee!!!!!!!!!!! ← Forget that, I tried it, fixed a couple of issues and now works perfectly.
Note
For this code to work, the index of every item has to be equal to its ID.
The array I used to try the code.
$data = array(
'1' => array('id' => '1', 'comment' => 'a', 'parent_id' => 0),
'2' => array('id' => '2', 'comment' => 'b', 'parent_id' => 0),
'3' => array('id' => '3', 'comment' => 'c', 'parent_id' => 1),
'4' => array('id' => '4', 'comment' => 'd', 'parent_id' => 1),
'5' => array('id' => '5', 'comment' => 'e', 'parent_id' => 2),
'6' => array('id' => '6', 'comment' => 'f', 'parent_id' => 2),
'7' => array('id' => '7', 'comment' => 'g', 'parent_id' => 5),
'8' => array('id' => '8', 'comment' => 'h', 'parent_id' => 7)
);
This is the problem when you use Adjacency list for trying to retrieve all child nodes in the hierarchy. It just doesn'y handle recursion very well if you are using mysql. (Oracle is another matter).
Creating the structure is simple, you should not really concern yourself with how to create the array structure just yet, first you want to try and create an efficient query and effiecient models that play perfectly to the type of queries that you will be making.
For example, you say that you want to retrieve all child nodes. Well then you should probably be using nested set models instead or in addition to adjacency list.
Take a look at some of these resources...
Is there a simple way to query the children of a node?
The idea of a nested set, is that you store the lft and right edge values of a node, meaning that retrieving any child nodes, is incredibly simple, beause you just select nodes which have a lft value greater than the target nodes lft value, and smaller than the rgt value.
Once you retrieve your result set, creating your array structure will be effortless.
See here : http://en.wikipedia.org/wiki/Nested_set_model
Once you have your results, then take a look at this question, which I asked a year or so ago, which is exactly what you want. PHP > Form a multi-dimensional array from a nested set model flat array
Example
id | comment | parent_id | lft | rgt |
-------------------------------------------------
1 World null 1 12
2 Europe 1 2 11
3 England 2 3 10
4 Kent 3 4 5
5 Devon 3 6 9
6 Plymouth 5 7 8

PHP: How will array depth influence performance?

Now I know there is some related questions on this topic but this is somewhat unique.
I have two array structures :
array(
[0] => array(
'stat1' => 50,
'stat2' => 12,
'stat3' => 0,
'country_name' => 'United States'
),
[1] => array(
'stat1' => 40,
'stat2' => 38,
'stat3' => 15,
'country_name' => 'Ireland'
),
[2] => array(
'stat1' => 108,
'stat2' => 0,
'stat3' => 122,
'country_name' => 'Autralia'
)
)
and the second
array(
'stat1' => array(
'countries' => array(
'United States' => 50,
'Ireland' => 40,
'Autralia' => 108,
)
)
),
'stat2' => array(
'countries' => array(
'United States' => 12,
'Ireland' => 38,
)
)
),
etc...
The second array can go even to level 4 or 5 if you add the cities of those respective countries. Further to note is that the second array structure will have no 0 data fields (note that in the second one australia is not there because it is 0) but the first structure will have a whole whack of zeros. Also note that the second structure will have duplicates i.e. 'United States'
My question is this: How does these array structures compare when they are json_encode() and used in a POST ajax request? Will the shallow depth array, with it's whack of zeros be faster or will the better structured array be better in terms of size?
I have done some testing and for a finite dataset the difference in the output data - (I outputted the data into a text file) between the two is insignificant really.
Array structure 1 - All city and country data outputs to 68kb
Array structure 2 - All city and country data outputs to 71kb
So there is a slight difference but it seems that the difference is insignificantly small when taking into account that the data is in JSON format and used in an AJAX request to the google visualization geomap API.
I haven't tested the micro times in loading difference but for a user to look at a loading .gif image for 0.0024microseconds (i'm shooting a random time for the sake of argument) does not make a big dent in usability either way. Thanx all for you comments

Categories