I use PHP/7.2.0beta3. So I want to create a custom function to reverse an array in PHP. If the array is (1,2,3) the functions turns it to (3,2,1).
I thought I should use the array_pop, grab the last value of the array and pass it to another array.
The problem
Here is the code I wrote. Just copy it and run it as PHP. I dont know why it stops in the middle of the array and does not continue till the end.
$originalarray = range(0, 100, 5);//works
echo '<br> original array <br>';
print_r($originalarray); // 0-100, with 5 step
function customreverse($x){
echo '<br> original array in function <br>';
print_r($x); //works, 0-100, with 5 step
echo '<br> sizeof in function '.sizeof($x).'<br>'; //works, is 21
for($a=0; $a<sizeof($x); $a++){
$reversearray[$a] = array_pop($x);
echo '<br> reversearray in for loop <br>';
print_r($reversearray);//stops at 50
echo '<br> a in for loop <br>';
echo $a;//stops at 10
}
echo '<br> reverse in function <br>';
print_r($reversearray);////stops at 50
}
customreverse($originalarray);
The same problem occurs even if I replace sizeof with count. Or $a<sizeof($x) with $a<=sizeof($x). Why does it stop and does not traverse the whole array? What am I missing here?
Thanks
sizeof (or count) is evaluated on every iteration of the loop and the array shrinks on each iteration. You need to store the original count in a variable. For Example (I removed a few lines to focus on the issue):
<?php
$originalarray = range(0, 100, 5);//works
function customreverse($x){
$origSize=sizeof($x);
for($a=0; $a<$origSize; $a++){
$reversearray[$a] = array_pop($x);
}
return($reversearray);//stops at 50 (Now it doesn't)
}
print_r(customreverse($originalarray));
jh1711 properly explained that your loop ends early because the middle statement in for(statement1; statement2; statement3) gets executed each iteration, and because you're popping the original array within the loop, sizeOf() returns a smaller number each time.
You could compact your code a bit, by building your reverse array like so:
while(!empty($original)) $reverse[] = array_pop($original);
If you want to preserve key=>value bindings (meaning reverse keys as well, so that the same keys will bind to the same values), you could do:
while(!empty($original)):
$val = end($original); // set pointer at end of array
$reverse[key($original)] = $val;
endwhile;
If you want to modify the array in-place (not create a 2nd array), you could do:
for($i=0, $j=sizeof($original); $i < $j; $i++){
array_splice($original,$i,0,array_pop($original));
} // pop last element and insert it earlier in the array
Related
Hey everyone I have a question about arrays and loops in PHP.
For a game I'm making, I need to write a function that generates a stack of crystal_id's based on a given size and ratio.
The ratio is the ratio between black crystals and different colored crystal (so a ratio of 0,25 (1:4) and a stack of 50 would yield a stack with 40 black crystals and 10 colored crystals).
I have all the math to calculate the amount of crystals per color and stuff figured out, but I can't figure out how to create an array with the right amount of colored crystals where each color is represented equally.
For reference, the array the code gets to choose from is a variable called $crystals_array, which is an array filled with integers where each integer represents a different colored crystal (e.g. [2,3,4,5,6]).
In the above case we have 5 different colored crystals and we needed a total of 10 colored crystals where each crystal is represented equally. So I need to create an array that looks a little something like this:
[2,2,3,3,4,4,5,5,6,6].
The code I have so far is this:
for($i = 0; $i <= count($amount_crystals_color) - 1; $i++)
{
$array = array_fill(0, $amount_crystals_per_color_stack, $crystals_array[$i]);
$i++;
}
Using the above example $amount_crystals_per_color_stack is equal to 2 and amount_crystals_color is equal to 5.
When executing this code it outputs an array: [2,2] which is how many 2's we need, but I can't figure out how to add the remaining crystals to this array.
Can anyone help me out?
Your code has several problems and i will address each of them individually.
Using the for loop to iterate over an array
You are using a for loop in your code, that has the following loop head:
for($i = 0; $i <= count($crystals_array) - 1; $i++)
The loop had consists of three parts, each separated by a semicolon (;).
The first part $i = 0 is the initialization part. Here you initialize a variable, that is later used as an iterator, which shall change in each loop iteration. You could name this the start point as well.
The second part $i <= count($crystals_array) - 1 is the condition part. A condition is formed, that shall express for how long the loop shall iterate. As long as the expression evaluates as true, the loop will run again. Therefore this condition is evaluated at the start of each iteration. As soon as the condition evaluates as false the loop will end. Therefore this part can be named endpoint as well.
The third part $i++ is the step size. This is executed at the end of each iteration and determines how the iterator (the variable you defined in the first part) shall change. $i++ is in this context equal to $i = $i + 1 and represents a step size of 1. Therefore the variable $i gets increased by 1 for each run of the loop.
This said, you can improve and fix your code regarding the for loop with two changes:
Save functions, that are executed in your condition part into an variable, if they return a constant result for each iteration. You use the count() function, which will then count your array again for each iteration of the for loop. By saving it in a variable $count = count($crystals_array); before the for loop and changing the condition to $i < $count, the function is only called once and your code gets faster.
Do not change the iterator variable $i outside of your loop header. This is really bad code style. You added the line $i++; to the end of your loop, but that is already done in the step size part of the for header. Because that is executed at the and of each iteration as well you increased the step size to two, meaning that you only run the for loop with $i = 0, $i = 2 and $i = 4 instead of for each element.
For your code the usage of the $i iterator is only to address the elements of the initial array. Even though you should understand the for loop for the future, you should use a foreach loop for this case instead. The following code would be equivalent to your for loop.
//This code still contains another major bug and is jsut a partial improvvement
foreach($crystals_array as $crystal) {
$array = array_fill(0, $amount_crystals_per_color_stack, $crystal);
}
As you can see, you neither need to worry about counting the initial array, nor in which index the current value is. Instead the variable $crystal will automatically contain the next element for each iteration.
Appending elements to an array
You used the following line to save the newly generated elements in your array:
$array = array_fill(0, $amount_crystals_per_color_stack, $crystal);
If you look closely, you use a standard assignment with $array = at the beginning of your line. This means, that (like with each variable assignment) the previous value of the variable gets overwritten by the new value provided from the right side of the assignment. What you do not want yet is to overwrite the array, but to append something to it.
This can be done by adding two squared brackets to the end of the variable name: $array[] = .... Now, if the variable $array is really an array, what ever value is on the right side of the assignment will be appended to the array, instead of overwriting it.
Managing result types the right way
The following line still contains a major problem:
$array[] = array_fill(0, $amount_crystals_per_color_stack, $crystal);
The result type of array_fill() is an array itself. By appending it to the previous array, you would get the following structure:
$array = [
[2, 2],
[3, 3],
[4, 4],
[5, 5],
[6, 6],
];
As you can see, the code did exactly what it should but not what you wanted. Each result (array) was appended to the array. The result is therefore an array or arrays (or a multidimensional array). What you want instead, is that the values of the result are appended to the existing array.
PHP offers a function for that, named array_merge(). This function takes all elements for one (or more) array(s) and appends them to the end of the first array, that was given to the function. You can use it as followed:
$newCrystals = array_fill(0, $amount_crystals_per_color_stack, $crystal);
$array = array_merge($array, $newCrystals);
As you can see the latter line contains a normal assignment again. ($array =) This is because array_merge() does not modify the first array given to it, but creates a new array with the merged fields. Therefore the new array contains all values from the old one and it is safe to overwrite the old one with it.
The complete code would therefore be:
$array = [];
foreach($crystals_array as $crystal) {
$newCrystals = array_fill(0, $amount_crystals_per_color_stack, $crystal);
$array = array_merge($array, $newCrystals);
}
As I understood the problem
$crystals_array = [2,3,4,5,6];
$amount_crystals_per_color_stack = 2;
$res = [];
foreach($crystals_array as $v) {
// repeat each item from array $amount_crystals_per_color_stack times
$res = array_merge($res, array_fill(0, $amount_crystals_per_color_stack, $v));
}
print_r($res); // [2,2,3,3,4,4,5,5,6,6]
You need to merge your array at each iteration of the loop (repl online) or you lose the result each time.
Like:
$array = array();
for($i = 0; $i < count($amount_crystals_color); $i++)
{
$array = array_merge($array, array_fill(0, $amount_crystals_per_color_stack, $crystals_array[$i]);
}
Also you don't need the $i++ in the loop, because it iterate twice otherwise, and you don't need count(..)-1 if the condition is < instead of <=.
You could use simple foreach() to achieve this-
$amount_crystals_per_color_stack = 2;
$array = [2,3,4,5,6];
$result = array();
foreach($array as $a){
for($i=1;$i<=$amount_crystals_per_color_stack;$i++){
array_push($result, $a);
}
}
print_r($result);
For example i have an array like this [-1,-1,-1,-1,-1,-1,2.5,-1,-1,8.3]
I want to count the element which are not -1 like except of the -1. Is there any function in php to get this ? One approach which i think is
Count the total array - count of the -1 in the array.
how can we achieve this.
P.S : please provide me a comment why this question deserve negative vote.
Like #MarkBaker said, PHP doesn't have a convenient function for every single problem, however, you could make one yourself like this:
$arr = [-1,-1,-1,-1,-1,-1,2.5,-1,-1,8.3];
function countExcludingValuesOfNegativeOne ($arr) {
return count($arr) - array_count_values(array_map('strval', $arr))['-1'];
}
echo countExcludingValuesOfNegativeOne($arr); // Outputs `2`.
It just counts the whole array, then subtracts the number of negative 1s in the array. Because PHP throws an error if any element in the array isn't a string 1 when using array_count_values, I've just converted all elements of the array to a string using array_map('strval', $arr) 2
you can use a for loop counter skipping for a specific number.
function skip_counter($array,$skip){
$counter = 0;
foreach ($array as $value) {
if ($value != $skip) $counter++;
}
return $counter;
}
then you call skip_counter($your_list, $number_to_be_skipped);
I have a foreach loop like below code :
foreach($coupons as $k=>$c ){
//...
}
now, I would like to fetch two values in every loop .
for example :
first loop: 0,1
second loop: 2,3
third loop: 4,5
how can I do ?
Split array into chunks of size 2:
$chunks = array_chunk($coupons, 2);
foreach ($chunks as $chunk) {
if (2 == sizeof($chunk)) {
echo $chunk[0] . ',' . $chunk[1];
} else {
// if last chunk contains one element
echo $chunk[0];
}
}
If you want to preserve keys - use third parameter as true:
$chunks = array_chunk($coupons, 2, true);
print_r($chunks);
Why you don't use for loop like this :
$length = count($collection);
for($i = 0; $i < $length ; i+=2)
{
// Do something
}
First, I'm making the assumption you are not using PHP 7.
It is possible to do this however, it is highly, highly discouraged and will likely result in unexpected behavior within the loop. Writing a standard for-loop as suggested by #Rizier123 would be better.
Assuming you really want to do this, here's how:
Within any loop, PHP keeps an internal pointer to the iterable. You can change this pointer.
foreach($coupons as $k=>$c ){
// $k represents the current element
next($coupons); // move the internal array pointer by 1 space
$nextK = current($coupons);
prev($coupons);
}
For more details, look at the docs for the internal array pointer.
Again, as per the docs for foreach (emphasis mine):
Note: In PHP 5, when foreach first starts executing, the internal array pointer is automatically reset to the first element of the
array. This means that you do not need to call reset() before a
foreach loop. As foreach relies on the internal array pointer in PHP
5, changing it within the loop may lead to unexpected behavior. In PHP
7, foreach does not use the internal array pointer.
Let's assume your array is something like $a below:
$a = [
"a"=>1,
"b"=>2,
"c"=>3,
"d"=>4,
"e"=>5,
"f"=>6,
"g"=>7,
"h"=>8,
"i"=>9
];
$b = array_chunk($a,2,true);
foreach ($b as $key=>$value) {
echo implode(',',$value) . '<br>';
}
First we split array into chunks (the parameter true preserves the keys) and then we do a foreach loop. Thanks to the use of implode(), you do not need a conditional statement.
well this'd be 1 way of doing it:
$keys=array_keys($coupons);
for($i=0;$i<count($keys);++$i){
$current=$coupons[$keys[$i]];
$next=(isset($keys[$i+1])?$coupons[$keys[$i+1]]:NULL);
}
now the current value is in $current and the next value is in $next and the current key is in $keys[$i] and the next key is in $keys[$i+1] , and so on.
I'm trying to run a PHP script that finds all the numbers divisible by 3 or 5, dumps them into an array, and adds all the values together. However, When I try to run it, I get a number output (I don't know if it's correct or not) and several hundred errors. They start out with:
Notice: Undefined offset: 1 in G:\Computer Stuff\WampServer\wamp\www\findthreesandfives.php on line 18
Then the offset number increases by increments of 1-3 (randomly, I haven't seen a pattern yet). I can't figure out what's wrong. Here's my code:
<?php
function loop($x)
{
$a = array(); //array of values divisible by 3 or 5
$l = 0; //length of the array
$e = 0; //sum of all the values in the array
for ($i=0; $i<=$x; $i++){ //this for loop creates the array
$n3=$i%3;
$n5=$i%5;
if($n3 === 0 || $n5 === 0){
$a[$i]=$i;
$l++;
}
}
for ($v=0; $v<=$l; $v++){ //this loop adds each value of the array to the total value
$e=$e + $a[$v];
}
return $e;
}
echo loop(1000);
?>
Someone please help...
The problem in your code is the following line:
$a[$i]=$i;
Should be:
$a[count($a)] = $i;
This is because the value of $i is always increasing, so using $i as your pointer will create gaps in the array's indices. count($a) returns the total number of items in the given array, which also happens to be the next index.
EDIT: #pebbl suggested using $a[] = $i; as a simpler alternative providing the same functionality.
EDIT 2: Solving the subsequent problem the OP described in the comments:
The problem seems to be that $l is greater than the number of items in $a. Thus, using count($a) in the for loop should fix your subsequent error.
Try replacing:
for ($v=0; $v<=$l; $v++){
With:
for ($v=0; $v<=count($a); $v++){
I found the same problem as #zsnow said. There are gaps within $a. The if condition allowed the gaps making the assignment skip some indexes. You can also use this
foreach ($a as $v){ //this loop adds each value of the array to the total value
$e=$e + $a[$v];
}
should actually be
foreach ($a as $v){ //this loop adds each value of the array to the total value
$e=$e + $v;
}
Here code (executed in php 5.3.5 and 5.2.13):
$res = array(1, 2, 3);
unset($res[0]);
for($i = 0; $i < sizeof($res); $i++)
{
echo $res[$i] . '<br />';
}
In results i see
<br />2<br />
Why only one element, and first empty? Can`t understand. When doing:
print_r($res);
See:
Array ( [1] => 2 [2] => 3 )
Thanx for help!
This is because you start with $i = 0; rather than 1 which is the new first index. The last element is missing because it stops before the second (previously third) element since the size has been reduced to 2. This should get the results you wish:
foreach($res as $value) {
echo $value . '<br />';
}
PHP doesn't rearrange the keys on unset. Your keys after the unset are 1 and 2. In the for cycle, i gets the 0 and 1 values. Using this snippet you should init i to 1, the first key of the array.
Hint 1: Use foreach to itearate over an array.
Hint 2: Don't use aliases. Use count instad of sizeof.
Because after unset sizeof array = 2
And basicly use error_reporting(E_ALL) for development, it will help you
This is not working as expected because when you unset, so sizeof() returns 2. Thus you are looping on 0 to less than 2(aka 1).
So it will only display the element at index 1, because you unset element at 0.
A simple fix for this would be to use the foreach loop:
foreach($res as $value){
echo $value .'<br />';
}
It is iterating 2 times, the first time through it is accessing index 0, which you've unset, the second time it's accessing index 1, which is what you see outputted. Even though there are only two elements, at index 1 & 2, you're still starting from the original index.