PHP foreach Group by Value - php

I am trying to group items within a PHP foreach by their 'field->value' and get the sum of each group separately. This code below works, but I feel like there is a more efficient way of doing it?
Thanks in advance.
<?php
$number_1 = 0;
$number_2 = 0;
$number_3 = 0;
foreach ( $fields as $field ) {
if($field->value == 1) {
$number_1 += $field->number;
}
if($field->value == 2) {
$number_2 += $field->number;
}
if($field->value == 3) {
$number_3 += $field->number;
}
}
echo $number_1;
echo $number_2;
echo $number_3;
?>

In this case you can use variable variables.
Generally I would rather recommend an array but it's your choice.
foreach ( $fields as $field ) {
${"number_" . $field->value} += $field->number;
}
Array version.
foreach ( $fields as $field ) {
$arr["number_" . $field->value] += $field->number;
}
// Either output as array or extract values to separate variables.
Echo $arr["number_1"];
//Or
Extract($arr);
Echo $number_1;
Edit had number where it should be value.

Don't use separate variables, use an array with $field->value as the index.
$numbers = array();
foreach ($fields as $field) {
if (isset($numbers[$field->value])) {
$numbers[$field->value] += $field->number;
} else {
$numbers[$field->value] = $field->number;
}
}

Related

PHP: Wildcards on multidimensional key

