Related
like the question says, I have 2 foreach cycles, 1 cycle iterates an array with 4 keys, and the other one an array with 3 keys, how to get this two arrays in only 1 ?
I have this
....
],
],
'Detalle' => array()
];
foreach ($datos["line_items"] as $line => $item) {
$tempArray = array(
'NmbItem' => $item['name'],
'QtyItem' => $item['quantity'],
'PrcItem' => $item['price'],
'IndExe' => 1
);
}
foreach($datos["fee_lines"] as $line => $fee){
$tempArray=array(
'NmbItem' => $fee['name'],
'QtyItem' => 1,
'PrcItem' => $fee['total']
);
}
$dte['Detalle'][] = $tempArray;
}
if you notice the second array cycle doesnt contain 'indexe' key, after asign this tempArray in $dte['Detalle'][] = $tempArray only works with the last cycle, or the first one if I remove the second.
the output should be something like:
temp = Array (
array[0]
'NmbItem => name',
'QtyItem'=> 10,
'PrcItem'= 1000,
'IndExe' => 1
array[1]
'NmbItem => another name',
'QtyItem'=> 5,
'PrcItem'=> 3000
)
To make it work with your code, you should also add $dte['Detalle'][] = $tempArray; after the first loop.
The issue is that you are setting the $tempArray in each foreach so you end up with only the last one when assigning it in the $dte['Detalle'].
So, something like this should work:
foreach ($datos["line_items"] as $line => $item) {
$tempArray = array(
'NmbItem' => $item['name'],
'QtyItem' => $item['quantity'],
'PrcItem' => $item['price'],
'IndExe' => 1
);
}
$dte['Detalle'][] = $tempArray;
foreach($datos["fee_lines"] as $line => $fee){
$tempArray=array(
'NmbItem' => $fee['name'],
'QtyItem' => 1,
'PrcItem' => $fee['total']
);
}
$dte['Detalle'][] = $tempArray;
However, I would do it using the array_map function.
Docs for array_map
You can have it something like this:
<?php
$data_1 = array_map(function($item){
return array(
'NmbItem' => $item['name'],
'QtyItem' => $item['quantity'],
'PrcItem' => $item['price'],
'IndExe' => 1
);
}, $datos["line_items"]);
$data_2 = array_map(function($fee){
return array(
'NmbItem' => $fee['name'],
'QtyItem' => 1,
'PrcItem' => $fee['total']
)
}, $datos["fee_lines"]);
$dte['Detalle'] = array_merge($dte['Detalle'], $data_1, $data_2);
(In between Christmas meals) I am again stuck with the loop through array and group by logic.
here is the array I am given
$aTest = Array
(
Array
(
'date' => 2017-05-04,
'period' => 'period2',
'category' => 'Indoor room',
'score' => 1
),
Array
(
'date' => 2017-05-04,
'period' => 'period5',
'category' => 'Indoor room',
'score' => 1
),
Array
(
'date' => 2017-05-04,
'period' => 'period2',
'category' => 'Indoor room',
'score' => 2
),
Array
(
'date' => 2017-05-04,
'period' => 'period4',
'category' => 'Indoor room',
'score' => 1
),
Array
(
'date' => 2017-05-03,
'period' => 'period5',
'category' => 'Gym Class',
'score' => 1
),
Array
(
'date' => 2017-05-03,
'period' => 'period1',
'category' => 'Gym Class',
'score' => 1
),
Array
(
'date' => 2017-05-03,
'period' => 'period4',
'category' => 'Indoor room',
'score' => 1
)
);
This time I like group by category and sum the score group by period. Y-axis will be the category and X-axis will be the period. In the end I need this for a google chart
/*period, total indoor, total gym, 'total indoor', 'total gym'*/
array(
['Period1', 0,1,'0','1'],
['Period2', 3,0,'3','0'],
['Period3', 0, 0,'0','0'],
['Period4', 4,0,'4','0'],
['Period5', 1,1,'1','1']
)
I have this php code:
foreach ($aTest as $value) {
//echo $value['period'].' - '.$value['score'].'<br/>';
if (empty($output[$value]['period']))
$output[$value]['period'] = ['Period1' => 0, 'Period2' => 0, 'Period3' =>0, 'Period4' => 0, 'Period5' => 0];
if(empty($output[$value]['category']))
$output[$value['catgeory']] = ['Gym Class' => 0, 'Indoor room' =>0];
$output[$value['category']] += $value['score'];
}
ksort($output);
but this only totals the score by Category and not by period.
I think I need to loop through the periods as well, but how?
You have a wrong logic here.
if (empty($output[$value]['period']))
$output[$value]['period'] = ['Period1' => 0, 'Period2' => 0, 'Period3' =>0, 'Period4' => 0, 'Period5' => 0];
that $value is an array and you try to check $output[$value]
I saw that you don't have any line sum periods Value.
I have a solution for your data.
What is my code do??
Sum score of the period by the category
For each merge period and category score to an array using arraySort category to set position of these category scores values
$temp = [];
$output = [];
foreach($aTest as $value){
$period = $value['period'];
$category = $value['category'];
// Create default values
if (empty($temp[$period])){
$temp[$period] = [];
}
if(empty($temp[$period][$category])){
$temp[$period][$category] = 0;
}
//Sum score
$temp[$period][$category] += $value['score'];
}
//After Forech we have an array with ['period name' => ['category name' => score]];
//Sort values of the category change it if you want, you can add more option such as (item type for add '' to values)
$arraySort = [
"Indoor room", //total indoor,
"Gym Class", // total gym,
"Indoor room", //'total indoor',
"Gym Class" //'total gym'
];
foreach($temp as $period => $catsScore){
$periodItem = [$period];
foreach($arraySort as $cat){
$periodItem[] = $catsScore;
}
$output[] = $periodItem;
}
I have a requirement to allow my end users to input formula much like a spreadsheet. I have an array like this:
$table = array(
1=>array(
"id"=>1,
"Name"=>"Regulating",
"Quantity"=>"[2]Quantity+[3]Value",
"Value"=>"[2]Cost"
),
...)
The first level array key is always the same value as the id key in that array.
A tabulated example follows:
id Name Quantity Value
1 Regulating [2]Quantity+[3]Value [2]Cost
2 Kerbs 3 6
3 Bricks 9 7
4 Sausages [3]Cost 3
5 Bamboo [4]Quantity [7]Cost
6 Clams [4]Quantity NULL
7 Hardcore [3]Quantity*0.5 12
8 Beetles [6]Quantity*[4]Value [2]Value
The Quantity and Value keys represent formula which reference the [id] and either Quantity, Value or Cost.
Cost is derived by multiplying the Value and Quantity.
I am using:
preg_match_all("/\[(.*?)\]([A-Z]*[a-z]*)/", $string, $matches, PREG_SET_ORDER);
which outputs an array like so for[1][Quantity]:
Array
(
[0] => Array
(
[0] => [2]Quantity
[1] => 2
[2] => Quantity
)
[1] => Array
(
[0] => [3]Value
[1] => 3
[2] => Value
)
)
Iterating through the table using something similar to:
$calcString = $table[1]['Quantity'];`
foreach ($matches as $match) {
$calcString = str_replace($match[0], $table[$match[1]][$match[2]], $calcString);
}
I can get the string to be calculated and am using a matheval class to do the sum.
For example
[1]Quantity = [2]Quantity + [3]Value
[2]Quantity = 3
[3]Value = 7 // [1]Quantity = 3 + 7 = 10
[1]Value = [2]Cost
[2]Cost = [2]Quantity * [2]Value // 3 * 6 = 18
Basically the variables in the table refer to other [id]key in the same table.
But here is my issue
I need to resolve references to other parts of the table (which may or may not themselves be formula) to fill in the blanks. This is outside my comfort zone and I would appreciate any advice (or even better functional code) which provides enlightenment on how I might be able to achieve this.
Thanks
Deep down, you already know how to solve this, you're just intimidated by the task.
A recursive approach would be to expand references instantly. For example,
expand('[1]Value') # returns '[2]Cost'
expand('[2]Cost') # returns '[2]Quantity * [2]Value'
expand('[2]Quantity') # returns 3
expand('[2]Value') # returns 6
eval('3 * 6')
# returns 18
# returns 18
# returns 18
An iterative (non-recursive) approach is to expand one reference at a time and repeat until there are unresolved references in the string.
expand('[1]Value') // returns '[2]Cost'
expand('[2]Cost') // returns '[2]Quantity + [2]Value'
expand('[2]Quantity + [2]Value') // returns 3 for [2]Quantity
expand('3 * [2]Value') // returns 6 for [2]Value
eval('3 * 6')
# returns 18
Normally, I prefer iterative solutions, because they're much less prone to stack overflows. However, recursive solutions are usually easier to write.
Here's a quickly slapped-together recursive evaluator: https://gist.github.com/stulentsev/b270bce4be67bc1a96ae (written in ruby, though)
If calcString's are reasonably sized and you don't expect replacements to get too elaborate, you could use a while loop to simulate the recursion. Here's an example that outputs the string along the way as it is being modified:
$calcString = $table[8]['Quantity'];
preg_match_all("/\[(.*?)\]([A-Z]*[a-z]*)/", $calcString, $matches, PREG_SET_ORDER);
print_r($calcString . "\n");
while (!empty($matches)){
foreach ($matches as $match) {
preg_match_all("/\[(.*?)\](Cost)/", $match[0], $matchCost, PREG_SET_ORDER);
if (!empty($matchCost)){
$cost = $table[$matchCost[0][1]]['Quantity'] . "*" . $table[$matchCost[0][1]]['Value'];
$calcString = str_replace($match[0], $cost, $calcString);
} else {
$calcString = str_replace($match[0], $table[$match[1]][$match[2]], $calcString);
}
print_r($calcString . "\n");
}
preg_match_all("/\[(.*?)\]([A-Z]*[a-z]*)/", $calcString, $matches, PREG_SET_ORDER);
}
Output:
[6]Quantity*[4]Value
[4]Quantity*[4]Value
[4]Quantity*3
[3]Cost*3
9*7*3
The table variable:
$table = array(
1 => array(
"id" => 1,
"Name" => "Regulating",
"Quantity" => "[2]Quantity+[3]Value",
"Value" => "[2]Cost"
),
2 => array(
"id" => 2,
"Name" => "Kerbs",
"Quantity" => 3,
"Value" => 6
),
3 => array(
"id" => 3,
"Name"=>"Bricks",
"Quantity"=> 9,
"Value"=> 7
),
4 => array(
"id" => 2,
"Name" => "Sausages",
"Quantity" => "[3]Cost",
"Value" => 3
),
5 => array(
"id" => 2,
"Name" => "Bamboo",
"Quantity" => "[4]Quantity",
"Value" => "[7]Cost"
),
6 => array(
"id" => 2,
"Name" => "Clams",
"Quantity" => "[4]Quantity",
"Value" => NULL
),
7 => array(
"id" => 2,
"Name" => "Hardcore",
"Quantity" => "[3]Quantity*0.5",
"Value" => 12
),
8 => array(
"id" => 2,
"Name" => "Beetles",
"Quantity" => "[6]Quantity*[4]Value",
"Value" => "[2]Value"
)
);
A dangerously easy, and your-situation-specific well-performable solution!
<?php
class solver {
private
// The final output array
$arr_evaled,
// When a cell gains its final value, the corresponding entry in the following array gets marked as being done!
$arr_done;
private $solving_iterations_count;
public function solver($array) {
$this->arr_done = array();
foreach($array as $k => $arr)
$this->arr_done[$k] = array('Quantity' => false, 'Value' => false);
// Firstly,expand all of the "[x]Cost"s to "([x]Quantity*[x]Value)"s!
$this->arr_evaled = array_map(
function($v){ return preg_replace('#\[(\d*?)\]Cost#', '([$1]Quantity*[$1]Value)', $v); },
$array
);
$this->solving_iterations_count = 0;
$this->solve();
}
private function isDone() {
foreach($this->arr_done as $a)
if($a['Quantity'] == false || $a['Value'] == false)
return false;
return true;
}
private function isCellDone($id, $fieldName) {
return $this->arr_done[$id][$fieldName];
}
private function markCellAsDone($id, $fieldName, $evaluation) {
$this->arr_done[$id][$fieldName] = true;
$this->arr_evaled[$id][$fieldName] = $evaluation;
}
private function isEvaluable($str) {
return preg_match('#^[0-9*+-\/\(\)\.]*$#', $str) == 1 || strtolower($str)=='null';
}
private function replace($from, $to) {
foreach($this->arr_evaled as &$arr) {
$arr['Quantity'] = str_replace($from, $to, $arr['Quantity']);
$arr['Value'] = str_replace($from, $to, $arr['Value']);
}
}
private function solve() {
$isSolvable = true; // YOUR TODO: I believe coding this part is also fun!) (e.g: check for "reference cycles")
if(!$isSolvable) return null;
while( !$this->isDone() )
{
foreach($this->arr_evaled as $arr) {
foreach(['Quantity', 'Value'] as $fieldName) {
if(!$this->isCellDone($arr['id'], $fieldName)) {
if($this->isEvaluable($arr[$fieldName])) {
$evaluation = eval("return {$arr[$fieldName]};");
$this->markCellAsDone($arr['id'], $fieldName, $evaluation);
$this->replace("[{$arr['id']}]$fieldName", "$evaluation");
}
}
}
}
$this->solving_iterations_count++;
}
foreach($this->arr_evaled as &$row)
$row['Cost'] = $row['Quantity'] * $row['Value'];
return $this->arr_evaled;
}
public function print_tabulated() {
echo "The count of solving iterations: {$this->solving_iterations_count}<br/><br/>";
echo '<table border="1"><tr><th>id</th><th>Name</th><th>Quantity</th><th>Value</th><th>Cost</th></tr>';
foreach($this->arr_evaled as $arr)
echo "<tr><td>{$arr['id']}</td><td>{$arr['Name']}</td><td>{$arr['Quantity']}</td><td>{$arr['Value']}</td><td>{$arr['Cost']}</td></tr>";
echo '</table>';
}
}
// Testing
$arr = array(
1 => array( 'id' => 1, 'Name' => 'Regulating', 'Quantity' => '[2]Quantity+[3]Value', 'Value' => '[2]Cost' ),
2 => array( 'id' => 2, 'Name' => 'Kerbs', 'Quantity' => '3', 'Value' => '6' ),
3 => array( 'id' => 3, 'Name' => 'Bricks', 'Quantity' => '9', 'Value' => '7' ),
4 => array( 'id' => 4, 'Name' => 'Sausages', 'Quantity' => '[3]Cost', 'Value' => '3' ),
5 => array( 'id' => 5, 'Name' => 'Bamboo', 'Quantity' => '[4]Quantity', 'Value' => '[7]Cost' ),
6 => array( 'id' => 6, 'Name' => 'Clams', 'Quantity' => '[4]Quantity', 'Value' => 'NULL' ),
7 => array( 'id' => 7, 'Name' => 'Hardcore', 'Quantity' => '[3]Quantity*0.5', 'Value' => '12' ),
8 => array( 'id' => 8, 'Name' => 'Beetles', 'Quantity' => '[6]Quantity*[4]Value', 'Value' => '[2]Value' ),
);
echo '<pre>';
(new solver($arr))->print_tabulated();
Here is the output:
I'm trying to optimize an e-commerce category system with unlimited category depth (barring system memory limitations). I retrieve all the categories at once and order them as a multi-dimensional array that roughly looks like:
[array] (
[0] (
'CategoryId' => 1,
'ParentCategoryId' => 0,
'Title' => 'Category A',
'SubCategories' => [array] (
[0] (
'CategoryId' => 2,
'ParentCategoryId' => 1,
'Title' => 'Category B',
'SubCategories' => [array] (
[0] (
'CategoryId' => 3,
'ParentCategoryId' => 2,
'Title' => 'Category C'
)
)
)
)
)
)
Each item in the array is actually an object, but for simplicity I wrote it out kind of like an array format.
I'm able to traverse my tree downwards using this function:
/**
* Find Branch using Recursive search by Object Key
* #param String Needle
* #param Array Haystack
* #return Array
*/
public static function findBranchByKey($key, $needle, $haystack)
{
foreach ($haystack as $item)
{
if ( $item->$key == $needle || ( is_object($item) && $item = self::findBranchByKey($key, $needle, $item->SubCategories)) )
{
return $item;
}
}
return false;
}
This finds the object with a matching key and returns it (which may contain more subcategories).
My issue is figuring out how to traverse the other direction. For example, using the data above, let's say I am displaying "Category C" and want to create bread crumbs of it's parents. I can't think of a good way to take my tree array, jump to a specific subcategory, then iterate upwards to get each parent. A resulting array from something like this could be like this so it's easy to spit them out as bread crumbs:
array( 'Category A', 'Category B', 'Category C' )
I could probably do this using SQL in my database but I'd like to retrieve the tree once, cache it, and perform traversal on that object whenever I need to rather than making tons of queries.
TL;DR; How can I traverse upwards in a multidimensional array of categories?
It can be done by recursion.
Let's say, this function should work:
function getPath($id, $tree, &$path = array()) {
foreach ($tree as $item) {
if ($item['CategoryId'] == $id) {
array_push($path, $item['CategoryId']);
return $path;
}
if (!empty($item['SubCategories'])) {
array_push($path, $item['CategoryId']);
if (getPath($id, $item['SubCategories'], $path) === false) {
array_pop($path);
} else {
return $path;
}
}
}
return false;
}
This:
$data = array(
array(
'CategoryId' => 10,
'ParentCategoryId' => 0,
'SubCategories' => array(
array(
'CategoryId' => 12,
'ParentCategoryId' => 1,
'SubCategories' => array()
),
)
),
array(
'CategoryId' => 1,
'ParentCategoryId' => 0,
'SubCategories' => array(
array(
'CategoryId' => 2,
'ParentCategoryId' => 1,
'SubCategories' => array()
),
array(
'CategoryId' => 3,
'ParentCategoryId' => 1,
'SubCategories' => array()
),
array(
'CategoryId' => 4,
'ParentCategoryId' => 1,
'SubCategories' => array(
array(
'CategoryId' => 5,
'ParentCategoryId' => 4,
'SubCategories' => array()
),
)
)
)
)
);
$result = getPath(5, $data);
print_r($result);
will result in:
Array ( [0] => 1 [1] => 4 [2] => 5 )
I have the following 2D array and I would like to compare some values. First I would like to get the highest value from the array and depends on time extract from the previous value with the same index.
example: highest(work_something(223))-previous(work_something(120))
$data = array(
0 => array(
'time' => '2011-10-03 18:43:00',
),
1 => array(
'time' => '2011-10-03 18:44:00',
),
2 => array(
'time' => '2011-10-03 18:45:00',
'item_something' => -17,
'keyword_something' => 0,
'keyword_something_1' => 0,
'search_something' => 0,
'search_links_something' => 0,
'work_something' => 120,
),
3 => array(
'time' => '2011-10-03 18:45:00',
'item_something' => -17,
'keyword_something' => 0,
'keyword_something_1' => 0,
'search_something' => 0,
'search_links_something' => 0,
'work_something' => 223,
),
);
$array //This is your 2D array
$highest = 0;
foreach($array as $key => $value):
if(isset($value['work_something'])){
if($value['work_something'] > $highest){
$highest = $value['work_something']; //This is highest 'work_something' value
$highest_array_key = $key; //This is the key of 'work_something' array
}
}
endforeach;
//To compare with previews element use
$array[$highest_array_key - 1] //this is prew element
example: highest(work_something(223))-previous(work_something(120))
$highest - $array[$highest_array_key - 1]['work_something'];