I have the following structure of data in an array and I am trying to calculate the total duration:
$elements = array(
'elementfrom-work' => "09:00",
'elementto-work' => "17:00",
'elementdays-work' => "5",
'elementfrom-karate' => "18:00",
'elementto-karate' => "20:00",
'elementdays-karate' => "3",
'elementfrom-stamp' => "21:00",
'elementto-stamp' => "22:00",
//it doest have the default days 'elementdays-stamp' set
//so it will take the default 7
'element-simple1' => "4", //it will take the default 7
'element-simple2' => "8", //it will take the default 7
'element-simple3' => "1",
'elementdays-simple3' => "1", //day is set
);
I have managed to do it but my code is messy, for each item it gets the sub string and runs another for loop to check if any other elements exists when it is not simple (like days) .
I am trying to calculate for each item the total duration e.g outcome is:
Work:40
Karate:6
Stamp:7
Simple1=28
Simple2=56
Simple3=1
total duration:138
Can this be done without two for loops and how ? If it is not possible how would you calculate it.
I actually found this problem quite interesting, so you can do something like:
$elements[] = array(
'elementfrom-work' => "09:00",
'elementto-work' => "17:00",
'elementdays-work' => "7",
'elementfrom-karate' => "18:00",
'elementto-karate' => "20:00",
'elementdays-karate' => "3",
'elementfrom-stamp' => "21:00",
'elementto-stamp' => "22:00",
'a' => "21:00",
'b' => "22:00"
);
And use those two functions:
function negative($x)
{
if($x < 0)
{
return -$x;
}
return $x;
}
function isTime($string)
{
$split = explode(":", $string);
if(isset($split[1]))
{
return true;
}
return false;
}
foreach($elements as $key => $val)
{
$total = 0;
$temp = 0;
$i = 0;
foreach($val as $innerKey => $time)
{
$isTime = isTime($time);
$split = explode(":", $time);
$h = $split[0];
switch($i)
{
case 0:
$temp -= $h;
break;
case 1:
$temp += $h;
break;
case 2:
if($isTime)
{
$mult = $temp *= 7;
$unsigned = negative($mult);
$total += $unsigned;
$temp = 0;
$temp -= $h;
$i = 0;
break;
}
$mult = $temp *= $h;
$unsigned = negative($mult);
$total += $unsigned;
$temp = 0;
$i = -1;
break;
default:
break;
}
$i++;
}
echo $total;
}
Your idea of the carry is a bit funky but something like that should work.
It looks like you know the expected name of the days item... check if it's exists instead of looping, iterate the array keys, when you get to "elementfrom-work" you can check if array got key "elementdays-work" instead of re iterate to search this item in N level complexity...
Related
I have a already defined array, containing values just like the one below:
$arr = ['a','b','c'];
How could one add the following using PHP?
$arr = [
'a' => 10,
'b' => 5,
'c' => 21
]
I have tried:
$arr['a'] = 10 but it throws the error: Undefined index: a
I am surely that I do a stupid mistake.. could someone open my eyes?
Full code below:
$finishes = []; //define array to hold finish types
foreach ($projectstages as $stage) {
if ($stage->finish_type) {
if(!in_array($stage->finish_type, $finishes)){
array_push($finishes, $stage->finish_type);
}
}
}
foreach ($projectunits as $unit) {
$data[$i] = [
'id' => $unit->id,
'project_name' => $unit->project_name,
'block_title' => $unit->block_title,
'unit' => $unit->unit,
'core' => $unit->core,
'floor' => $unit->floor,
'unit_type' => $unit->unit_type,
'tenure_type' => $unit->tenure_type,
'floors' => $unit->unit_floors,
'weelchair' => $unit->weelchair,
'dual_aspect' => $unit->dual_aspect
];
$st = array();
$bs = '';
foreach ($projectstages as $stage) {
$projectmeasure = ProjectMeasure::select('measure')
->where('project_id',$this->projectId)
->where('build_stage_id', $stage->id)
->where('unit_id', $unit->id)
->where('block_id', $unit->block_id)
->where('build_stage_type_id', $stage->build_stage_type_id)
->first();
$st += [
'BST-'.$stage->build_stage_type_id => ($projectmeasure ? $projectmeasure->measure : '0')
];
if (($stage->is_square_meter == 0) && ($stage->is_draft == 0)) {
$height = ($stage->height_override == 0 ? $unit->gross_floor_height : $stage->height_override); //08.14.20: override default height if build stage type has it's own custom height
$st += [
'BST-sqm-'.$stage->build_stage_type_id => ($projectmeasure ? $projectmeasure->measure * $height: '0')
];
if ($stage->finish_type) {
$finishes[$stage->finish_type] += ($projectmeasure ? $projectmeasure->measure * $height: '0') * ($stage->both_side ? 2 : 1); //error is thrown at this line
}
} else {
if ($stage->finish_type) {
$finishes[$stage->finish_type] += ($projectmeasure ? $projectmeasure->measure : '0');
}
}
}
$data[$i] = array_merge($data[$i], $st);
$data[$i] = array_merge($data[$i], $finishes[$stage->finish_type]);
$i++;
}
The above code is used as is and the array $finishes is the one from the first example, called $arr
You're using += in your real code instead of =. That tries to do maths to add to an existing value, whereas = can just assign a new index with that value if it doesn't exist.
+= can't do maths to add a number to nothing. You need to check first if the index exists yet. If it doesn't exist, then assign it with an initial value. If it already exists with a value, then you can add the new value to the existing value.
If you want to convert the array of strings to a collection of keys (elements) and values (integers), you can try the following:
$arr = ['a','b','c'];
$newVals = [10, 5, 21];
function convertArr($arr, $newVals){
if(count($arr) == count($newVals)){
$len = count($arr);
for($i = 0; $i < $len; $i++){
$temp = $arr[$i];
$arr[$temp] = $newVals[$i];
unset($arr[$i]);
}
}
return $arr;
}
print_r(convertArr($arr, $newVals));
Output:
Array ( [a] => 10 [b] => 5 [c] => 21 )
I have a question about how to make an iteration. I want to place a total row after each item in the array if the next element in the array matches a specific condition. Spesific conditions have logic like this
the data like this
if i request a qty for example = 60 the result i hope like this
you can see
data[2] = 01/03/2020 just took 10 out of 40
$iter = new \ArrayIterator($values);
$sum = 0;
foreach($values as $key => $value) {
$nextValue = $iter->current();
$iter->next();
$nextKey = $iter->key();
if(condition) {
$sum += $value;
}
}
dd($iter);
how to make this logic work on php language/ laravel?
Following logic might help you on your way:
<?php
$stock = [
'01/01/2020' => 20,
'01/02/2020' => 30,
'01/03/2020' => 40
];
showStatus($stock, 'in stock - before transaction');
$demand = 60;
foreach ($stock as $key => $value) {
if ($value <= $demand) {
$stock[$key] = 0;
$supplied[$key] = $value;
$demand -= $value;
} else {
$stock[$key] -= $demand;
$supplied[$key] = $value - ($value - $demand);
$demand = 0;
}
}
showStatus($supplied, 'supplied');
showStatus($stock, 'in stock - after transaction');
function showStatus($arr = [], $msg = '')
{
echo $msg;
echo '<pre>';
print_r($arr);
echo '</pre>';
}
?>
**Output:**
in stock - before transaction
Array
(
[01/01/2020] => 20
[01/02/2020] => 30
[01/03/2020] => 40
)
supplied
Array
(
[01/01/2020] => 20
[01/02/2020] => 30
[01/03/2020] => 10
)
in stock - after transaction
Array
(
[01/01/2020] => 0
[01/02/2020] => 0
[01/03/2020] => 30
)
Working demo
I'm not sure I've understood you correctly but this might help:
$values = [
'01/01/2020' => 20,
'01/02/2020' => 30,
'01/03/2020' => 40
];
$demand = 60;
$total = array_sum($values);
$decrease = $total - $demand; //(20+30+40) - 60 = 30
$last_key = array_keys($values,end($values))[0]; //Is 01/03/2020 in this case
$values[$last_key] -= $decrease; //Decrease value with 30 calulated above
Would output:
Array
(
[01/01/2020] => 20
[01/02/2020] => 30
[01/03/2020] => 10
)
I have 2 sets of multidimensional array ($profit & $sales). I want to divide the numbers in 2 multidimensional array to get the % of margin (using this formula: $profit/$sales*100)
$profit= array(
0 => array(
"no"=> "1",
"value"=>"10"
),
1=> array(
"no"=> "2",
"value"=>"15"
)
);
$sales= array(
0 => array(
"no"=> "1",
"value"=>"100"
),
1=> array(
"no"=> "2",
"value"=>"200"
)
);
This is the expected output:
$margin= array(
0 => array(
"no"=> "1",
"value"=>"10"
),
1=> array(
"no"=> "2",
"value"=>"7.5"
)
);
I have done some search with no luck still, below is the function that I'm using, it is not working:
function ArrayDivide($arrayList = [])
{
$m = [];
$no_details = [];
$i = 0;
foreach ($arrayList as $arrayItem) {
foreach ($arrayItem as $subArray) {
if (isset($no_details[$subArray['x']])) {//if no is exist
$m[$no_details[$subArray['x']]]['y'] = $m[$no_details[$subArray['x']]]['y'] /$subArray['y']*100;
} else {
$no_details[$subArray['x']] = $i;
$m[$i] = ["x"=>$subArray['x'], "y"=>"0"];
$i++;
}
}
}
return $m;
}
How you done similar function before? Where should I fix?
Thanks.
Use "Sourcey86" code and replace:
$res = $value / $no;
to
$res = $value/$sales[$key]['value']*100;
(I don't have enough points to add a comment on his post :D )
Something like this ? If I understood your question correctly.
function getMargin($profit, $sales){
$margin = [];
foreach($profit as $key => $val){
$no = $val['no'];
$value = $val['value'];
if(isset($sales[$key]) && $sales[$key]['no'] == $no){
$res = ($value/$sales[$key]['value'])*100;
$margin[$key]['no'] = $no;
$margin[$key]['val'] = $res;
}
}
return $margin;
}
var_dump(getMargin($profit, $sales));
I have a series of columns (Jan, Feb, Mar, etc) and I want to average the values of each column for each row however many there may be.
I have:
protected function generateAverage($staff, $type)
{
$months = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
$staff = array_map(function ($row) use ($type) {
if ($row['row_type'] == $type) {
return $row;
}
}, $staff);
foreach ($staff as $key => $employee) {
$months[0] += $employee['amounts'][0];
$months[1] += $employee['amounts'][1];
$months[2] += $employee['amounts'][2];
$months[3] += $employee['amounts'][3];
$months[4] += $employee['amounts'][4];
$months[5] += $employee['amounts'][5];
$months[6] += $employee['amounts'][6];
$months[7] += $employee['amounts'][7];
$months[8] += $employee['amounts'][8];
$months[9] += $employee['amounts'][9];
$months[10] += $employee['amounts'][10];
$months[11] += $employee['amounts'][11];
}
$months = array_map(function ($value) use ($staff) {
return $value / (count($staff) / 2);
}, $months);
$months[] = array_sum($months);
return $months;
}
Here is a sample of the data that goes into the above function:
array:6 [
0 => array:4 [
"amounts" => array:13 [
0 => "30000.00"
1 => "30000.00"
2 => "30000.00"
3 => "30000.00"
4 => "30000.00"
5 => "30000.00"
6 => "30000.00"
7 => "30000.00"
8 => "30000.00"
9 => "30000.00"
10 => "30000.00"
11 => "30000.00"
12 => 360000.0
]
"image" => "test.jpg"
"row_name" => "Target"
"row_type" => "target"
]
...
Usage:
$data['aggregates']['Target average'] = $this->generateAverage(array_values($data['staff']), 'target');
I feel the way the average is calculated is messy, is there a better way to do this?
A couple of small footprint reductions
protected function generateAverage($staff, $type)
{
// create 12 months with 0 value
$months = array_fill(0, 12, 0);
// use array_filter instead of map
$staff = array_filter(function ($row) use ($type) {
return $row['row_type'] === $type;
}, $staff);
// do count outside loop
$staffCount = count($staff);
// loop employees and add up each month, dividing early
foreach ($staff as $employee) {
for ($i = 0; $i < 12; $i++) {
$months[$i] += $employee['amounts'][$i] / $staffCount;
}
}
return $months;
}
I dont know why you are dividing the staff count by 2 or why you are summing in the end, my function just gives an average per month.
Since you're using Laravel, most of the data you work with is collections. So, before converting a collection into an array, you could use avg() helper:
$collection->avg($key);
I think you can consider using array_colum,
1) array_column makes an array of all the values in a particular index position in a row
$column1 = array_column($employee, 0 );
$column2 = array_column($employee, 1 );
$column3 = array_column($employee, 2 );
.
.
.
2)Get the column count by count($columnX), where X is the index of the column
3) Using (1) and (2) calculate the average as needed
I am trying to create a schedule of who uses a specific item on which day.
I have 2 arrays.
one with dates &
another with names and how many days they can use the item.
i have managed to create dates array using this.
function dateArray($from, $to, $value = NULL) {
$begin = new DateTime($from);
$end = new DateTime($to);
$interval = DateInterval::createFromDateString('1 day');
$days = new DatePeriod($begin, $interval, $end);
$baseArray = array();
foreach ($days as $day) {
$dateKey = $day->format("d-m-Y");
$baseArray[$dateKey] = $value;
}
return $baseArray;
}
$dates_array = dateArray('01-01-2014', '30-09-2014',true);
print_r($dates_array );
which gives me dates as
Array
(
[01-01-2014] => 1
[02-01-2014] => 1
[03-01-2014] => 1
[04-01-2014] => 1
[05-01-2014] => 1
[06-01-2014] => 1
[07-01-2014] => 1
[08-01-2014] => 1
[09-01-2014] => 1
and so on.
)
i have another array of names having name as key and days as value , they can use the item like this.
$names_array = array("name1" => "4", "name2" => "3", "name3" => "1");
Now i would like to assign names to dates depending on how many days the person can use the item.
like this.
I need my final output array to be like this
Array
(
[01-01-2014] => name1
[02-01-2014] => name1
[03-01-2014] => name1
[04-01-2014] => name1
[05-01-2014] => name2
[06-01-2014] => name2
[07-01-2014] => name2
[08-01-2014] => name3
[09-01-2014] => name1
and so on. notice name1 comes again
)
so i am trying to get output like above but i am failing at the while loop inside the foreach.
so far i have tried this.
function dateArray($from, $to, $value = NULL) {
$begin = new DateTime($from);
$end = new DateTime($to);
$interval = DateInterval::createFromDateString('1 day');
$days = new DatePeriod($begin, $interval, $end);
$baseArray = array();
foreach ($days as $day) {
$dateKey = $day->format("d-m-Y");
$baseArray[$dateKey] = $value;
}
return $baseArray;
}
$dates_array = dateArray('01-01-2014', '30-09-2014',true);
$names_array = array("name1" => "4", "name2" => "3", "name3" => "1");
print_r($dates_array );
$new_dates = array();
foreach($dates_array as $dates => $key){
//echo $dates;
foreach ($names_array as $name => $days){
while($days <= 1){
$new_dates[$dates] = $name ;
$days = $days - 1;
}
}
}
print_r($new_dates);
But my final array is empty.
so how can i solve this ?
You can use a MultipleIterator whereby the second array (names) loops around when needed:
$names_array = array();
// unwind the array values
foreach (array("name1" => "4", "name2" => "3", "name3" => "1") as $value => $freq) {
for ($i = 0; $i < $freq; ++$i) {
$names_array[] = $value;
}
}
// attach both arrays
$m = new MultipleIterator;
$m->attachIterator(new ArrayIterator(array_keys($dates_array)));
$m->attachIterator(new InfiniteIterator(new ArrayIterator($names_array)));
// build final array
$result = array();
foreach ($m as $value) {
$result[$value[0]] = $value[1];
}
It should probably go like this...
while($days <= 1){
$new_dates[$dates] = $name ;
$names_array[$name] = $days - 1;
}
Hi You can try this one
$dates_array = array_keys(dateArray('01-01-2014', '30-09-2014',true));
$names_array = array("name1" => "4", "name2" => "3", "name3" => "1");
$new_dates = array();
$daysindex = 0;
while($daysindex < count($dates_array)){
foreach ($names_array as $name => $day){
for($x = 0; $x<$day; $x++){
if(isset($dates_array[$daysindex])){
$new_dates[$dates_array[$daysindex]] = $name ;
$daysindex++;
}
}
}
}
print_r($new_dates);
$dates = dateArray('01-01-2014', '30-09-2014',true);
$names = array('name1' => 4,'name2' => 3,'name3' => 1);
$count = current($names);
$name = key($names);
foreach($dates as &$value) {
$value = $name;
//if we've displayed a name $count times, move to the next name
if(--$count == 0) {
//if we're at the end of $names, wrap around
if(false === next($names)) {
reset($names);
}
$count = current($names);
$name = key($names);
}
}
print_r($dates);