Create dynamic sub array from a big one [duplicate] - php

This question already has answers here:
PHP: How to split array into 2 parts?
(6 answers)
Closed 10 months ago.
I have an array, made by using explode and the delimiter #. I'm trying without success to create from this big array in PHP subarrays (the number of subarrays is not fixed and could vary). The goal is to make every 10 # a new sub array and store in all the values between 0 - 9, 10 - 19, etc...
Here my big array where the PHP has to work on : the ( ) are just comment to be more clear, in the code there is only #
#ACT/A (line 1)
#XXX (2)
#2 (3)
#51,6844 (4)
#50,7000 (5)
#101,40 (6)
#-1,97 (7)
#-1,91 (8)
#-0,61 (9)
#3,34 (10)
#ACT/B (11
#X
#4
#68,86750
#63,2700
#253,08
#-22,39
#-8,13
#-0,41
#8,27 (line 20)
#ACT/C
#X
#15
#10,33132
#4,18
#62,70
#-92,27
#-59,54
#0,00
#2,03
My PHP code (which is not working) :
$start = 1;
$equities = explode("#", $allEquities); // split the big array here
//$howManyEquities is a number between 1 and X, only int, not float)
while($start <= $howManyEquities) // doing the loop for each equities counted (could vary from 1 to x)
{
$array[$howManyEquities] = $equities[0]; // trying to store in a array the result (line 1 from the example above, and then line 11, etc...)
$equities = $equities[$start * 10]; // trying to prepare next loop, start (1) * 10 = 11 to catch next time result from line 11
$start++;
}
To sum up, I'm probably very not clear and I apologize. Here an example of the dynamic array I want from the code (I tried foreach loop but didn't seem to work) :
BigArray (the number of key inside vary according to the number of equity) = (
subArray1 = (ACT/A, XXX, 2, 51,6844, etc from line 1 to 10)
subArray2 = (ACT/B, X, 68,86750, etc from line 11 to 20)
subArray3 = (ACT/C, etc)
subArrayX = (ACT/X, etc)
It could be resumed by every ten first values inside a first array, the next ten in another array, and so on until we cover all the big array (that's why I tried $start * 10 in my code). I have to precise that if $howManyEquities = 7 by example, there will be 70 #, if = 5 there will be 50 # and so on.
EDIT : Solution thanks to #user3783243
while($start <= $howManyEquities)
{
$newArray = array_chunk($equities, 10);
$start++;
}
Don't hesitate if you need more information, thanks for reading and enjoy week-end !
Respectfully

As #user3783243 said, array_chunk does the job.
Source string:
$string = '#ACT/A (line 1)
#XXX (2)
#2 (3)
#51,6844 (4)
#50,7000 (5)
#101,40 (6)
#-1,97 (7)
#-1,91 (8)
#-0,61 (9)
#3,34 (10)
#ACT/B (11
#X
#4
#68,86750
#63,2700
#253,08
#-22,39
#-8,13
#-0,41
#8,27 (line 20)
#ACT/C
#X
#15
#10,33132
#4,18
#62,70
#-92,27
#-59,54
#0,00
#2,03';
The code:
// Explode.
$array = explode('#', $string);
// Should trim all values to f.e. remove new lines.
$array = array_map('trim', $array);
// Should filter empty values (due to empty lines in string).
$array = array_filter($array, 'strlen');
// Split into chunks.
$array = array_chunk($array, 10, true);
Output:
echo var_export($array, true) . PHP_EOL;
// [
// 0 => [
// 1 => 'ACT/A (line 1)',
// 2 => 'XXX (2)',
// 3 => '2 (3)',
// 4 => '51,6844 (4)',
// 5 => '50,7000 (5)',
// 6 => '101,40 (6)',
// 7 => '-1,97 (7)',
// 8 => '-1,91 (8)',
// 9 => '-0,61 (9)',
// 10 => '3,34 (10)',
// ],
// 1 => [
// 11 => 'ACT/B (11',
// 12 => 'X',
// 13 => '4',
// 14 => '68,86750',
// 15 => '63,2700',
// 16 => '253,08',
// 17 => '-22,39',
// 18 => '-8,13',
// 19 => '-0,41',
// 20 => '8,27 (line 20)',
// ],
// 2 => [
// 21 => 'ACT/C',
// 22 => 'X',
// 23 => '15',
// 24 => '10,33132',
// 25 => '4,18',
// 26 => '62,70',
// 27 => '-92,27',
// 28 => '-59,54',
// 29 => '0,00',
// 30 => '2,03',
// ],
// ]

Related

Algorithm to find best combination of numbers - Bin Packing Prob lem

Say I have the following measures:
80
180
200
240
410
50
110
I can store each combination of numbers to a maximum of 480 per unit. How can I calculate the least number units required so all measures are spread in the most efficient way?
I've tagged PHP but it can be in JS too, or even pseudo language.
I know I'm supposed to tell what I did already but I'm quite stuck on how to approach this. The first thing that comes to mind is recursion but I'm no math expert to see how this can be done efficient...
Any help is greatly appreciated.
To further elaborate: I'm trying to calculate the number of skirtings I have to order, based on the different lengths I need for the walls. Each skirting has a length of 480cm and I want to know the best way to spread them so I have to buy the least number of skirtings. It's not so much about ordering a skirting extra, but the puzzle to figure it out is an interesting one (at least to me)
Update with solution
Despite people trying to close the question I've started fiddling with the Bin Packing Problem description and following the idea of sorting all items from largest to smallest and then fit them in the best possible way I created this small class that might help others in the future:
<?php
class BinPacker {
private $binSize;
public function __construct($binSize) {
$this->binSize = $binSize;
}
public function pack($elements) {
arsort($elements);
$bins = [];
$handled = [];
while(count($handled) < count($elements)) {
$bin = [];
foreach($elements as $label => $size) {
if(!in_array($label, $handled)) {
if(array_sum($bin) + $size < $this->binSize) {
$bin[$label] = $size;
$handled[] = $label;
}
}
}
$bins[] = $bin;
}
return $bins;
}
public function getMeta($bins) {
$meta = [
'totalValue' => 0,
'totalWaste' => 0,
'totalBins' => count($bins),
'efficiency' => 0,
'valuePerBin' => [],
'wastePerBin' => []
];
foreach($bins as $bin) {
$value = array_sum($bin);
$binWaste = $this->binSize - $value;
$meta['totalValue'] += $value;
$meta['totalWaste'] += $binWaste;
$meta['wastePerBin'][] = $binWaste;
$meta['valuePerBin'][] = $value;
}
$meta['efficiency'] = round((1 - $meta['totalWaste'] / $meta['totalValue']) * 100, 3);
return $meta;
}
}
$test = [
'Wall A' => 420,
'Wall B' => 120,
'Wall C' => 80,
'Wall D' => 114,
'Wall E' => 375,
'Wall F' => 90
];
$binPacker = new BinPacker(488);
$bins = $binPacker->pack($test);
echo '<h2>Meta:</h2>';
var_dump($binPacker->getMeta($bins));
echo '<h2>Bin Configuration</h2>';
var_dump($bins);
Which gives an output:
Meta:
array (size=6)
'totalValue' => int 1199
'totalWaste' => int 265
'totalBins' => int 3
'efficiency' => float 77.898
'valuePerBin' =>
array (size=3)
0 => int 420
1 => int 465
2 => int 314
'wastePerBin' =>
array (size=3)
0 => int 68
1 => int 23
2 => int 174
Bin Configuration
array (size=3)
0 =>
array (size=1)
'Wall A' => int 420
1 =>
array (size=2)
'Wall E' => int 375
'Wall F' => int 90
2 =>
array (size=3)
'Wall B' => int 120
'Wall D' => int 114
'Wall C' => int 80
While the data set is relatively small a rather high inefficiency rate is met. But in my own configuration where I entered all wall and ceiling measures I've reached an efficiency of 94.212% (n=129 measures).
(Note: the class does not check for ambigious labels, so if for example you define Wall A twice the result will be incorrect.)
Conclusion: for both the ceiling and the wall skirtings I can order one less skirting than my manual attempt to spread them efficiently.
Looks to me like a variation on the Bin Packing Problem where you're trying to pick the combination of elements that make up 480 (or just under). This is a fairly computationally hard problem and depending on how efficient/accurate it needs to be, might be overkill trying to get it exact.
A rough heuristic could be just to sort the measures, keep adding the smallest ones into a unit until the next one makes you go over, then add to a new unit and repeat.

PHP combinations using all words each time [duplicate]

This question already has answers here:
Combinations Of String While Maintaining Order Of Words
(3 answers)
Closed 8 years ago.
This is my first question at this site, so I hope that I will be specific enough with this.
I need to transform a text string into several array with all different combinations of the 'words' and 'word phrases' in the text string.
So string would be like:
"Football match France 2013"
From this I want the following array:
array(
0 => array(
'Football',
'match',
'France',
'2013'
),
1 => array(
'Football',
'match',
'France 2013'
),
2 => array(
'Football',
'match France',
'2013'
),
3 => array(
'Football',
'match France 2013'
),
4 => array(
'Football match',
'France',
'2013'
),
5 => array(
'Football match',
'France 2013',
),
6 => array(
'Football match France',
'2013'
),
7 => array(
'Football match France 2013',
),
)
So the restriction that a each result string string may consist of 1 to n consecutive words and that in total each sub array should contain each word one time.
Here is some code that works.
<?php
$str = 'Football match France 2013'; // Initialize sentence
$words = explode(" ",$str); // Break sentence into words
$p = array(array(array_shift($words))); // Load first word into permutation that has nothing to connect to
foreach($words as $word) { // for each remaining word
$a = $p; // copy existing permutation for not-connected set
$b = $p; // copy existing permutation for connected set
$s = count($p); // cache number of items in permutation
$p = array(); // reset permutation (attempt to force garbage collection before adding words)
for($i=0;$i<$s;$i++) { // loop through each item
$a[$i][] = $word; // add word (not-connected)
$b[$i][count($b[$i])-1] .= " ".$word; // add word (connected)
}
$p = array_merge($a,$b); // create permutation result by joining connected and not-connected sets
}
// Dump the array
print_r($p);
?>

Finding maximum path of a complex array

I have an array:
$data = array(
1 => array(
"time" => 1,
"parent" => array(4)
),
2 => array(
"time" => 3,
"parent" => array(4, 5)
),
3 => array(
"time" => 2,
"parent" => array(6)
),
4 => array(
"time" => 1,
"parent" => array(6)
),
5 => array(
"time" => 1,
"parent" => array(4)
),
6 => array(
"time" => 1,
"parent" => array()
)
);
Key is the ID of an element, parent is an array of elements, which refers to element id and time is just an integer.
This is an illustrated example of a given array:
Schema
The integer on the bottom-left is the "id" and the integer in the middle is "time".
My goal here is find the most time-consuming path of this array. In the given example, the path would be 2->5->4->6 (id wise) resulting in 6 "time" overall. It looks pretty easy on paper, however I can't really seem to code an algorythm to get the elements of the most time-consuming path. I would appreciate any kind of help.
I think the algorythm should be kind of bruteforce-ish and check through all of the options available. Thus with the given array it would go like:
1 -> 4 -> 6 = 3
2 -> 4 -> 6 = 5
2 -> 5 -> 4 -> 6 = 6
3 -> 6 = 3
4 -> 6 = 2
5 -> 4 -> 6 = 3
Thanks in advance.
Note that this will only work if there are no loops in the array.
// Note: built this in the SO editor, might have bugs
$cached = [];
$arrays = []; // Do this yourself
function get_path($num) {
global $arrays, $cached;
if (isset($cached[$num])) return $cached[$num];
$array = $arrays[$num];
$maxtime = $array['time'];
$bestpath = array($num);
foreach ($array['parent'] as $i) {
$path = get_path($i);
if ($path['time']+$array['time'] > $maxtime) {
$maxtime = $path['time'] + $array['time'];
$bestpath = array_merge(array($num),$path['path']);
}
}
$cached[$num] = array('path' => $bestpath, 'time' => $maxtime);
return $cached[$num];
}
var_dump(get_path(5));
Not really a bruteforce way, should be close enough to O(n). The basic idea is that you just cache the paths it can take.
Note: I used a bit of C-style syntax here, but ideally you wouldn't actually write the code like this

PHP/mySQL: Import data and store in hierarchical nested set for use with jsTree

I'm using jsTree to view hierarchical data that is stored in a mySQL database as a nested set (left, right, level, etc.). This is working fine, but I need to allow users to import data by uploading a CSV file. When they do so, any existing data in the table will be removed so I don't have to worry about updating the left/right fields.
The data they will be uploading will be in this format:
"Code","Title"
"100","Unit 100"
"200","Unit 200"
"101","Task 101: This is a task"
"102","Task 102: Another task"
"201","Task 201: Yet another"
"300","Unit 300"
"301","Task 301: Another one"
Everything will be a child of a main "Group" that is a level 1 node. All of the "codes" divisible by 100 (ie. 100, 200, 300) will be level 2 (parent nodes.. children of "Group"). All others will be level 3 (child) nodes of their respective parent nodes (ie. 101 and 102 are children of 100, 201 is a child of 200, etc.)
The resulting table in mySQL should look like this:
id parent_id position left right level title
1 0 0 1 18 0 ROOT
2 1 0 2 17 1 Group
3 2 0 3 8 2 Unit 100
4 2 1 9 12 2 Unit 200
5 3 0 4 5 3 Task 101: This is a task
6 3 1 6 7 3 Task 102: Another task
7 4 0 10 11 3 Task 201: Yet another
8 2 2 13 16 2 Unit 300
9 8 0 14 15 3 Task 301: Another one
The tree would then look like this:
My question is: using PHP, what is the best method to accomplish this? I already have code in place that pulls the data contained in the uploaded CSV file and stores it in an array, but I'm not sure what the logic to convert this to a nested set should look like.
Right now, the data is stored in a 2-dimensional array called $data (in the format $data[$col][$row]):
$data[0][0] = "Code";
$data[0][1] = "100";
$data[0][2] = "200";
$data[0][3] = "101";
$data[0][4] = "102";
$data[0][5] = "201";
$data[0][6] = "300";
$data[0][7] = "301";
$data[1][0] = "Title";
$data[1][1] = "Unit 100";
$data[1][2] = "Unit 200";
$data[1][3] = "Task 101: This is a task";
$data[1][4] = "Task 102: Another task";
$data[1][5] = "Task 201: Yet another";
$data[1][6] = "Unit 300";
$data[1][7] = "Task 301: Another one";
Array ( [0] => Array ( [0] => Code [1] => 100 [2] => 200 [3] => 101 [4] => 102 [5] => 201 [6] => 300 [7] => 301 ) [1] => Array ( [0] => Title [1] => Unit 100 [2] => Unit 200 [3] => Task 101: This is a task [4] => Task 102: Another task [5] => Task 201: Yet another [6] => Unit 300 [7] => Task 301: Another one ) )
Any help would be very much appreciated. I now have the parent_id, position, and level being calculated correctly... I just need to figure out the left/right part. Here is the code I'm currently using (thanks for getting me started Matteo):
$rows = array();
// insert ROOT row
$rows[] = array(
'id' => 1,
'parent_id' => 0,
'position' => 0,
'left' => 1,
'right' => 10000, // just a guess, will need updated later
'level' => 0,
'title' => 'ROOT',
);
echo "<br>";
print_r($rows[0]);
// insert group row
$rows[] = array(
'id' => 2,
'parent_id' => 1,
'position' => 0,
'left' => 2,
'right' => 9999, // just a guess, will need updated later
'level' => 1,
'title' => 'Group',
);
echo "<br>";
print_r($rows[1]);
// next ID to be used
$id = 3;
// keep track of code => ID correspondence
$map = array();
// parse data
for ($i = 1, $c = count($data[0]); $i < $c; ++$i) {
// save ID in the map
$map[$data[0][$i]] = $id;
// initialize the current row
$row = array(
'id' => $id,
'parent_id' => 1,
'position' => 0,
'left' => 0,
'right' => 0,
'level' => 1,
'title' => $data[1][$i],
);
// if the code is multiple of 100
if ($data[0][$i] % 100 == 0) {
$row['parent_id'] = 2;
$row['level'] = 2;
$row['position'] = (floor($data[0][$i] / 100)) - 1;
} else {
// get parent id from map
$row['parent_id'] = $map[floor($data[0][$i] / 100) * 100];
$row['level'] = 3;
$row['position'] = $data[0][$i] % 100;
}
// add the row
$rows[] = $row;
++$id;
echo "<br>";
print_r($row);
}
Given your $data array, you could parse it like this:
// this will contain all the rows to be inserted in your DB
$rows = array();
// insert ROOT row
$rows[0] = array(
'id' => 1,
'parent_id' => 0,
'position' => 0,
'level' => 0,
'left' => 1,
'right' => 10000,
'title' => 'ROOT',
);
// insert group row
$rows[1] = array(
'id' => 2,
'parent_id' => 1,
'position' => 0,
'level' => 1,
'left' => 2,
'right' => 9999,
'title' => 'Group',
);
// keep trace of code => ID correspondence
$map = array();
// next ID to be used
$id = 3;
// keep father => sons relationship
$tree = array();
// keep trace of code => row index correspondence
$indexes = array();
// next row index
$index = 2;
// parse your data
for ($i = 1, $c = count($data[0]); $i < $c; ++$i) {
// current code
$code = $data[0][$i];
// save ID in the map
$map[$code] = $id;
// update the indexes map
$indexes[$code] = $index;
// prepare the current row
$row = array(
'id' => $id,
'title' => $data[1][$i],
)
// get the value of code mod 100
$mod = $code % 100;
// if the code is multiple of 100
if ($mod == 0) {
// the parent_id is 2
$row['parent_id'] = 2;
// it is level two
$row['level'] = 2;
// compute position
$row['position'] = floor($code / 100) - 1;
}
else {
// get the parent code
$parent = floor($code / 100) * 100;
// get parent id from map using parent code
$row['parent_id'] = $map[$parent];
// it is level three
$row['level'] = 3;
// save position
$row['position'] = $mod;
// save in relationship tree
$tree[$parent][] = $code;
}
// add the row
$rows[$index] = $row;
// prepare next id
++$id;
// update row index
++$index;
}
// sort the relationship tree base on the parent code (key)
ksort($tree, SORT_NUMERIC);
// next left value
$left = 3;
// now, using the relationship tree, assign left and right
foreach ($tree as $parent => $sons) {
// calculate parent left value
$parentLeft = $left;
// prepare next left value
++$left;
// to be sure that the sons are in order
sort($sons, SORT_NUMERIC);
// assign values to sons
foreach ($sons as $son) {
// index in the rows array
$index = $indexes[$son];
// set left value
$rows[$index]['left'] = $left;
// set right value
$rows[$index]['right'] = $left + 1;
// increment left value
$left += 2;
}
// calculate parent right value
$parentRight = $left;
// prepare next left value
++$left;
// index of parent in the rows array
$index = $indexes[$parent];
// set the values
$rows[$index]['left'] = $parentLeft;
$rows[$index]['right'] = $parentRight;
}
// update group row right value
$rows[1]['right'] = $left;
// prepare next left value
++$left;
// update root row right value
$rows[0]['right'] = $left;
At this point, you can insert all the rows one at a time.
EDIT: now the script should handle all the required values correctly.
I would use Doctrine2 with a Nested Set Extension. You could use a nice and convenient API and don't have to worry about the nested set implementation:
See
http://www.gediminasm.org/article/tree-nestedset-behavior-extension-for-doctrine-2
or http://wildlyinaccurate.com/simple-nested-sets-in-doctrine-2
There are several extensions on github. Actually, i don't know which one is best.
https://github.com/l3pp4rd/DoctrineExtensions
https://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior
https://github.com/blt04/doctrine2-nestedset
List item
If your data is flat, you could parse for keywords like 'Unit' or 'Task' to arrange your elements to the needed hierarchical order.

Stuck on trying to turn input into a string of certain format

So I have a field in the db for delivery times, and it is of certain format (not my design for sure!):
Here are a couple of examples:
M, T, W, TH ; MTWTH 9 TO 4 CLOSED 12 TO 1
T, W, TH, F ; TTHF 9 TO 5 CLOSED 12 TO 2, W 9 TO 12
T, W, TH, F ; T 10 TO 7 CLOSED 2 TO 3, WTH 9 TO 6 CLOSED 1 TO 2, F 8 TO 5 CLOSED 1 TO 2
So basically, you have a list of days, separated by a comma, followed by semi-colon.
After semi-colon there is a list of days, not separated, and followed by delivery times including break times.
The problem is, delivery times can be different for each day, same for all days, or same for let's say Monday and tuesday, then different for Wednesday, and same for Thursday and Friday (but Thursday and Friday have different delivery times then Monday and Tuesday).
And if delivery times match for some days, those days should be grouped together, with delivery times listed after the group days, then other matched or single days listed after that. First day for which there are delivery hours specified must go first.
I know, the format is ridiculous and delivery days/times should be in a separate table linked by organization id, but it's the format they use now for some download files.
I hope to change it later, but for now...
I can't figure out how to transform input from 4 drop-downs (From, To, Closed From, Closed To) into that format.
Any of the days may not have closing times at all. At least one day must have delivery times specified (I check for that).
I of course got the first part (before semi-colon) - that's easy, after that I'm totally stuck.
If all days for which there's delivery had the same hours, I wouldn't have issues. But since it can be totally different from day to day, I don't know how to do it.
So this is the processing block so far:
$trimmed is the array of POST values that has been trimmed,
$trimmed['1Monday'] is Monday From, $trimmed['2Monday'] is Monday To,
$trimmed['3Monday'] is Monday Closed From, $trimmed['4Monday'] is Monday Closed To.
So $M_times variable will have open/closing times for Monday.
$days_del array is used to later create a string that is the first part of required format.
I can get the first day with some times specified and I can try to catch any matches after that, but that isn't useful if first delivery day has different values from the rest, yet there are some matching values after that. I'm not even sure that I need to use anoither (numbered) days array or I can do it with just the main one with M-F for keys.
<?php
$days = array('M' => 'Monday', 'T' => 'Tuesday', 'W' => 'Wednesday', 'TH' => 'Thursday', 'F' => 'Friday');
$days_numbered = array(1 => 'M', 'T', 'W', 'TH', 'F');
$days_del = $matches = $full_list = array();
$days_delivery2 = '';
$count_no_del = 0; # initialize count for days with no delivery
$times_6 = '';
foreach ($days as $k => $v){ ##### Beginning of days loop
if (isset($trimmed["1$v"]) && isset($trimmed["2$v"])){ # If both closing and opening times have been specified
$days_del[] = $k;
${$k.'_times'} = $trimmed["1$v"] . ' TO ' . $trimmed["2$v"];
if ((!isset($trimmed["3$v"]) && isset($trimmed["4$v"])) || (isset($trimmed["3$v"]) && !isset($trimmed["4$v"]))){ # If only beginning or only end of lunch(?) break has been specified
$errors[] = 'Delivery times for $v - please specify both start and end of closing time.';
}elseif (isset($trimmed["3$v"]) && isset($trimmed["4$v"]){ # If both beginning and end of lunch(?) breakh have been specified
${$k.'_times'} .= 'CLOSED' . $trimmed["3$v"] . ' TO ' . $trimmed["4$v"];
}
}elseif ((!isset($trimmed["1$v"]) && isset($trimmed["2$v"])) || (isset($trimmed["1$v"]) && !isset($trimmed["2$v"]))){ # If only closing or only opening time has been specified
$errors[] = 'Delivery times for $v - please specify both start and end of delivery time.';
${$k.'_times'} = NULL;
}elseif (!isset($trimmed["1$v"]) && !isset($trimmed["2$v"])){ # No delivery times specified for this day
${$k.'_times'} = NULL;
$count_no_del++;
}
$full_list["$k"] = ${$k.'_times'};
} ##### End of days loop
if ($count_no_del > 0){ # If there are no delivery days specified
$errors[] = 'You must specify delivery hours for at least one day of the week.';
}
$days_delivery1 = implode(',', $days_del);
$days_delivery1 = $days_delivery1 . ' ; ';
foreach ($days_numbered as $num => $val){ # Getting first day for which delivery hours have been specified
if (isset(${$val.'_times'}) && (${$val.'_times'} != NULL)){
${'times_'.$num} = ${$val.'_times'};
$first_day = $num;
break;
}
}
$check_array = array_keys($full_list, ${'times_'.$first_day})); # checking how many other days match the delivery hours for the first specified day.
foreach ($check_array as $array_key){
$days_delivery2 .= $array_key;
}
$days_delivery2 .= " " . ${'times_'.$first_day};
$note_line = $days_delivery1 . " " ; # second part, something like 'MTH 9 To 5, TW 10 TO 5 CLOSED 1 TO 2, F 10 TO 2' should go a s the second part of the string.
?>
As you can see, after getting the first part of the string in that format ($days_delivery1) I'm stuck and don't know what the hell am I doing. I have a vouge idea of using 2 different arrays (main one and numbered one) and using array_keys to find matching values, but any time I try to work on it I just run into a wall. Any ideas would be much apreciated.
The thing that is potentially difficult here is grouping like entries for the second section. In order to overcome this, I suggest you convert the times to 4 digit representations to normalise them and concatenate the patterns, so create a "signature" for each pattern.
In the below example, I will be working on the principal that the following $_POST arrays will result in the corresponding result strings:
$_POST1 = array(
'1Monday' => 9,
'2Monday' => 5,
'3Monday' => 12,
'4Monday' => 1,
'1Tuesday' => 9,
'2Tuesday' => 5,
'3Tuesday' => 12,
'4Tuesday' => 1,
'1Wednesday' => 9,
'2Wednesday' => 5,
'3Wednesday' => 12,
'4Wednesday' => 1,
'1Thursday' => 9,
'2Thursday' => 5,
'3Thursday' => 12,
'4Thursday' => 1,
'1Friday' => 9,
'2Friday' => 4,
'3Friday' => 11,
'4Friday' => 12
);
$result1 = "M, T, W, TH, F ; MTWTH 9 TO 5 CLOSED 12 TO 1, F 9 TO 4 CLOSED 11 TO 12";
$_POST2 = array(
'1Monday' => '',
'2Monday' => '',
'3Monday' => '',
'4Monday' => '',
'1Tuesday' => 9,
'2Tuesday' => 5,
'3Tuesday' => 12,
'4Tuesday' => 1,
'1Wednesday' => 9,
'2Wednesday' => 5,
'3Wednesday' => 12,
'4Wednesday' => 1,
'1Thursday' => 9,
'2Thursday' => 5,
'3Thursday' => 12,
'4Thursday' => 1,
'1Friday' => 9,
'2Friday' => 5,
'3Friday' => 12,
'4Friday' => 1
);
$result2 = "T, W, TH, F ; TWTHF 9 TO 5 CLOSED 12 TO 1";
Now let's take a look at how we'd process that data. I'm going to assume that we are working directly with the unprocessed POST array.
<?php
// Obviously we will need this map if the inputs use the full day names
$days = array('M' => 'Monday', 'T' => 'Tuesday', 'W' => 'Wednesday', 'TH' => 'Thursday', 'F' => 'Friday');
// A couple of arrays to hold the results of the loop iterations
$resultDays = $resultTimes = array();
// First we iterate over the days
foreach ($days as $dayCode => $dayFull) {
// Data about this day
$dayData = array(
'open' => 0,
'close' => 0,
'lunchClose' => 0,
'lunchOpen' => 0,
'days' => array()
);
// First get the open/close times
$open = $_POST["1$dayFull"];
$close = $_POST["2$dayFull"];
if (empty($open) || empty($close)) {
// If we don't have both open/close times, skip this day
continue;
}
// We definitely open on this day
$resultDays[] = $dayCode;
$dayData['open'] = $open;
$dayData['close'] = $close;
// Pad the strings to make the signature
$openPadded = str_pad($open, 2, "0", STR_PAD_LEFT);
$closePadded = str_pad($close, 2, "0", STR_PAD_LEFT);
// Now look at lunch times
$lunchClose = $_POST["3$dayFull"];
$lunchOpen = $_POST["4$dayFull"];
if (!empty($lunchClose) || !empty($lunchOpen)) {
// If we have both open/close times, add a lunch break
// Add data to $dayData
$dayData['lunchClose'] = $lunchClose;
$dayData['lunchOpen'] = $lunchOpen;
// Pad the strings to make the signature
$lunchClosePadded = str_pad($lunchClose, 2, "0", STR_PAD_LEFT);
$lunchOpenPadded = str_pad($lunchOpen, 2, "0", STR_PAD_LEFT);
} else {
// So we don't break this signature
$lunchClosePadded = $lunchOpenPadded = '';
}
// Build the signature
$signature = $openPadded.$closePadded.$lunchClosePadded.$lunchOpenPadded;
// Add day data to result times array
if (!isset($resultTimes[$signature])) {
$resultTimes[$signature] = $dayData;
}
$resultTimes[$signature]['days'][] = $dayCode;
}
// Now we can build the string
// Like you say, first part is easy
$firstPart = implode(', ', $resultDays);
// Loop $resultTimes and construct to more sensible arrangements
$secondPart = array();
foreach ($resultTimes as $block) {
$str = implode('', $block['days'])." {$block['open']} TO {$block['close']}";
if (!empty($block['lunchClose']) && !empty($block['lunchOpen'])) {
$str .= " CLOSED {$block['lunchClose']} TO {$block['lunchOpen']}";
}
$secondPart[] = $str;
}
// Now we can construct the final string
$finalResult = $firstPart." ; ".implode(', ', $secondPart);
See it working.
Well, that was fun.

Categories