php array loop with sub loop - php

I would like to process an array in PHP to generate a new one.
The case is that I have an array with sales request and want to generate ticket request from that.
The issue I have is that depending on the type of sales request, 1 or more tickets are requered to be generated. If more tickets are requered, the only difference is the ticket number has a suffix of -1 or -2 added to the general ticket number.
I could do a foreach on the array and then a IF / ELSE if on the sales type and then set all the new array keys (which are all the same except for the ticket number)
But because I have many sales requests / lines this would be hard to maintain and I think not good for performance.
Example "semi" code:
$ticket = array ();
foreach ( $input as $k=>$v )
{
$ticket[$k] ['exp_date'] = date('Y-m-d', strtotime('+1 year', strtotime($input[$k] ['sales_date'])) );
$ticket[$k] ['ticket_number'] = input[$k] ['ticketnumber']; // if 1 day, ticket number, if 2 days ticket number + '-2' If multiple tickets are used, the first ticket should also have '-1'
$ticket[$k] ['ticket_type'] = $input[$k] ['product'];
$ticket[$k] ['sales_date'] = $input[$k] ['sales_date'];
$ticket[$k] ['sales_ticket_number'] = $input[$k] ['ticket_number'] ;
$ticket[$k] ['days'] = '0'; // not yet in use
$ticket[$k] ['days_remaining'] = '0'; // not yes in use
}
// if
// if ($input[$k] ['product'] == '1-day') { $loop is null}
// elseif ($input[$k] ['product'] == '2-days') { $loop is 2}
// elseif ($input[$k] ['product'] == '3-days') { $loop is 3}
Suggestions / tips are much appreciated !
/EDIT
Flow logic without code and sub-optimal:
foreach ($input as $k=>$v)
{
if ( $input[$k] ['product'] == '1-day')
{
create new lines in new array
}
else if ( $input[$k] ['product'] == '2-days')
{
loop 2 times
create same entries, but ticket number = ticketnumber-1 and second loop ticketnumber-2
}

From what I understand, you want something like this:
$ticket = array ();
foreach ( $input as $k=>$v )
{
// add the shared data for all tickets of one product to temporary ticket
$temp_ticket['exp_date'] = date('Y-m-d', strtotime('+1 year', strtotime($input[$k] ['sales_date'])) );
$temp_ticket['ticket_type'] = $input[$k]['product'];
$temp_ticket['sales_date'] = $input[$k]['sales_date'];
$temp_ticket['sales_ticket_number'] = $input[$k]['ticket_number'] ;
$temp_ticket['days'] = '0'; // not yet in use
$temp_ticket['days_remaining'] = '0'; // not yes in use
// get 'product' and retrieve the number of days
$days = (int) $input[$k]['product']; // '1-day' becomes 1, '2-days' becomes 2, ...
for ($d = 1; $d <= $days; $d++) {
// loop through the number of days and add to ticket array
// creates one ticket for each day of a product
$ticket[$k . '-' . $d] = $temp_ticket;
$ticket[$k . '-' . $d]['ticket_number'] = input[$k]['ticketnumber'] . '-' . $d;
}
}
Notes: $ticket[$k] (as in your code) can't be used multiple times since the data would be overwritten. That's why I used $ticket[$k . '-' . $d] to generate one ticket entry for each day of each sale. Since the rest of the ticket data seems to be the same, I can generate the temp ticket before and make copies in the for loop.
Example of input:
$input = array(
'order_1234' => array(
'sales_date' => '2013-12-31',
'product' => '1-day',
'ticket_number' => '1234',
),
'order_5678' => array(
'sales_date' => '2014-03-31',
'product' => '3-days',
'ticket_number' => '5678',
),
...
);
My code would produce output like this
$ticket = array(
'order_1234-1' => array(
'exp_date' => '2014-12-31',
'sales_date' => '2013-12-31',
'ticket_type' => '1-day',
'sales_ticket_number' => '1234',
'days' => '0',
'days_remaining' => '0',
'ticket_number' => '1234-1',
),
'order_5678-1' => array(
'exp_date' => '2015-03-31',
'sales_date' => '2014-03-31',
'ticket_type' => '3-days',
'sales_ticket_number' => '5678',
'days' => '0',
'days_remaining' => '0',
'ticket_number' => '5678-1',
),
'order_5678-2' => array(
'exp_date' => '2015-03-31',
'sales_date' => '2014-03-31',
'ticket_type' => '3-days',
'sales_ticket_number' => '5678',
'days' => '0',
'days_remaining' => '0',
'ticket_number' => '5678-2',
),
'order_5678-3' => array(
'exp_date' => '2015-03-31',
'sales_date' => '2014-03-31',
'ticket_type' => '3-days',
'sales_ticket_number' => '5678',
'days' => '0',
'days_remaining' => '0',
'ticket_number' => '5678-3',
),
...
);

If I understand the question correctly, which i may not have done you could do:
$prod = strpos($input[$k] ['product'], "-");
if ($prod == '1'):
$loop is null;
else:
$loop is $prod;
endif;

You can create an array holding all elements except the ticket number.
Then for each ticket you copy this array and add the ticket number.
Unfortunately I did not find a better way to copy a PHP array than this:
http://php.net/manual/de/arrayobject.getarraycopy.php

Related

JSON php extract data (where a statement is true)

I'm trying to catch specific data from a weather forecast API request (JSON output). The request gives me data for the next 3 days but I only need the next day. Here a snippet of the output:
array (
'cod' => '200',
'message' => 0.00259999999999999988065102485279567190445959568023681640625,
'cnt' => 40,
'list' =>
array (
0 =>
array (
'dt' => 1526461200,
'main' =>
array (
'temp' => 292.93000000000000682121026329696178436279296875,
'temp_min' => 292.05000000000001136868377216160297393798828125,
'temp_max' => 292.93000000000000682121026329696178436279296875,
'pressure' => 1019.259999999999990905052982270717620849609375,
'sea_level' => 1029.170000000000072759576141834259033203125,
'grnd_level' => 1019.259999999999990905052982270717620849609375,
'humidity' => 71,
'temp_kf' => 0.88000000000000000444089209850062616169452667236328125,
),
'weather' =>
array (
0 =>
array (
'id' => 802,
'main' => 'Clouds',
'description' => 'scattered clouds',
'icon' => '03d',
),
),
'clouds' =>
array (
'all' => 36,
),
'wind' =>
array (
'speed' => 4.20000000000000017763568394002504646778106689453125,
'deg' => 37.00240000000000151203494169749319553375244140625,
),
'sys' =>
array (
'pod' => 'd',
),
'dt_txt' => '2018-05-16 09:00:00',
),
1 =>
array (
'dt' => 1526472000,
'main' =>
array (
'temp' => 293.6100000000000136424205265939235687255859375,
'temp_min' => 292.95800000000002683009370230138301849365234375,
'temp_max' => 293.6100000000000136424205265939235687255859375,
'pressure' => 1019.799999999999954525264911353588104248046875,
'sea_level' => 1029.65000000000009094947017729282379150390625,
'grnd_level' => 1019.799999999999954525264911353588104248046875,
'humidity' => 66,
'temp_kf' => 0.66000000000000003108624468950438313186168670654296875,
),
'weather' =>
array (
0 =>
array (
'id' => 803,
'main' => 'Clouds',
'description' => 'broken clouds',
'icon' => '04d',
),
),
'clouds' =>
array (
'all' => 56,
),
'wind' =>
array (
'speed' => 5.79999999999999982236431605997495353221893310546875,
'deg' => 38.0009000000000014551915228366851806640625,
),
'sys' =>
array (
'pod' => 'd',
),
'dt_txt' => '2018-05-16 12:00:00',
)
I'm trying to get the 'all' values where 'dt_txt' starts from 0am the next day to 0pam the day after.
For now I'm using the following php code without checking for dt_txt:
<?php
$url = "http://api.openweathermap.org/data/2.5/forecast?q=D%C3%BClmen,de&mode=json";
$response = file_get_contents($url);
$obj = json_decode($response);
$t = 0;
$regen = 0;
$regendiv = 0;
for($i=0; $i < 10; $i++) {
$fg = $obj->list[$i]->clouds->all;
$regen += $fg;
}
echo $regen;
?>
My code requires me to run the request at a specific time (like close to midnight) in order to catch the 'all' values for the next day. Is there any way to check for 'dt_text' = the next day?
My Idea would be something like this:
for($i=0; $i < 10; $i++) {
$fg = $obj->list[$i]->clouds->all;
if (strpos($obj->list[$i]->dt_txt), $date){
$regen += $fg;
}
**//But how do I get $date to be the following day**
}
echo $regen;
?>
I was able to solve it this way:
<?php
$nextWeek = time() + (24 * 60 * 60);
$morgen = date('Y-m-d', $nextWeek);
$url = "http://api.openweathermap.org/data/2.5/forecast?q=D%C3%BClmen,de&mode=json";
$response = file_get_contents($url);
$obj = json_decode($response);
$quote = 0;
for($i=0; $i < 14; $i++) {
if (strpos($obj->list[$i]->dt_txt, $morgen) !== false) {
$fg = $obj->list[$i]->clouds->all;
if($fg > 80) { $t = 1; }
$regen += $fg;
$quote++;
}
}
$regendiv = $regen / $quote;
?>
Probably not the prettiest way but it gets the job done.

sorting a multi dimensional array in php

I have an array of arrays, as such
$statuses = array(
[0] => array('id'=>10, 'status' => 'active'),
[1] => array('id'=>11, 'status' => 'closed'),
[2] => array('id'=>12, 'status' => 'active'),
[3] => array('id'=>13, 'status' => 'stopped'),
)
I want to be able to make a new array of arrays and each of those sub arrays would contain the elements based on if they had the same status.
The trick here is, I do not want to do a case check based on hard coded status names as they can be random. I want to basically do a dynamic comparison, and say "if you are unique, then create a new array and stick yourself in there, if an array already exists with the same status than stick me in there instead". A sample result could look something like this.
Ive really had a challenge with this because the only way I can think to do it is check every single element against every other single element, and if unique than create a new array. This gets out of control fast if the original array is larger than 100. There must be some built in functions that can make this efficient.
<?php
$sortedArray = array(
['active'] => array(
array(
'id' => 10,
'status' => 'active'
),
array(
'id' => 12,
'status' => 'active'
)
),
['closed'] => array(
array(
'id' => 11,
'status' => 'active'
)
),
['stopped'] => array(
array(
'id' => 13,
'status' => 'active'
)
),
)
$SortedArray = array();
$SortedArray['active'] = array();
$SortedArray['closed'] = array();
$SortedArray['stopped'] = array();
foreach($statuses as $Curr) {
if ($Curr['status'] == 'active') { $SortedArray['active'][] = $Curr; }
if ($Curr['status'] == 'closed') { $SortedArray['closed'][] = $Curr; }
if ($Curr['status'] == 'stopped') { $SortedArray['stopped'][] = $Curr; }
}
You can also do it with functional way though it's pretty the same like Marc said.
$sorted = array_reduce($statuses, function($carry, $status) {
$carry[$status['status']][] = $status;
return $carry;
}, []);

How many times an element shows in a multidimensional array

I need to know how many times a "phase" shows up, and if it's completed or not.
The final objective is to show:
Phase 1: 50% completed
Phase 2: 0% completed
Tasks:
Array
(
[0] => Array
(
[phase] => 1
[tasks_status] => completed
)
[1] => Array
(
[phase] => 1
[tasks_status] => pending
)
[2] => Array
(
[phase] => 2
[tasks_status] => pending
)
)
Phases:
Array
(
[0] => Array
(
[title] => Briefing e Planejamento
[id] => 1
[created] => 1437436800
)
[1] => Array
(
[title] => Rabiscos e Design
[id] => 2
[created] => 1438128000
)
)
edit
So, with the help of you guys who answered, I've managed to do it! Here's the result:
/** Define wich tasks has been completed **/
foreach ($tasks as $task) {
if ($task['status'] == "completed") {
foreach ($fases as &$fase) {
if ($fase['id'] == $task['fase_id']) {
$fase['numero_total_fases']++;
$fase['completed']++;
}
}
} elseif ($task['status'] == "pending") {
foreach ($fases as &$fase) {
if ($fase['id'] == $task['fase_id']) {
$fase['numero_total_fases']++;
$fase['pending']++;
}
}
} elseif ($task['status'] == "behind schedule") {
foreach ($fases as &$fase) {
if ($fase['id'] == $task['fase_id']) {
$fase['numero_total_fases']++;
$fase['behind schedule']++;
}
}
}
unset($fase);
}
/** Define progress percentage **/
foreach ($fases as &$fase) {
$fase['progresso'] = round(($fase['completed'] / ($fase['numero_total_fases'])) * 100);
}
/** Print phases progress **/
$f = 1;
foreach ($fases as $fase) {
echo 'Fase '.$f.' - '.$fase['title'].' = '.$fase['progresso'].'% completed';
}
A little slow to the draw, but since I already wrote it, here's my answer too. It's always helpful to see that most programming tasks can be done several ways:
<?php
$tasks = array(
array(
'phase' => 1,
'tasks_status' => 'completed'
),
array(
'phase' => 1,
'tasks_status' => 'pending'
),
array(
'phase' => 2,
'tasks_status' => 'pending'
)
);
//$phases = array();
$phases = array(
1=>array(
'title'=>'Briefing e Planejamento',
'id'=>1,
'created'=>1437436800
),
2=>array(
'title'=>'Rabiscos e Design',
'id'=>2,
'created'=>1438128000
)
);
foreach($tasks as $key=>$task){
if(isset($phases[$task['phase']])){
$thisPhase = $phases[$task['phase']];
}
else{
$thisPhase = array(
'completed' => 0,
'pending' => 0
);
}
$thisPhase[$task['tasks_status']]++;
$phases[$task['phase']] = $thisPhase;
}
foreach($phases as $name=>$phase){
echo $phase['title'].": ".round(($phase['completed'] / ($phase['completed'] + $phase['pending'])) * 100)."% completed <br>";
}
?>
I know you've already finished, but regarding the phases array - you should have put that in the original question if you wanted it worked into the answer. My suggestion, however, would be to use the ID of the phase as the index name in your phases array. The solution I posted will work with that too - and you can then use $phase in the final foreach loop to show the title or created time.
Is this what you are after?
I assume you want to calculate the percentage based on your original array.
<?php
$tasks = array(
array(
'phase' => 1,
'tasks_status' => 'completed'
),
array(
'phase' => 1,
'tasks_status' => 'pending'
),
array(
'phase' => 2,
'tasks_status' => 'pending'
),
);
$phases_arr = array();
foreach($tasks as $task) {
if(!array_key_exists($task['phase'], $phases_arr)) {
$phases_arr[$task['phase']] = array();
}
$phases_arr[$task['phase']][] = $task;
}
$phases = array();
foreach($phases_arr as $phase_num => $phase) {
$p = array(
'phase' => $phase_num,
'total' => count($phase),
'complete' => 0,
'percent' => 0
);
foreach($phase as $task) {
if($task['complete']) $p['complete']++;
}
$p['percent'] = ($p['complete'] / $p['total']) * 100;
$phases[$phase_num] = $p;
}
foreach($phases as $phase) {
echo "<p>Phase {$phase['phase']}: {$phase['percent']}% completed</p>";
}
Your inputs,
$tasks = array( array('phase' => 1, 'tasks_status' => 'completed'),
array('phase' => 1, 'tasks_status' => 'pending'),
array('phase' => 2, 'tasks_status' => 'pending'),
);
Consider this,
$phase = array();
foreach ($tasks as $key => $value)
{
$phase[$value['phase']][] = $value['tasks_status'] == 'completed' ? 50 : 0 ;
}
function get_phase_percentage($i)
{
global $phase;
if(isset($phase[$i]) && is_array($phase[$i]))
return array_sum($phase[$i]);
return 0;
}
Use it like,
Phase 1: <?php echo get_phase_percentage(1)?>% completed
Phase 2: <?php echo get_phase_percentage(2)?>% completed

Merge array elements within time range - how?

I have an array that contain user's activities on the website. It contains activities such as writing comments, news and groups. If two comments (or more) from different users have been written within an hour, I would like to gather those two arrays into one: User and 2 more commented on X. The code I have so far looks like this:
<?php
$output = array();
$output[] = array('userID' => 12, 'txt' => sprintf('%s commented in %s', 'User1', 'Event'), 'date' => 1393080072);
$output[] = array('userID' => 13, 'txt' => sprintf('%s commented in %s', 'User2', 'Event'), 'date' => 1393080076);
$output[] = array('userID' => 13, 'txt' => sprintf('%s created the news %s', 'User2', 'RANDOMNEWS'), 'date' => 1393080080);
$output[] = array('userID' => 14, 'txt' => sprintf('%s commented in %s', 'User3', 'Event'), 'date' => 1393080088);
$date = array();
foreach($output as $k => $d) {
$date[$k] = $d['date'];
}
array_multisort($date, SORT_DESC, $output);
print_r($output);
?>
So far the code above sorts the arrays by date (DESC). Desired result: one array: %s and 2 more commented in... and the other arrays removed from output. So by taking the latest comment and checking the date from the rest of the comments, it should be possible to handle this. I simply need some suggestions.
Thanks in advance
From what I understand from your question, I think you want to find out the number of users commenting in the last hour with respect to the latest commentor.
Using your logic, array_filter can help get those values which lie in the last hour.
This is the continuation of your code -
/*
...your code...
*/
$latest_time = $output[0]['date'];
$hour_past_time = $latest_time - 3600;
$user_ids = Array();
$res=array_values(
array_filter($output,function($arr)use($latest_time, $hour_past_time,&$user_ids){
if(
$arr["date"] <= $latest_time &&
$arr["date"] >= $hour_past_time &&
in_array($arr['userID'],$user_ids) == false
){
$user_ids[] = $arr['userID'];
return true;
}
}
)
);
echo "Users with their latest comments in the past hour- <br />";
var_dump($res);
$latest_user_id = "User".$res[0]['userID'];
$rest = count($res) - 1;
echo "<br />$latest_user_id and $rest more commented.<br />";
OUTPUT -
Users with their latest comments in the past hour-
array
0 =>
array
'userID' => int 14
'txt' => string 'User3 commented in Event' (length=24)
'date' => int 1393080088
1 =>
array
'userID' => int 13
'txt' => string 'User2 created the news RANDOMNEWS' (length=33)
'date' => int 1393080080
2 =>
array
'userID' => int 12
'txt' => string 'User1 commented in Event' (length=24)
'date' => int 1393080072
User14 and 2 more commented.
Hope this helps-

how can you merge arrays systematically without losing data?

so I'm trying to write a method that will merge all the arrays with a certain key value, but the problem I'm running into is that when I try and unset the array, so that there are not duplicate results it skips over several things, which is confusing me. so any advice on how I can improve this method would be greatly appreciated. the other question I have.. is there a way to check if each of these arrays have all 4 keys that I'm looking for.
'Release Date' =>
'Spreadsheet and Flyer Month' =>
'Advertise in Monthly Update' =>
'Feature in Catalog' =>
so what I'm doing is merging arrays with the same id in the db, so I don't have to do some really nasty SQL querys, but I'm wondering if there's a way of making sure that all 4 of these keys will be in every result... and if there is a value associated with one or however many.. my method will add the value to its key, and if there is no value associated with the key it will just make a empty string.
protected function array_with_same_val($array, $key) {
for($i = 0; $i < count($array); $i++) {
for($j = 1; $j < count($array); $j++) {
if(isset($array[$j]) && isset($array[$i]) && $array[$i][$key]==$array[$j][$key]) {
$temp = array_merge($array[$i], $array[$j]);
$array[$i] = $temp;
//unset($array[$j]);
}
}
}
return $array;
}
Here is a sample of my array (there will be a lot more values, this is just to give an idea):
'0' => array
(
'Release Date' => 'September 1, 2013',
'cp_id' => '112960'
),
'1' => array
(
'Spreadsheet and Flyer Month' => 'September 1, 2013',
'cp_id' => '112960'
),
'2' => array
(
'Advertise in Monthly Update' => 'September 1, 2013',
'cp_id' => '112960'
),
'3' => array
(
'Release Date' => 'September 1, 2013',
'cp_id' => '109141'
),
);
any help on this would be greatly appreciated.
Try this ($input is the array you provided above) ...
$output = array();
$requiredKeys = array('Release Date' => '', 'Spreadsheet and Flyer Month' => '', 'Advertise in Monthly Update' => '', 'Feature in Catalog' => '');
foreach ($input as $item) {
if (array_key_exists($item['cp_id'], $output)) {
$output[$item['cp_id']] = array_merge($output[$item['cp_id']], $item);
} else {
$output[$item['cp_id']] = array_merge($requiredKeys, $item);
}
}
$output = array_values($output);
The array_values call at the bottom is just to remove the string keys from the array.

Categories