PHP How to set the current values of array? - php

I'm using codeigniter for this project;
at the start of my script there's this query that checks the completed steps and sets it into array $completed_steps.
public function checkSteps()
{
$completed_steps = $this->prefModel->checkStepstbl();
$this->getPref($completed_steps);
}
so the example result is like;
Array
(
[01] => Array
(
[cat_id] => 15
[offset] => 4951
)
[02] => Array
(
[cat_id] => 15
[offset] => 4251
)
[03] => Array
(
[cat_id] => 15
[offset] => 4001
)
[04] => Array
(
[cat_id] => 15
[offset] => 4951
)
)
this is my function to get prefectures;
public function getPref($completed_steps)
{
$prefectures = $this->prefModel->getList();
foreach ( $prefectures as $prefecture ) {
$prefectureId = $prefecture["id"];
$batch_count = 0;
$max_batch = 10;
$this->getInd($prefectureId, $completed_steps,$batch_count,$max_batch);
}
}
The $prefectures have;
$prefectures = array(array("id"=>"01","name"=>"北海道"),array("id"=>"02","name"=>"青森県"),array("id"=>"03","name"=>"岩手県"),array("id"=>"04","name"=>"宮城県"),array("id"=>"05","name"=>"秋田県"),array("id"=>"06","name"=>"山形県"),array("id"=>"07","name"=>"福島県"),array("id"=>"08","name"=>"茨城県"),array("id"=>"09","name"=>"栃木県"),array("id"=>"10","name"=>"群馬県"),array("id"=>"11","name"=>"埼玉県"),array("id"=>"12","name"=>"千葉県"),array("id"=>"13","name"=>"東京都"),array("id"=>"14","name"=>"神奈川県"),array("id"=>"15","name"=>"新潟県"),array("id"=>"16","name"=>"富山県"),array("id"=>"17","name"=>"石川県"),array("id"=>"18","name"=>"福井県"),array("id"=>"19","name"=>"山梨県"),array("id"=>"20","name"=>"長野県"),array("id"=>"21","name"=>"岐阜県"),array("id"=>"22","name"=>"静岡県"),array("id"=>"23","name"=>"愛知県"),array("id"=>"24","name"=>"三重県"),array("id"=>"25","name"=>"滋賀県"),array("id"=>"26","name"=>"京都府"),array("id"=>"27","name"=>"大阪府"),array("id"=>"28","name"=>"兵庫県"),array("id"=>"29","name"=>"奈良県"),array("id"=>"30","name"=>"和歌山県"),array("id"=>"31","name"=>"鳥取県"),array("id"=>"32","name"=>"島根県"),array("id"=>"33","name"=>"岡山県"),array("id"=>"34","name"=>"広島県"),array("id"=>"35","name"=>"山口県"),array("id"=>"36","name"=>"徳島県"),array("id"=>"37","name"=>"香川県"),array("id"=>"38","name"=>"愛媛県"),array("id"=>"39","name"=>"高知県"),array("id"=>"40","name"=>"福岡県"),array("id"=>"41","name"=>"佐賀県"),array("id"=>"42","name"=>"長崎県"),array("id"=>"43","name"=>"熊本県"),array("id"=>"44","name"=>"大分県"),array("id"=>"45","name"=>"宮崎県"),array("id"=>"46","name"=>"鹿児島県"),array("id"=>"47","name"=>"沖縄県"));
when getting industry, this is the function;
public function getInd($prefectureId,$completed_steps,$batch_count,$max_batch)
{
$industries = $this->indModel->getList();
foreach ( $industries as $industry ) {
$industryId = $industry["id"];
$this->companiesCount($prefectureId,$industryId,$completed_steps,$batch_count,$max_batch);
}
}
$industries would give;
$industries=array(array("id"=>"1","name"=>"グルメ"),array("id"=>"2","name"=>"住まい"),array("id"=>"3","name"=>"医療・健康・介護"),array("id"=>"4","name"=>"美容・ファッション"),array("id"=>"5","name"=>"暮らし"),array("id"=>"6","name"=>"ショッピング"),array("id"=>"7","name"=>"ペット"),array("id"=>"8","name"=>"旅行宿泊"),array("id"=>"9","name"=>"ビジネス"),array("id"=>"10","name"=>"教育・習い事"),array("id"=>"11","name"=>"趣味"),array("id"=>"12","name"=>"公共機関・団体"),array("id"=>"13","name"=>"レジャー・スポーツ"),array("id"=>"14","name"=>"冠婚葬祭・イベント"),array("id"=>"15","name"=>"自動車・バイク"));
and in my companiesCount function;
public function companiesCount($prefectureId,$industryId, $completed_steps, $batch_count,$max_batch)
{
$loop_flg = true;
$offset = 1;
$limit = 50;
while ($loop_flg) {
if ($completed_steps != null) {
if ((array_key_exists($prefectureId, $completed_steps)) && ($completed_steps[$prefectureId]["cat_id"] == $industryId) && $completed_steps[$prefectureId]["offset"] == $offset) {
continue;
}
}
if($batch_count >= $max_batch){
sleep(75);
$last_offset = $this->lastOffset($prefectureId,$industryId);
$batch_count = $this->batchCount();
if($last_offset == ($offset - $limit)) {
if(!empty($batch_count)) {
$result = $this->indModel->getprocessId();
}
if(!empty($result)) {
if(!($this->PsExists($result->pid))) {
$pid = $this->startCompany($result->prefecture_id,$result->industry_id,$result->offset);
$batch_count++;
$this->compModel->updatePid($result->prefecture_id,$result->industry_id,$pid,$result->offset);
}
}
} else {
return $loop_flg = false;
}
} else {
$pid = $this->startCompany($prefectureId,$industryId,$offset);
$this->compModel->saveStepflg($prefectureId,$industryId,$pid,$offset);
$batch_count++;
$offset += 50;
}
}
}
What I want to do is skip through the array from what is given by the $completed_steps. So that it can start from what remains from the other arrays.
this functionality is located from this code;
if ((array_key_exists($prefectureId, $completed_steps)) && ($completed_steps[$prefectureId]["cat_id"] == $industryId) && $completed_steps[$prefectureId]["offset"] == $offset) {
continue;
}
But I can't do it somehow, help is really needed.
The current results are the perimeter $prefectureId starts again at 01, also the $industryId starts at 1, $offset too will begin from 1.
expected results, based on given $completed_steps;
I want the $prefectureId to set in 05. $industryId starts at 1(because max industry is at 15), offset to start 1 also(because usually offset max is at 4951).