I have a multidimensional array $array["A"]["B"]["C"]["D"]. The list is longer.
Is there a wildcard that I can use to get ["D"] value in let say ["B"] array?
Something like this, $array["A"]["B"][*]["D"] ?
or $array[*]["B"][*]["D"] ?
Example, I would like to get all prices that were bought on February regardless of the year.
$array[2013][2][23]["ItemName"]["ItemPrice"] .....
If this would work, it would be really wonderful
$array[*][2][*][*]["ItemPrice"]..
any idea?
You could do multiple foreach to loop though every nested array that you want to loop though.
foreach ($array as $a) {
foreach ($a["B"] as $c) {
foreach ($c as $d) {
// Do something with $d
}
}
}
This would be $array[*]["B"][*][*]
Edit: You could combine my suggestion with a while loop.
$innerArray = $array;
while (true) {
foreach ($array as $key => $value) {
if ($key == "D") {
// Do something with this value
} else if (is_array($value)) {
$innerArray = $value;
} else {
break;
}
}
}
Thanks to #Sepehr-Farshid it just crossed my mind that I can use recursive function (Something that I haven't use for quiet a while. So here a example.
$newarray = array();
$tempArray = $oldarray;
$levels[] = 1;
$keys[] = 2;
$levels[] = 4;
$keys[] = "ItemPrice";
$lastLevel =4;
recurArray($tempArray, 0);
function recurArray($array, $level)
{
foreach($array as $key => $value) {
if(array_search($level, $GLOBALS["levels"]) {
$tempKey = array_search($level, $GLOBALS["levels"];
if($key == $GLOBALS["keys"][$tempKey] {
if($level == $GLOBALS["lastLevel"]) $GLOBALS["newarray"] = $value;
else recurArray($value, $level + 1);
}
else { return; }
}
else { recurArray($value, $level + 1); }
}
}
this might not be the optimum way, but it will work and can be refined. :D

how to convert index array to associative array?

I have an array like that
$products = array(array(354),array(1),array(375),array(1),array(344),array(2));
and i want to achieve array like that
$arrProducts= array(array('product_id'=>354,'qty'=>1),array('product_id'=>375,'qty'=>1),array('product_id'=>344,'qty'=>2));
I achieved this array using this code
foreach($products as $val)
{
$abc[] =$val[0];
}
for($i=0;$i<count($abc);$i++)
{
if($i%2==0)
{
$newarr[]['product_id'] = $abc[$i];
}
else{
$newarr[]['qty'] = $abc[$i];
}
}
for($j=0;$j<count($newarr);$j++)
{
if($j%2==0)
{
$arrProducts[] = array_merge($newarr[$j],$newarr[$j+1]);
}
else{
continue;
}
}
echo '<pre>';
print_r($arrProducts);
but i think my way to get this array is too long so how can i get this array in short way using some array functions or should i use this code?
You can use array_chunk in this case if this is always by twos, and combine it with array_combine():
$products = array(array(354),array(1),array(375),array(1),array(344),array(2));
$products = array_chunk($products, 2);
$arrProducts = array();
$keys = array('product_id', 'qty');
foreach($products as $val) {
$arrProducts[] = array_combine($keys, array(reset($val[0]), reset($val[1])));
}
echo '<pre>';
print_r($arrProducts);
Another alternative would be:
$products = array(array(354),array(1),array(375),array(1),array(344),array(2));
$keys = array('product_id', 'qty');
$arrProducts = array_map(function($e) use ($keys) {
return array_combine($keys, array_map('reset', $e));
}, array_chunk($products, 2));
This will yield the same result.
Consume two array elements on each iteration:
$arrProducts = array();
$inputLength = count($products);
for ($i = 0; $i < $inputLength; $i += 2) {
$arrProducts[] = array('product_id' => $products[$i][0], 'qty' => $products[$i+1][0]);
}
$i=1;
$j=0;
foreach($products as $val)
{
if(($i%2) == 0)
{
$abc[$j]['qty'] =$val[0];
$j++;
}
else
{
$abc[$j]['product_id'] =$val[0];
}
$i++;
}

Trying to generate incremental numbers in a function with foreach

This function creates <li> tags and im trying to give each li tag a unique CSS class name,
I've tried to create a for loop to generate numbers but this is producing the number 7 rather than counting down to the number 7 in <li> tag.
any help greatly appreciated!
function the_meta() {
if ( $keys = get_post_custom_keys() ) {
echo "<ul class='post-meta'>\n";
//tried to add this counter here to generate incremental numbers in the foreach loop
for ($i = 0; $i <= 6; $i++) { }
foreach ( (array) $keys as $key ) {
$keyt = trim($key);
if ( is_protected_meta( $keyt, 'post' ) )
continue;
$values = array_map('trim', get_post_custom_values($key));
$value = implode($values,', ');
echo apply_filters('the_meta_key', "<li class='$i'><span class='post-meta-key'>$key:</span> $value</li>\n", $key, $value);
}
echo "</ul>\n";
}
}
This line of code:
for ($i = 0; $i <= 6; $i++) { }
Will just increase $i until it get to 6,
So when you're getting to the "main loop" the variable $i will have the same value.
Why having another loop to count?
You can implement the counter inside your current loop:
$i = 1; //Before the loop , set a starting value.
foreach ( (array) $keys as $key ) {
$keyt = trim($key);
if ( is_protected_meta( $keyt, 'post' ) )
continue;
$values = array_map('trim', get_post_custom_values($key));
$value = implode($values,', ');
echo apply_filters('the_meta_key', "<li class='$i'><span class='post-meta-key'>$key:</span> $value</li>\n", $key, $value);
$i++; //Increase by one!!
}
That initial for loop wasn't really doing anything (as you noted), just initialize a variable at 0, then print and increment it every time you use it:
function the_meta() {
if ( $keys = get_post_custom_keys() ) {
echo "<ul class='post-meta'>\n";
$i=0;
foreach ( (array) $keys as $key ) {
$keyt = trim($key);
if ( is_protected_meta( $keyt, 'post' ) )
continue;
$values = array_map('trim', get_post_custom_values($key));
$value = implode($values,', ');
echo apply_filters('the_meta_key', "<li class='".$i++."'><span class='post-meta-key'>$key:</span> $value</li>\n", $key, $value);
}
echo "</ul>\n";
}
}
Your for loop just iterates doing nothing. You have to use:
$i = 0;
foreach ( (array) $keys as $key ) {
// ...
$i++;
}

for loop and if loop is refusing to print a varriable outside the loop?

i am facing a problem that after i created the jQuery post, i was able to receive all the data but as a one peace, so when i began rephrasing them i succeed until the final part which was the inserting into the database, where inside the for loop and if loop i am getting the value but when i wanted to start inserting them into the database i am getting null values, below is the for loop and if loop
if ($action == "insert")
{
$fields = explode("&",$data);
foreach($fields as $field)
{
$field_key_value = explode("=",$field);
$key = urldecode($field_key_value[0]);
$value = urldecode($field_key_value[1]);
$id = $row['id'];
$date1 = date("d/n/Y");
foreach ($cart->get_contents() as $item)
{
$item_id = $item['id'];
$item_name = $item['name'];
$item_price = $item['price'];
$item_qty = $item['qty'];
$item_ids = explode("-",$item_id);
for($i = 0; $i < count($item_ids); $i++)
{
$item_idn = join("",$item_ids);
}
if($key == $item_id."id")
{
$ids = $value;
echo $ids."\r\n";
}
elseif($key == "Small".$item_idn)
{
$small= $value;
echo $small."\r\n";
}
elseif($key == "large".$item_idn)
{
$large= $value;
echo $large."\r\n";
}
elseif($key == "medium".$item_idn)
{
$medium= $value;
echo $medium."\r\n";
}
elseif($key == "xlarge".$item_idn)
{
$xlarge= $value;
echo $xlarge."\r\n";
}
elseif($key == "qty".$item_idn)
{
$qty = $value;
echo $qty."\r\n";
}
elseif($key == "Total".$item_idn)
{
$subtotal = $value;
echo $subtotal."\r\n";
}
elseif($key == "finaltotal")
{
$finaltotal = $value.",";
$final = explode(",",$finaltotal);
for($i = 0; $i < count($final); $i++)
{
$totalf = $final[$i];
break 3;
}
}
}
}
}
From jQuery docs:
The .serialize() method creates a text string in standard URL-encoded notation
So on the PHP side you'll get a similar string like this:
a=1&b=2&c=3&d=4&e=5
There's no need to explode &'s (or any other hocus-pocus) you can easily access your submitted variables like:
$a = $_POST['a']; //1
Of course when you submit your data via $_GET, you need to use $_GET.
It appears as if you have a number of issues. If your jQuery is posting to your PHP script you can get all your keys/values through the $_POST array. You could get $finaltotal like this $finaltotal = $_POST['finaltotal']. If your jQuery is not POSTing, but sending them as a GET request, you can get the same value like so $finaltotal = $_GET['finaltotal'].
Also, regarding your big if/elseif block of code, I would recommend using a switch statement instead if you are going to keep your code as is.
switch($key)
{
case 'finaltotal':
//do stuff
break;
default:
//do default
break;
}
First of all, you either can use $_GET/$_POST for the submited data, as fabrik pointed out, or (if for some reason you really only have your data in $data) can use the built in function parse_str.
parse_str($data, $fields);
foreach($fields as $key => $field) {
foreach ($cart->get_contents() as $item) {
if($key == ...) {
...
}
}
}
And if I correctly understand what you are doing here, you need to move the break 3; out of your for loop:
elseif($key == "finaltotal") {
$finaltotal = $value.",";
$final = explode(",",$finaltotal);
for($i = 0; $i < count($final); $i++){
$totalf = $final[$i];
}
break 3;
}

PHP How to determine the first and last iteration in a foreach loop?

The question is simple. I have a foreach loop in my code:
foreach($array as $element) {
//code
}
In this loop, I want to react differently when we are in first or last iteration.
How to do this?
If you prefer a solution that does not require the initialization of the counter outside the loop, then you can compare the current iteration key against the function that tells you the last / first key of the array.
PHP 7.3 and newer:
foreach ($array as $key => $element) {
if ($key === array_key_first($array)) {
echo 'FIRST ELEMENT!';
}
if ($key === array_key_last($array)) {
echo 'LAST ELEMENT!';
}
}
PHP 7.2 and older:
PHP 7.2 is already EOL (end of life), so this is here just for historic reference. Avoid using.
foreach ($array as $key => $element) {
reset($array);
if ($key === key($array)) {
echo 'FIRST ELEMENT!';
}
end($array);
if ($key === key($array)) {
echo 'LAST ELEMENT!';
}
}
You could use a counter:
$i = 0;
$len = count($array);
foreach ($array as $item) {
if ($i == 0) {
// first
} else if ($i == $len - 1) {
// last
}
// …
$i++;
}
To find the last item, I find this piece of code works every time:
foreach( $items as $item ) {
if( !next( $items ) ) {
echo 'Last Item';
}
}
A more simplified version of the above and presuming you're not using custom indexes...
$len = count($array);
foreach ($array as $index => $item) {
if ($index == 0) {
// first
} else if ($index == $len - 1) {
// last
}
}
Version 2 - Because I have come to loathe using the else unless necessary.
$len = count($array);
foreach ($array as $index => $item) {
if ($index == 0) {
// first
// do something
continue;
}
if ($index == $len - 1) {
// last
// do something
continue;
}
}
You could remove the first and last elements off the array and process them separately.
Like this:
<?php
$array = something();
$first = array_shift($array);
$last = array_pop($array);
// do something with $first
foreach ($array as $item) {
// do something with $item
}
// do something with $last
?>
Removing all the formatting to CSS instead of inline tags would improve your code and speed up load time.
You could also avoid mixing HTML with php logic whenever possible.
Your page could be made a lot more readable and maintainable by separating things like this:
<?php
function create_menu($params) {
//retrieve menu items
//get collection
$collection = get('xxcollection') ;
foreach($collection as $c) show_collection($c);
}
function show_subcat($val) {
?>
<div class="sub_node" style="display:none">
<img src="../images/dtree/join.gif" align="absmiddle" style="padding-left:2px;" />
<a id="'.$val['xsubcatid'].'" href="javascript:void(0)" onclick="getProduct(this , event)" class="sub_node_links" >
<?php echo $val['xsubcatname']; ?>
</a>
</div>
<?php
}
function show_cat($item) {
?>
<div class="node" >
<img src="../images/dtree/plus.gif" align="absmiddle" class="node_item" id="plus" />
<img src="../images/dtree/folder.gif" align="absmiddle" id="folder">
<?php echo $item['xcatname']; ?>
<?php
$subcat = get_where('xxsubcategory' , array('xcatid'=>$item['xcatid'])) ;
foreach($subcat as $val) show_subcat($val);
?>
</div>
<?php
}
function show_collection($c) {
?>
<div class="parent" style="direction:rtl">
<img src="../images/dtree/minus.gif" align="absmiddle" class="parent_item" id="minus" />
<img src="../images/dtree/base.gif" align="absmiddle" id="base">
<?php echo $c['xcollectionname']; ?>
<?php
//get categories
$cat = get_where('xxcategory' , array('xcollectionid'=>$c['xcollectionid']));
foreach($cat as $item) show_cat($item);
?>
</div>
<?php
}
?>
An attempt to find the first would be:
$first = true;
foreach ( $obj as $value )
{
if ( $first )
{
// do something
$first = false; //in order not to get into the if statement for the next loops
}
else
{
// do something else for all loops except the first
}
}
Simply this works!
// Set the array pointer to the last key
end($array);
// Store the last key
$lastkey = key($array);
foreach($array as $key => $element) {
....do array stuff
if ($lastkey === key($array))
echo 'THE LAST ELEMENT! '.$array[$lastkey];
}
Thank you #billynoah for your sorting out the end issue.
1: Why not use a simple for statement? Assuming you're using a real array and not an Iterator you could easily check whether the counter variable is 0 or one less than the whole number of elements. In my opinion this is the most clean and understandable solution...
$array = array( ... );
$count = count( $array );
for ( $i = 0; $i < $count; $i++ )
{
$current = $array[ $i ];
if ( $i == 0 )
{
// process first element
}
if ( $i == $count - 1 )
{
// process last element
}
}
2: You should consider using Nested Sets to store your tree structure. Additionally you can improve the whole thing by using recursive functions.
Best answer:
$arr = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
foreach ($arr as $a) {
// This is the line that does the checking
if (!each($arr)) echo "End!\n";
echo $a."\n";
}
The most efficient answer from #morg, unlike foreach, only works for proper arrays, not hash map objects. This answer avoids the overhead of a conditional statement for every iteration of the loop, as in most of these answers (including the accepted answer) by specifically handling the first and last element, and looping over the middle elements.
The array_keys function can be used to make the efficient answer work like foreach:
$keys = array_keys($arr);
$numItems = count($keys);
$i=0;
$firstItem=$arr[$keys[0]];
# Special handling of the first item goes here
$i++;
while($i<$numItems-1){
$item=$arr[$keys[$i]];
# Handling of regular items
$i++;
}
$lastItem=$arr[$keys[$i]];
# Special handling of the last item goes here
$i++;
I haven't done benchmarking on this, but no logic has been added to the loop, which is were the biggest hit to performance happens, so I'd suspect that the benchmarks provided with the efficient answer are pretty close.
If you wanted to functionalize this kind of thing, I've taken a swing at such an iterateList function here. Although, you might want to benchmark the gist code if you're super concerned about efficiency. I'm not sure how much overhead all the function invocation introduces.
For SQL query generating scripts, or anything that does a different action for the first or last elements, it is much faster (almost twice as fast) to avoid using unneccessary variable checks.
The current accepted solution uses a loop and a check within the loop that will be made every_single_iteration, the correct (fast) way to do this is the following :
$numItems = count($arr);
$i=0;
$firstitem=$arr[0];
$i++;
while($i<$numItems-1){
$some_item=$arr[$i];
$i++;
}
$last_item=$arr[$i];
$i++;
A little homemade benchmark showed the following:
test1: 100000 runs of model morg
time: 1869.3430423737 milliseconds
test2: 100000 runs of model if last
time: 3235.6359958649 milliseconds
And it's thus quite clear that the check costs a lot, and of course it gets even worse the more variable checks you add ;)
With Keys and Values this works as well:
foreach ($array as $key => $value) {
if ($value === end($array)) {
echo "LAST ELEMENT!";
}
}
Using a Boolean variable is still the most reliable, even if you want to check the first appearance of a $value (I found it more useful in my situation and in many situations), such like this:
$is_first = true;
foreach( $array as $value ) {
switch ( $value ) {
case 'match':
echo 'appeared';
if ( $is_first ) {
echo 'first appearance';
$is_first = false;
}
break;
}
}
if( !next( $array ) ) {
echo 'last value';
}
}
Then how about !next( $array ) to find the last $value which will return true if there's no next() value to iterate.
And I prefer to use a for loop instead of foreach if I were going to use a counter, like this:
$len = count( $array );
for ( $i = 0; $i < $len; $i++ ) {
$value = $array[$i];
if ($i === 0) {
// first
} elseif ( $i === $len - 1 ) {
// last
}
// …
$i++;
}
I came across this thread when I have the same problem. I only need to get the first element then I re-analyze my code until this came up to my mind.
$firstElement = true;
foreach ($reportData->result() as $row)
{
if($firstElement) { echo "first element"; $firstElement=false; }
// Other lines of codes here
}
The above codes are great and complete but if you only need just the first element then you may try this code.
Using reset($array) and end($array)
<?php
$arrays = [1,2,3,4,5];
$first = reset($arrays);
$last = end($arrays);
foreach( $arrays as $array )
{
if ( $first == $array )
{
echo "<li>{$array} first</li>";
}
else if ( $last == $array )
{
echo "<li>{$array} last</li>";
}
else
{
echo "<li>{$array}</li>";
}
}
Demo repl.it
Not sure if it still necessary. But the following solution should work with iterators and does not require count.
<?php
foreach_first_last(array(), function ($key, $value, $step, $first, $last) {
echo intval($first), ' ', intval($last), ' ', $step, ' ', $value, PHP_EOL;
});
foreach_first_last(array('aa'), function ($key, $value, $step, $first, $last) {
echo intval($first), ' ', intval($last), ' ', $step, ' ', $value, PHP_EOL;
});
echo PHP_EOL;
foreach_first_last(array('aa', 'bb', 'cc'), function ($key, $value, $step, $first, $last) {
echo intval($first), ' ', intval($last), ' ', $step, ' ', $value, PHP_EOL;
});
echo PHP_EOL;
function foreach_first_last($array, $cb)
{
$next = false;
$current = false;
reset($array);
for ($step = 0; true; ++$step) {
$current = $next;
$next = each($array);
$last = ($next === false || $next === null);
if ($step > 0) {
$first = $step == 1;
list ($key, $value) = $current;
if (call_user_func($cb, $key, $value, $step, $first, $last) === false) {
break;
}
}
if ($last) {
break;
}
}
}
You can use an anonymous function, too:
$indexOfLastElement = count($array) - 1;
array_walk($array, function($element, $index) use ($indexOfLastElement) {
// do something
if (0 === $index) {
// first element‘s treatment
}
if ($indexOfLastElement === $index) {
// last not least
}
});
Three more things should be mentioned:
If your array isn‘t indexed strictly (numerically) you must pipe your array through array_values first.
If you need to modify the $element you have to pass it by reference (&$element).
Any variables from outside the anonymous function you need inside, you‘ll have to list them next to $indexOfLastElement inside the use construct, again by reference if needed.
You can use the counter and array length.
$array = array(1,2,3,4);
$i = 0;
$len = count($array);
foreach ($array as $item) {
if ($i === 0) {
// first
} else if ($i === $len - 1) {
// last
}
// …
$i++;
}
foreach ($arquivos as $key => $item) {
reset($arquivos);
// FIRST AHEAD
if ($key === key($arquivos) || $key !== end(array_keys($arquivos)))
$pdf->cat(null, null, $key);
// LAST
if ($key === end(array_keys($arquivos))) {
$pdf->cat(null, null, $key)
->execute();
}
}
The simplest way would be
$array = [9,5,6,4,7,8];
$current_iteration = 0;
foreach($array as $item){
if( 0 === $current_iteration ){
echo 'this is the first item: ' . $item;
}
if( (count($array) - 1) === $current_iteration){
echo 'this is the last item: ' . $item;
}
$current_iteration++;
}
Try this:
function children( &$parents, $parent, $selected ){
if ($parents[$parent]){
$list = '<ul>';
$counter = count($parents[$parent]);
$class = array('first');
foreach ($parents[$parent] as $child){
if ($child['id'] == $selected) $class[] = 'active';
if (!--$counter) $class[] = 'last';
$list .= '<li class="' . implode(' ', $class) . '"><div>' . $child['name'] . '</div></li>';
$class = array();
$list .= children($parents, $child['id'], $selected);
}
$list .= '</ul>';
return $list;
}
}
$output .= children( $parents, 0, $p_industry_id);

Categories