Related

Search nested multidimensional array

I have a function that fills an array:
foreach ($request->get('ids') as $id) {
$pdfArray['other']++; // Yes this is initialized
$pdfArray['rows'][$i]['id'] = $schedule->getId();
$pdfArray['rows'][$i]['date'] = $schedule->getStart()->format('d.m.Y');
$pdfArray['rows'][$i]['dateSort'] = $schedule->getStart()->format('Y-m-d H:i');
$pdfArray['rows'][$i]['from'] = $schedule->getStart()->format('H:i');
$pdfArray['rows'][$i]['to'] = $schedule->getEnd()->format('H:i');
$pdfArray['rows'][$i]['desc'] = $schedule->getDescription();
}
What I want to do
On each loop, I want to check if the array (so far) already has a desc entry equal to the current $schedule->getDescription() AND the same date as $schedule->getStart()->format('d.m.Y') (actually more, but let's keep it simple)
What I tried
public function recursive_array_search($needle,$haystack) {
foreach($haystack as $key=>$value) {
$current_key=$key;
if($needle===$value OR (is_array($value) && $this->recursive_array_search($needle,$value) !== false)) {
return $current_key;
}
}
return false;
}
Source
I use it like that:
if ($this->recursive_array_search($schedule->getDescription(), $pdfArray['rows']) &&
$this->recursive_array_search($schedule->getStart()->format('d.m.Y'), $pdfArray['rows'])){
$pdfArray['ma'][$schedule->getId()]++;
}
but this is true when ANY of the start or desc are SOMEWHERE in the current array.
How would I check if desc is found and start is in the SAME $i level?
EDIT for example
Let's say I have 10 $ids to loop through. After 2 loops, the $pdfArray looks like this:
Array
(
[other] => 2
[rows] => Array
(
[0] => Array
(
[id] => 1
[date] => 13.07.2016
[dateSort] => 2016-07-13 08:00
[from] => 08:00
[to] => 09:00
[desc] => TEST
)
[1] => Array
(
[id] => 2
[date] => 12.07.2016
[dateSort] => 2016-07-12 08:00
[from] => 08:00
[to] => 09:00
[desc] => TEST
)
)
)
The next iteration has the following:
$schedule->getStart()->format('d.m.Y') => 12.07.2016
$schedule->getDescription() => TEST
So I want to have the info that the combination already exists in the array.
BUT
$schedule->getStart()->format('d.m.Y') => 12.07.2016
$schedule->getDescription() => TEST2
should NOT return true upon checking of it exists.
To test for a "duplicate" you can use this function:
function testPresence($pdfArray, $desc, $date) {
foreach ($pdfArray["rows"] as $row) {
if ($row["desc"] == $desc && $row["date"] == $date) return true;
}
}
Example use:
echo testPresence($pdfArray, "TEST2", "12.07.2016") ? "Found" : "Not found"; // Not found
echo testPresence($pdfArray, "TEST", "12.07.2016") ? "Found" : "Not found"; // Found
In your original loop, you can use it as follows:
foreach ($request->get('ids') as $id) {
if (testPresence($pdfArray, $schedule->getDescription(),
$schedule->getStart()->format('d.m.Y')) {
// We have a duplicate. Maybe skip this entry?:
continue;
}
$pdfArray['other']++;
$pdfArray['rows'][$i]['id'] = $schedule->getId();
$pdfArray['rows'][$i]['date'] = $schedule->getStart()->format('d.m.Y');
$pdfArray['rows'][$i]['dateSort'] = $schedule->getStart()->format('Y-m-d H:i');
$pdfArray['rows'][$i]['from'] = $schedule->getStart()->format('H:i');
$pdfArray['rows'][$i]['to'] = $schedule->getEnd()->format('H:i');
$pdfArray['rows'][$i]['desc'] = $schedule->getDescription();
}
try this at your validation function
public function array_search($needle1, $needle2 ,$haystack) {
foreach($haystack as $singleArray){
if (in_array($needle1, $singleArray) && in_array($needle2, $singleArray))
return true;
else
continue;
}
return false;
}
and invoke your recursive_array_search like this
if ($this->array_search($schedule->getStart(), $schedule->getDescription(), $pdfArray['rows'])
continue;//Or any other kind of logic you want. At this place you know that description and date staet exist in at your array level
$pdfArray['other']++; // Yes this is initialized
$pdfArray['rows'][$i]['id'] = $schedule->getId();
$pdfArray['rows'][$i]['date'] = $schedule->getStart()->format('d.m.Y');
$pdfArray['rows'][$i]['dateSort'] = $schedule->getStart()->format('Y-m-d H:i');
$pdfArray['rows'][$i]['from'] = $schedule->getStart()->format('H:i');
$pdfArray['rows'][$i]['to'] = $schedule->getEnd()->format('H:i');
$pdfArray['rows'][$i]['desc'] = $schedule->getDescription();
Function version:
/**
* Find matches for $item into pdfArray.
* Returns an index array, possibly empty if no matches.
* #param $item item to find
* #param $rows rows where to search
*/
function findPdfArrayMatches(array $item, array $rows) {
return array_keys(
array_filter(
$rows,
function ($entry) use ($item) {
// These are the matching criteria. More than one row may match.
return $entry['desc'] == $item['desc']
&& $entry['date'] == $item['date']
;
}
)
);
}
You could do like this, in the loop:
$item = [
'id' => $schedule->getId(),
'date' => $schedule->getStart()->format('d.m.Y'),
'dateSort' => $schedule->getStart()->format('Y-m-d H:i'),
'from' => $schedule->getStart()->format('H:i'),
'to' => $schedule->getEnd()->format('H:i'),
'desc' => $schedule->getDescription(),
];
$matches = findPdfArrayMatches($item, $pdfArray['rows']);
if (!empty($matches)) {
...do something with the matches:
foreach ($matches as $match) {
$pdfArray['rows'][$match]['Duplicate'] = true;
}
}
// Add new item
$pdfArray['rows'][$i] = $item;

How to change the pagination settings in smarty?

Following is the PHP code of a smarty plugin to apply pagination to the pages.
<?php
function smarty_function_pagination_link_01($params, &$smarty)
{
if ( !is_array($params['values']) )
{
return "is not array";
}
if ( 0 == count($params['values']) )
{
return "Empty Array";
}
if ( empty($params['values']['current_page']) )
{
return "Invalid Request";
}
$values = $params['values'];
//Seperator Used Betwinn Pagination Links
$seprator = empty( $params['seperator'] ) ? " " : $params['seperator'];
//Class Name For Links
$extra = empty( $params['extra'] ) ? "" : $params['extra'];
$current_page = (int)$values['current_page'];
if ( !empty($values['first']) )
{
//$ret[] = "<a $extra href='{$values['first']}' ><First</a>";
}
if ( !empty($values['previous'] ) )
{
$ret[] = "<a $extra href='{$values['previous']}' class='prev active'><span></span></a>";
}
$ret[] = "<ul>";
foreach( $values as $k => $v )
{
if( is_numeric( $k ) )
{
if ( $k == $current_page)
{
$ret[] = "<li><a $extra class='active'>$k</a></li>";
}
else
{
$ret[] = "<li><a $extra href='$v'>$k </a></li>";
}
}
}
if ( !empty($values['next'] ) )
{
$ret[] = "</ul><a $extra href='{$values['next']}' class='next active'><span></span></a>";
}
if ( !empty($values['last'] ) )
{
//$ret[] = "<a $extra href='{$values['last']}' >Last></a>";
}
//$str_ret = $first . $previous . $str_ret . $next . $last;
if ( $ret )
{
return implode( $seprator, $ret );
}
}
?>
This function is called from the smarty template. Now it's working perfectly. But I want to change the behavious of above function, I want to change the value it is returning. The value of the argument $params the above function is getting is as follows:
Array
(
[values] => Array
(
[1] => /xyz/pqr/web/control/modules/questions/view_questions.php?subject_id=&topic_id=&difficulty_level=&from_date=01/02/1999&to_date=17/02/2014&staff_id=&page=1
[2] => /xyz/pqr/web/control/modules/questions/view_questions.php?subject_id=&topic_id=&difficulty_level=&from_date=01/02/1999&to_date=17/02/2014&staff_id=&page=2
[3] => /xyz/pqr/web/control/modules/questions/view_questions.php?subject_id=&topic_id=&difficulty_level=&from_date=01/02/1999&to_date=17/02/2014&staff_id=&page=3
[next] => /xyz/pqr/web/control/modules/questions/view_questions.php?subject_id=&topic_id=&difficulty_level=&from_date=01/02/1999&to_date=17/02/2014&staff_id=&page=2
[last] => /xyz/pqr/web/control/modules/questions/view_questions.php?subject_id=&topic_id=&difficulty_level=&from_date=01/02/1999&to_date=17/02/2014&staff_id=&page=8170
[current_page] => 1
)
)
Now the display of the page numbers are is as follows:
![enter image description here][1]
But I want the opagination numbers in the following fashion:
1 2 3 4 5 6 7 8 9 10 Next Last
How should I get the above pattern by making changes into the function?Thanks in advance.
Try to configure it with num_links like
$config['num_links'] = 10;

Finding the best-fit object from an array of objects

Given an array in the following structure (although obviously with many more items in it):
Array
(
[0] => stdClass Object
(
[currency] => 1
[role] => 3
[client_company] =>
[client_group] =>
[hourly_rate] => 115.00
)
[1] => stdClass Object
(
[currency] => 1
[role] => 1
[client_company] =>
[client_group] =>
[hourly_rate] => 115.00
)
[2] => stdClass Object
(
[currency] => 1
[role] => 3
[client_company] => 58
[client_group] =>
[hourly_rate] => 110.00
)
)
I'm trying to create a function that will take four parameters:
$role
$currency
$company [optional]
$group [optional]
("groups" are children of "companies": if a group is specified, a parent company will always also be specified)
...and that will return the "hourly rate" value from the item that best fits those parameters, on the basis that:
if $row, $currency, $company and $group are specified:
find a rate that matches the role, currency, company and group.
if there isn't one, find one that matches the role, currency and company
if there isn't one, find one that matches the role and currency
if there isn't one, return FALSE
if just $row, $currency and $company are specified:
find a rate that matches the role, currency and company
if there isn't one, find one that matches the role and currency
if there isn't one, return FALSE
if just $row and $currency are specified:
find a rate that matches the role and currency
if there isn't one, return FALSE
What I've got is below, and it works. However, it's ugly as sin. There must be a more elegant way than just bashing a load of if/else and loops together. However, it's Friday and I've had too much pizza for lunch and my brain has become ensludged with cheese.
Can you help?
$hourly_rate = FALSE;
if ( !empty($group) && !empty($company) ) {
foreach ( $rates_cache as $rate ) {
if ( $rate->currency == $currency && $rate->role == $role && (int) $rate->client_company === (int) $company && (int) $rate->client_group === (int) $group ) {
$hourly_rate = $rate->hourly_rate;
}
}
if ( empty($hourly_rate) ) {
foreach ( $rates_cache as $rate ) {
if ( $rate->currency == $currency && $rate->role == $role && (int) $rate->client_company === (int) $company ) {
$hourly_rate = $rate->hourly_rate;
}
}
}
if ( empty($hourly_rate) ) {
foreach ( $rates_cache as $rate ) {
if ( $rate->currency == $currency && $rate->role == $role ) {
$hourly_rate = $rate->hourly_rate;
}
}
}
}else if ( !empty($company) ) {
foreach ( $rates_cache as $rate ) {
if ( $rate->currency == $currency && $rate->role == $role && (int) $rate->client_company === (int) $company ) {
$hourly_rate = $rate->hourly_rate;
}
}
if ( empty($hourly_rate) ) {
foreach ( $rates_cache as $rate ) {
if ( $rate->currency == $currency && $rate->role == $role ) {
$hourly_rate = $rate->hourly_rate;
}
}
}
}else{
foreach ( $rates_cache as $rate ) {
if ( $rate->currency == $currency && $rate->role == $role ) {
$hourly_rate = $rate->hourly_rate;
}
}
}
return $hourly_rate;
Assumption
I believe your cache is always in the format below
Cache Format:
$cache = array(
0 => (object) (array(
'currency' => 1,
'role' => 3,
'client_company' => '',
'client_group' => '',
'hourly_rate' => '115.00'
)),
1 => (object) (array(
'currency' => 1,
'role' => 1,
'client_company' => '',
'client_group' => '',
'hourly_rate' => '115.00'
)),
2 => (object) (array(
'currency' => 1,
'role' => 3,
'client_company' => 58,
'client_group' => '',
'hourly_rate' => '110.00'
))
);
Your Revised Function
$param = array(
"role" => 1,
"currency" => 1
);
echo find($cache, $param)->hourly_rate;
Function Used
function find($cache, $param) {
$mx = array();
if (! isset($param['role']) || ! isset($param['currency']))
throw new Exception("Missing Role Or Currency");
foreach ( $cache as $k => $r ) {
foreach ( array_keys(array_intersect($param, (array) $r)) as $key ) {
if ($r->{$key} == $param[$key]) {
isset($mx[$k]) ? $mx[$k] ++ : $mx[$k] = 1;
}
}
}
arsort($mx);
return $cache[key($mx)];
}
More Complex: Another Approach
Usage
$param = array(
"role" => 1,
"currency" => 1
);
$process = new Process($cache);
echo $process->find($param)->best()->hourly_rate; // Outputs 115.00
Multiple Results
When find best fit .. there is possibility you would get more than one result
$param = array(
"role" => 3,
"currency" => 1
);
$process = new Process($cache);
var_dump($process->find($param)->results());
Output
array (size=2)
0 =>
object(stdClass)[1]
public 'currency' => int 1
public 'role' => int 3
public 'client_company' => string '' (length=0)
public 'client_group' => string '' (length=0)
public 'hourly_rate' => string '115.00' (length=6)
2 =>
object(stdClass)[3]
public 'currency' => int 1
public 'role' => int 3
public 'client_company' => int 58
public 'client_group' => string '' (length=0)
public 'hourly_rate' => string '110.00' (length=6)
Not getting best result
You can see based on your parameters you are getting 2 if you are looking for cheapest prize and you call
$param = array(
"role" => 3,
"currency" => 1
);
echo Process::quick($cache, $param)->best()->hourly_rate; // returns 115.00 but that is not the cheapest
Resolution
The solution is you can add filter and sort
$param = array(
"role" => 3,
"currency" => 1
);
$sort = function ($a, $b) {
return $a->hourly_rate < $b->hourly_rate ? - 1 : 1;
};
echo Process::quick($cache, $param)->sort($sort)->best()->hourly_rate; // 110
Getting all Related
You can also just loop through all the result and select the columns you want insted of just getting best result
foreach ( Process::quick($cache, $param)->sort($sort)->getColoum("client_company", "hourly_rate") as $result ) {
print_r($result);
}
Output
stdClass Object
(
[client_company] => 58
[hourly_rate] => 110.00
)
stdClass Object
(
[client_company] =>
[hourly_rate] => 115.00
)
Updated Class
To add all this additional functions you need to upgrade your class to
class Process implements JsonSerializable, IteratorAggregate {
private $cache;
private $matrix = array();
private $final = array();
function __construct($cache) {
$this->cache = $cache;
}
function find($param) {
if (! isset($param['role']) || ! isset($param['currency']))
throw new Exception("Missing Role Or Currency");
foreach ( $this->cache as $k => $rate ) {
$keys = array_intersect($param, (array) $rate);
foreach ( array_keys($keys) as $key ) {
if ($rate->{$key} == $param[$key]) {
isset($this->matrix[$k]) ? $this->matrix[$k] ++ : $this->matrix[$k] = 1;
}
}
}
arsort($this->matrix);
$this->matrix = array_keys(array_filter($this->matrix, function ($v) {
return $v >= 2;
}));
$this->final = $this->sortArray($this->cache, $this->matrix);
return $this;
}
public static function quick($cache, $param) {
$process = new Process($cache);
return $process->find($param);
}
public function best() {
reset($this->final);
return empty($this->final) ? : current($this->final);
}
public function results() {
return $this->final;
}
public function limit($length = 0) {
$this->final = array_slice($this->final, 0, $length);
return $this;
}
public function sort(Callable $function) {
usort($this->final, $function);
return $this;
}
public function getColoum() {
$arg = array_flip(func_get_args());
foreach ( $this->final as &$s ) {
foreach ( $s as $k => $v ) {
if (! isset($arg[$k]))
unset($s->{$k});
}
}
return $this;
}
public function getIterator() {
return new ArrayIterator($this->final);
}
public function jsonSerialize() {
return json_encode($this->final);
}
public function __toString() {
return $this->jsonSerialize();
}
private function sortArray(array $array, array $orderArray) {
$ordered = array();
foreach ( $orderArray as $key => $value ) {
array_key_exists($value, $array) and $ordered[$value] = $array[$value];
}
return $ordered;
}
}

how to fetch array in different time slots

I have an two dimensional array like below:
array( array(), array(), array(), array(), array(), array() );
I have to access 0,1,2 indexed arrays in time slot of 8 hours in day, if a user request value matches array value in first 8th hour slot then he will be get 3rd index array otherwise 0th indexed array, if same user request again in next 8th hour slot then he'll get 4 th indexed array. If there is new request which doesn't matched value in array then they get scheduled indexed array i.e
from 12:00A.M to 8:00Am they get Array( 0 => array() ),
from 8:00Am to 16:00PM they get Array( 1 => array() ),
from 16:00Pm o 23:59PM they get Array( 2 => array() ).
I have implemented this using if else conditions but using hard cord timing conditions:
$params = array(); //main array
$pn = $_REQUEST['pn'];
$ad = new time_slot();
$time = date("H");
if ( $pn == $ad->ad_filter( $params, 0 ) ) { //ad_filter extract value from array and match request user value
if ( $time < 8 ) {
echo $ad->ad_data( $params, 1 ); //ad_data throw an array from multi-dimensional array
} else {
echo $ad->ad_data( $params, 3 );
}
} elseif ( $pn == $ad->ad_filter( $params, 1 ) ) {
if ( $time > 8 && $time < 16 ) {
echo $ad->ad_data( $params, 2 ); //after 8 hour
} else {
echo $ad->ad_data( $params, 4 );
}
} elseif ( $pn == $ad->ad_filter( $params, 2 ) ) {
if ( $time > 16 && $time < 24 ) {
echo $ad->ad_data( $params, 0 ); //after 16 hour
} else {
echo $ad->ad_data( $params, 5 );
}
} elseif ( !in_array($pn, $ad->ad_filter( $params ))) { //if request is new
if ( $time < 8 ) {
echo $ad->ad_data( $params, 0 );
} elseif ( $time > 8 && $time < 16 ) {
echo $ad->ad_data( $params, 1 );
} elseif ( $time > 16 && $time < 24 ) {
echo $ad->ad_data( $params, 2 );
} else {
echo $ad->ad_data( $params, 3 );
}
}
But if have to throw array in only two slots i.e in 12 hours slot or in if in 4 hour slots then i have to code again conditions which is pathetic for me. So i created a function wrapper for this scenario:
count array using sizeof and loop it over all iterations as:
function array_scheduling( $params = array() ) {
for ($i=0; $i<sizeof($params)/2; $i++) {
if ($pn == $ad->ad_filter($params, $i)) {
if ($time < (24/(sizeof($params)/2))*($i+1)) {
echo $ad->ad_data( $params, $i++ );
} else {
echo $ad->ad_data( $params, $i+=3 );
}
} elseif (!in_array($pn, $params)) {
echo $ad->ad_data( $params, $i );
}
}
}
This is only just a try, but it slightly not working because if user request value matches last array element value then i have to thrown first array. I can do this using if/else conditions successfully but in future for if there are more slots then how could i achieve it using a function which contains abstract definition.

Performance with time related algorithm

I have a function that take 2 arrays ($schedule, $remove), both are arrays of days with time inside, it will remove time from the schedule .
Now this function is working fine if I have between 1 & 20 user it takes 2-4 seconds to generate the calendar which is fine but when having 20+ user with a lot of schedules entries it goes to 15+ seconds.
I'm working with CodeIgniter and I have this function in a helper where it's called a lot.
So I wanted to know if you guys can see any better way to deal with my problem or adjustments that I make to my algorithm to make it faster.
Note:
In my code below, the big problem I see is the recursive call and the break of the loop every time I modify the structure.
I loop on both arrays and do test to see if the absence is inside/overlap/equal/outside of the availability and then recall the function if the structure was modified if not return the final structure.
Note 2 :
On local the Apache crash because the recursive function sometime is called more than 100 times .
Here is the code I have :
function removeSessionsFromSchedule($schedule, $remove) {
$modified = false;
if (is_array($schedule) && count($schedule) > 0 && is_array($remove) && count($remove) > 0 && checkArrayEmpty($remove)) {
// Minimise the iterations
$remove = minimiseRemoveSchedule($remove);
foreach ($schedule as $s => $dispo) {
if ($modified) {
break;
}
$pos = 0;
$countdispo = count($dispo);
foreach ($dispo as $d) {
$abs = isset($remove[$s]) ? $remove[$s] :null;
$counter = 0;
// availability start/end
$dis_s = strtotime($d['heure_debut']);
$dis_e = strtotime($d['heure_fin']);
if (is_array($abs) && count($abs) > 0) {
foreach ($abs as $a) {
// absence start/end
$abs_s = strtotime($a['heure_debut']);
$abs_e = strtotime($a['heure_fin']);
// Tests to see the if there is overlap between absence and availability
// (2) [a_s]---[ds - de]---[a_e]
if ($abs_s <= $dis_s && $abs_e >= $dis_e) {
// delete availability
unset($schedule[$s][$pos]);
$modified = true;
break;
}
// (7)[as == ds] && [ae < de]
else if ($abs_s == $dis_s && $abs_e < $dis_e) {
unset($schedule[$s][$pos]);
$schedule[$s][$pos] = $d;
$schedule[$s][$pos]['heure_debut'] = date("H:i", $abs_e);
$schedule[$s][$pos]['heure_fin'] = date("H:i", $dis_e);
$modified = true;
break;
}
// (6) [ds -de] --- [as ae] return dispo as is
else if ($abs_s >= $dis_e) {
unset($schedule[$s][$pos]);
$schedule[$s][$pos] = $d;
$modified ?: false;
}
// (5)[as ae] [ds -de] --- return dispo as is
else if ($abs_e <= $dis_s) {
unset($schedule[$s][$pos]);
$schedule[$s][$pos] = $d;
$modified ?: false;
}
// (1)[ds] --- [as] --- [ae] --- [de] (duplicate dis with new times)
else if ($abs_s > $dis_s && $abs_e <= $dis_e) {
// new times as : // s1 = ds-as && s2 = ae-de
unset($schedule[$s][$pos]);
$schedule[$s][$pos] = $d;
$schedule[$s][$pos + 1] = $d;
$schedule[$s][$pos]['heure_debut'] = date("H:i", $dis_s);
$schedule[$s][$pos]['heure_fin'] = date("H:i", $abs_s);
$schedule[$s][$pos + 1]['heure_debut'] = date("H:i", $abs_e);
$schedule[$s][$pos + 1]['heure_fin'] = date("H:i", $dis_e);
// a revoir si ca ne cause pas d'autre problem qu'on fasse pos++ ...
$pos++;
$modified = true;
break;
}
// (3)[as] -- [ds] --- [ae] -- [de]
else if ($abs_s < $dis_s && $abs_e < $dis_e) {
unset($schedule[$s][$pos]);
$schedule[$s][$pos] = $d;
$schedule[$s][$pos]['heure_debut'] = date("H:i", $abs_e);
$schedule[$s][$pos]['heure_fin'] = date("H:i", $dis_e);
$modified = true;
break;
}
// (4) [ds]---[as]--- [de]--- [ae]
else if ($abs_s > $dis_s && $abs_s < $dis_e && $abs_e > $dis_e) {
unset($schedule[$s][$pos]);
$schedule[$s][$pos] = $d;
$schedule[$s][$pos]['heure_debut'] = date("H:i", $dis_s);
$schedule[$s][$pos]['heure_fin'] = date("H:i", $abs_s);
$modified = true;
break;
} else {
$modified ?: false;
}
}
// if($modified == true) { break;}
} else {
$modified = false;
}
$pos++;
}
}
} else {
$modified = false;
}
if ($modified) {
$schedule = resetIndexes($schedule);
$schedule = sortByTime($schedule);
$schedule = removeSessionsFromSchedule($schedule, $remove);
}
return $schedule;
}
Related Helpers
function checkArrayEmpty($array) {
if(is_array($array) && !empty($array)) {
foreach($array as $arr) {
if(is_array($arr) && !empty($arr)) {
return true;
}
}
}
return false;
}
function subval_sort_by_time($a, $subkey) {
if (is_array($a) && count($a) > 0) {
foreach ($a as $k => $v) {
$b[$k] = strtotime($v[$subkey]);
}
asort($b);
foreach ($b as $key => $val) {
$c[] = $a[$key];
}
return $c;
}
else
return $a;
}
// Reset Index function
function resetIndexes($array) {
$new = array();
foreach($array as $date => $arr) {
//$new[$date]= array_values($arr);
$new[$date]= array_merge(array(),$arr);
}
return $new;
}
// sort by time
function sortByTime($array) {
$sorted = array();
if(is_array($array) && !empty($array)){
foreach ($array as $s => $val) {
$sorted[$s] = subval_sort_by_time($val, 'heure_debut');
}
}
return $sorted;
}
function minimiseRemoveSchedule($array) {
$new = array();
foreach($array as $date => $arr) {
$i=0;
if(is_array($arr) && !empty($arr)) {
foreach($arr as $a) {
if(isset($new[$date][$i])) {
if($new[$date][$i]['heure_fin'] == $a['heure_debut']) {
$new[$date][$i]['heure_fin'] = $a['heure_fin'];
}
else {
$i++;
$new[$date][$i]['heure_debut'] = $a['heure_debut'];
$new[$date][$i]['heure_fin'] = $a['heure_fin'];
}
} else {
$new[$date][$i]['heure_debut'] = $a['heure_debut'];
$new[$date][$i]['heure_fin'] = $a['heure_fin'];
}
}
}
}
return $new;
}
Example of Array that I pass:
$schedule = Array(
'2012-11-12' => Array(),
'2012-11-13' => Array(),
'2012-11-14' => Array( 0 => Array("employe_id" => 8 , "heure_debut" => '16:00' ,"heure_fin" => '20:00' ,"date_seance" => 2012-11-14 , "jour_id" => 3)),
'2012-11-15' => Array(
0 => Array("employe_id" => 8 , "heure_debut" => '09:00' ,"heure_fin" => '15:00' ,"date_seance" => 2012-11-15 , "jour_id" => 4),
1 => Array("employe_id" => 8 , "heure_debut" => '16:00' ,"heure_fin" => '21:00' ,"date_seance" => 2012-11-15 , "jour_id" => 4)
),
'2012-11-16' => Array(),
'2012-11-17' => Array(),
'2012-11-18' => Array(),
'2012-11-19' => Array(0 => Array("employe_id" => 8 ,"heure_debut" => '10:00' ,"heure_fin" => '22:00' ,"date_seance" => 2012-11-19 ,"jour_id" => 1)),
'2012-11-20' => Array(
0 => Array("employe_id" => 8 ,"heure_debut" => '09:00' ,"heure_fin" => '15:00' ,"date_seance" => 2012-11-20 ,"jour_id" => 2),
1 => Array("employe_id" => 8 ,"heure_debut" => '16:00' ,"heure_fin" => '20:00' ,"date_seance" => 2012-11-20 ,"jour_id" => 2)
)
);
And for the second array:
$remove = array(
'2012-11-12' => Array(),
'2012-11-13' => Array(),
'2012-11-14' => Array(),
'2012-11-15' => Array(),
'2012-11-16' => Array(),
'2012-11-17' => Array(),
'2012-11-18' => Array(),
// in this example i only have 1 absence ... I could have N absences
'2012-11-19' => Array(0 => Array("employe_id" => 8 ,"date_debut" => 2012-11-19,"date_fin" => 2012-11-19 ,"heure_debut" => '12:00:00',"heure_fin" => '14:00:00')),
'2012-11-20' => Array(),
'2012-11-21' => Array()
);
The resulting array would be:
$result = array(
Array
(
[2012-11-12] => Array()
[2012-11-13] => Array()
// no change
[2012-11-14] => Array( [0] => Array("employe_id" => 8 , "heure_debut" => 16:00 ,"heure_fin" => 20:00 ,"date_seance" => 2012-11-14 , "jour_id" => 3))
// no change
[2012-11-15] => Array(
[0] => Array("employe_id" => 8 , "heure_debut" => 09:00 ,"heure_fin" => 15:00 ,"date_seance" => 2012-11-15 , "jour_id" => 4),
[1] => Array("employe_id" => 8 , "heure_debut" => 16:00 ,"heure_fin" => 21:00 ,"date_seance" => 2012-11-15 , "jour_id" => 4)
)
[2012-11-16] => Array()
[2012-11-17] => Array()
[2012-11-18] => Array()
// since absence from 12 to 14 and we had availability from 8 to 22 instead we will have 8->12 and 14->22
[2012-11-19] => Array(
[0] => Array("employe_id" => 8 ,"heure_debut" => 08:00 ,"heure_fin" => 12:00 ,"date_seance" => 2012-11-20 ,"jour_id" => 1),
[1] => Array("employe_id" => 8 ,"heure_debut" => 14:00 ,"heure_fin" => 22:00 ,"date_seance" => 2012-11-20 ,"jour_id" => 1)
)
// no changes since no absence during those time
[2012-11-20] => Array(
[0] => Array("employe_id" => 8 ,"heure_debut" => 09:00 ,"heure_fin" => 15:00 ,"date_seance" => 2012-11-20 ,"jour_id" => 2),
[1] => Array("employe_id" => 8 ,"heure_debut" => 16:00 ,"heure_fin" => 20:00 ,"date_seance" => 2012-11-20 ,"jour_id" => 2)
)
)
I don't see why you need an exponential time recursion to execute this task. You can get away with an O(r * e^2) solution (where e is the average number of availabilities/removals per day, and r is size of removed times) via nested loop. Pseudocode below:
for removeday in remove:
define scheduleday := schedule[removeday.date]
if scheduleday not found:
continue
for removesegment in removeday:
define temparray := empty
for availsegment in scheduleday:
if availsegment.employeid != removesegment.employeid:
continue
if no overlap:
temparray.add(availsegment)
if partial overlap:
temparray.add(availsegment.split(removesegment))
scheduleday = temparray
schedule[removeday.date] := scheduleday
return schedule
The code below produces the same output for the given sample but I haven't tested all possible cases.
Working Demo
function removeSessionsFromScheduleHelper(&$schedule,&$remove) {
$change = false;
foreach($remove as $date => &$remove_ranges) {
if(empty($remove_ranges) || !isset($schedule[$date]))
continue;
foreach($remove_ranges as &$remove_range) {
foreach($schedule[$date] as $day_key => &$time) {
//start after finish, no overlap and because schedules are sorted
//next items in schedule loop will also not overlap
//break schedule loop & move to next remove iteration
if($time['heure_debut'] >= $remove_range['heure_fin'])
break;
//finish before start, no overlap
if($time['heure_fin'] <= $remove_range['heure_debut'])
continue;
//complete overlap, remove
if($time['heure_debut'] >= $remove_range['heure_debut']
&& $time['heure_fin'] <= $remove_range['heure_fin']) {
unset($schedule[$date][$day_key]);
continue;
}
//split into 2 ranges
if($time['heure_debut'] < $remove_range['heure_debut']) {
if($time['heure_fin'] > $remove_range['heure_fin']) {
$schedule[$date][] = array(
'heure_debut' => $remove_range['heure_fin'],
'heure_fin' => $time['heure_fin']
);
}
$change = true;
$time['heure_fin'] = $remove_range['heure_debut'];
continue;
}
if($time['heure_debut'] >= $remove_range['heure_debut']) {
$change = true;
$time['heure_debut'] = $remove_range['heure_fin'];
}
}
}
}
if($change) {
foreach($schedule as &$values) {
usort($values,'compare_schedule');
}
}
return $change;
}
function compare_schedule($a,$b) {
return strtotime($a['heure_debut']) - strtotime($b['heure_debut']);
}
function removeFromSchedule(&$schedule,$remove) {
foreach($remove as $k => &$v) {
foreach($v as $k2 => &$v2) {
$v2['heure_debut'] = substr($v2['heure_debut'],0,5);
$v2['heure_fin'] = substr($v2['heure_fin'],0,5);
}
}
while(removeSessionsFromScheduleHelper($schedule,$remove));
}
removeFromSchedule($schedule,$remove);
print_r($schedule);
If you don't want to add recursion to your function then you have to kind of convert it first to seconds of available schedule array matrix. Here the idea:
function scheduleToSecondsMatrix($value, $available=true){
if(!is_array($value) || empty($value))
return false;
$object = array();
foreach($value as $v) {
$s = strtotime('1970-01-01 ' . $v['heure_debut'] . (!$available ? ' +1 seconds' : '')); // ref. http://stackoverflow.com/questions/4605117/how-to-convert-a-hhmmss-string-to-seconds-with-php
$e = strtotime('1970-01-01 ' . $v['heure_fin'] . (!$available ? ' -1 seconds' : ''));
if($e < $s) continue; // logically end time should be greater than start time
while($s <= $e) {
// i use string as key as this result will be merged: http://php.net/manual/en/function.array-merge.php
$object["in_" . $s] = $available; // means in this seconds range is available
$s++;
}
}
return $object;
}
/**
* This function assume:
* - all parameters refer to only one employee
*/
function removeSessionsFromScheduleRev($schedule, $remove) {
if(!is_array($schedule) || !is_array($remove) || empty($schedule) || empty($remove)) return false;
foreach($schedule as $s => &$dispo){
if(empty($remove[$s]))
continue;
// convert the schedule to seconds array matrix, that's i call it :)
$seconds_available = scheduleToSecondsMatrix($dispo, true);
$seconds_not_available = scheduleToSecondsMatrix($remove[$s], false);
if( !$seconds_available || !$seconds_not_available ) continue; // nothing changed
$seconds_new = array_merge($seconds_available, $seconds_not_available);
$seconds_new = array_filter($seconds_new); // remove empty/false value
$new_time_schedule = array();
$last_seconds = 0;
$i=0;
foreach($seconds_new as $in_seconds => $val){
$in_seconds = intval(str_replace('in_', '', $in_seconds));
if($in_seconds > ($last_seconds+1)){
if(!empty($new_time_schedule)) $i++;
}
if(empty($new_time_schedule[$i]['start'])) $new_time_schedule[$i]['start'] = $in_seconds;
$new_time_schedule[$i]['end'] = $in_seconds;
$last_seconds = $in_seconds;
}
foreach($new_time_schedule as $idx => $val){
if($idx && empty($dispo[$idx])) $dispo[$idx] = $dispo[$idx-1];
$dispo[$idx]['heure_debut'] = date('H:i:s', $val['start']);
$dispo[$idx]['heure_fin'] = date('H:i:s', $val['end']);
}
}
return $schedule;
}
I haven't benchmark the performance yet so you may try this code on yours. I hope it works.
I think jma127 is on the right track with their pseudocode. Let me supplement their answer with some commentary.
Your basic structure is to loop through entries of $schedule, and then for each one, pull out the corresponding entry from $remove, and make some changes. As soon as a change happens, you break out of the loop, and start over again. The control structure you use to start over again is a recursive call. When you start over again, you loop again through all the entries of $schedule which you've already checked and don't need to change anymore.
Array $schedule and array $remove are related through shared subscripts. For a given index i, $remove[i] affects only $schedule[i] and no other part. If there is no entry $remove[i], then $schedule[i] is unchanged. Thus jma127 is right to restructure the loop to iterate first through entries of $remove, and have an inner code block to combine the entries of $remove[i] and $schedule[i]. No need for recursion. No need for repeatedly iterating over $schedule.
I believe this is the major reason your code becomes slow as the number of entries increases.
For a given day's entries in $remove and $schedule, the way you combine them is based on start times and end times. jma127 is right to point out that if you sort the day's entries by time (start time firstly, and end time secondly), then you can make a single pass through the two arrays, and end up with the correct result. No need for recursion or repeated looping.
I believe this is a secondary reason your code becomes slow.
Another thing I notice about your code is that you frequently put code inside a loop that isn't affected by the loop. It would be a tiny bit more efficient to put it outside the loop. For instance, your validity check for $remove and $schedule:
if (is_array($schedule) && count($schedule) > 0 \
&& is_array($remove) && count($remove) > 0)...
is repeated every time the routine is called recursively. You could instead move this check to an outer function, which calls the inner function, and the inner function won't need to check $remove and $schedule again:
function removeSessionsFromSchedule_outer($schedule, $remove) {
if ( is_array($schedule) && count($schedule) > 0
&& is_array($remove) && count($remove) > 0 ) {
$schedule = removeSessionsFromSchedule($schedule, $remove);
}
return $schedule;
}
Similarly,
foreach ($dispo as $d) {
if (isset($remove[$s])) {
$abs = $remove[$s];
} else
$abs = null;
// rest of loop....
}/*foreach*/
could be rewritten as:
if (isset($remove[$s])) {
$abs = $remove[$s];
} else
$abs = null;
foreach ($dispo as $d) {
// rest of loop....
}/*foreach*/
Another minor inefficiency is that your data structures don't contain the data in the format that you need. Instead of receiving a structure with data like:
[2012-11-14] => Array( [0] => Array(..."heure_debut" => 16:00 ...))
and each time during the loop, doing a data conversion like:
$abs_s = strtotime($a['heure_debut']);
How about having your upstream caller convert the data themselves:
["2012-11-14"] => Array([0]=>Array(..."heure_debut"=>strtotime("16:00") ...))
Another little detail is that you use syntax like 2012-11-14 and 16:00. PHP treats these as strings, but your code would be clearer if you put them in quotes to make it clear they are strings. See Why is $foo[bar] wrong? in PHP documenation Arrays.
I won't try to rewrite your code to make all these changes. I suspect you can figure that out yourself, looking at my comments and jma127's answer.
You have an availability schedule, implemented as a 2D array on day and entry number, and an absence schedule, implemented the same way, both sorted on time, and wish to update first using the second.
Both arrays are indexed the same way on their major dimension (using dates), so we can safely work on each of these rows without fear of modifying the rest of the arrays.
For a given day:
Within a day the simplest way to do it is to loop through all the $remove entries, and for each match on the employee_id, check the time and modify the schedule accordingly (something you already implemented, so we can reuse some of it). You want to keep the day schedule in order of time. The original arrays are well sorted, and if we store the modification in a new array in order creation, we won't have to sort it afterwards.
<?php
// create a schedule entry from template, with begin & end time
function schedule($tmpl, $beg, $end) {
$schedule = $tmpl;
$schedule['heure_debut'] = date("H:i", $beg);
$schedule['heure_fin'] = date("H:i", $end);
return $schedule;
}
// return one updated entry of a schedule day, based on an absence
function updateAvailability($d, $a){
// absence start/end
$dis_s = strtotime($d['heure_debut']);
$dis_e = strtotime($d['heure_fin']);
$abs_s = strtotime($a['heure_debut']);
$abs_e = strtotime($a['heure_fin']);
// Tests to see the if there is overlap between absence and availability
// (2) [a_s]---[ds - de]---[a_e]
if ($abs_s <= $dis_s && $abs_e >= $dis_e) {
return array();
}
// (7)[as == ds] && [ae < de]
else if ($abs_s == $dis_s && $abs_e < $dis_e) {
return array(schedule($d,$abs_e,$dis_e));
}
// (1)[ds] --- [as] --- [ae] --- [de] (duplicate dis with new times)
else if ($abs_s > $dis_s && $abs_e <= $dis_e) {
// new times as :
// s1 = ds-as && s2 = ae-de
return array(schedule($d,$dis_s,$abs_s), schedule($d,$abs_e,$dis_e));
}
// (3)[as] -- [ds] --- [ae] -- [de]
else if ($abs_s < $dis_s && $abs_e < $dis_e) {
return array(schedule($d,$abs_e,$dis_e));
}
// (4) [ds]---[as]--- [de]--- [ae]
else if ($abs_s > $dis_s && $abs_s < $dis_e && $abs_e > $dis_e) {
return array(schedule($d,$dis_s,$abs_s));
}
return array($d);
}
// move through all the entries of one day of schedule, and change
function updateDaySchedule($day, $absence){
$n = array();
foreach($day as $avail){
// intersect availability with absence
$a = updateAvailability($avail,$absence);
// append new entries
$n = array_merge($n, $a);
}
return $n;
}
function removeSessionsFromSchedule($schedule, $remove) {
if (!checkValidScheduleInput($schedule,$remove)
return $schedule;
foreach($remove as $day => $absences) {
// only update matching schedule day
if (isset($schedule[$day])) {
foreach ($absences as $abs)
$schedule[$day] = updateDaySchedule($schedule[$day], $abs);
}
}
return $schedule;
}
?>
There's still some room for improvement:
the $dis_s, $dis_e, etc. values in updateAvailability are recomputed each time, whereas some could be computed once, and passed in as parameter to the function. It may not be worth the hassle though.
the 'heure_debut' etc. constants could be made as defined constants:
define('HD','heure_debut');
This avoid possible typos (php will tell you if a constant is mispelled, but it won't tell you for a string literal), and make it easier for refactoring if the key names have to change.
The recursive nature of the function is your problem, nothing else in your function takes much processing power, so this should be quite fast. You really need to find a way to do this processing without recursing.

Categories