Error unset value from array in php - php

I want unset all value from this array, but result only remove half
Ex 1:
$filter = array("English", "Malay", "Student Pass", "NRIC");
for($i=0; $i<count($filter); $i++){
unset($filter[$i]);
}
print_r($filter);
=> Result Array ( [2] => Student Pass [3] => NRIC )
Ex 2:
$filter = array("English", "Malay");
for($i=0; $i<count($filter); $i++){
unset($filter[$i]);
}
print_r($filter);
=> Result Array ( [1] => Malay )

Your problem is that every time your for loop runs, it recalculates the array's length with count($filter). Thus, the for loop runs less often than there are elements within the array. Instead you should determine the array's length ahead of the loop and only use this variable within the loop:
e.g.:
<?
$filter = array("English", "Malay", "Student Pass", "NRIC");
$arrayLength = count($filter); // contains the initial length of the array
for($i=0; $i<$arrayLength;$i++) {
unset($filter[$i]);
}
print_r($filter);
?>
However, regarding overall performance it might be better to overwrite the array or even unset it.
e.g.:
// Overwrite it with an empty array
$filter = array();
// Unset it
unset($filter);

Its because when you unset the item on the array it removes it. So on the next iteration the count($filter) has now gone down. You could do as #Daan suggested and just set $filter = array(). However if you insisted on calling unset you should loop over the array in reverse order.
for($i = count($filter) - 1; $i >= 0; $i--) {
unset($filter[$i]);
}

Each time you unset an element, count($filter) decreases. Let me give you an example:
You have six apples. You are eating the n'th apple while n is smaller than the number of apples. So, you eat the 0'th apple, because 0 < 6. You have 5 apples. You eat the 1st apple, because 1 < 5. You have 4 apples. You eat the 2nd apple, because 2 < 4. You have 3 apples. You don't eat the 3rd apple, since 3 < 3 is not true. As a result, three apples will remain. Your mistake was assuming that the ever incremented index is slower than the ever decremented count if and only if the array is not empty. This is wrong and you need a different approach, like this:
while (count($filter) > 0) {
unset($filter[count($filter) - 1]);
}
This is sub-optimal though, just unset the array instead, like this:
unset($filter);

Related

How can I combine consecutive keys of an array depending on their values?

I have an array similar to this:
$stuff = array("a"=>"115","b"=>"0","c"=>"1","d"=>"0","e"=>"11","f"=>"326","g"=>"9","h"=>"1","i"=>"12","j"=>"0","k"=>"56");
What I want to do is concatenate the strings of the keys only where they are consecutive and their values are under 10 - note this includes keeping solitary keys with values under 10 too. I don't need to keep the actual values. In other words, the desired result in this case would be:
Array ( [0] => bcd [1] => gh [2] => j)
So there might be just two consecutive keys that need to be joined, or there might be more (eg as many as 5). I'm not sure how to 'look ahead' through the array to achieve this.
You don't need to look ahead but keep the past in mind.
$consecutive = '';
foreach($stuff as $k => $v) {
if ($v < 10) // or what ever condition you need
$consecutive .= $k;
else {
if ($consecutive) $res[] = $consecutive; // if exist add it
$consecutive= ''; // and reset
}
}
if ($con) $res[] = $con; //adding last element if exist as #Joffrey comment
Now $res will be your desire output
Live example: 3v4l

How to loop though an array and create an array, in the loop, containing values from the original array?

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);

Multidimensional sub array subtraction in PHP

Need subarray difference of below array
$arr = array(
array('s'=>'1','e'=>'3'),
array('s'=>'6','e'=>'7'),
array('s'=>'8','e'=>'9'),
array('s'=>'10','e'=>'14'),
array('s'=>'16','e'=>'17'),
)
if(arr[$arr[$i+1][s] - $i][e] <= 1){
//join them
}
else {
//save them as it is
}
Desired result should
$arr = array(
array('s'=>'1','e'=>'3'),
array('s'=>'6','e'=>'14'),
array('s'=>'16','e'=>'17'),
)
No consecutive (next S-E) should be 1
http://codepad.org/V8omMdn6 is where im struck at
See its like
iteration 0
6-3 = 3
so save array('s'=>'1','e'=>'3'),
iteration 1
8-7 = 1
array('s'=>'6','e'=>'9'), => discade in 2 as it
iteration 2
10-9 = 1
array('s'=>'6','e'=>'10'), => discade in 3 as it
iteration 3
10-9 = 1
array('s'=>'6','e'=>'14'),
iteration 4
16-14 = 4
array('s'=>'16','e'=>'17'),
$result = [];
foreach ($arr as $pair) {
if (empty($result) || $pair['s'] - end($result)['e'] > 1) {
$result[] = $pair;
} else {
$result[key($result)]['e'] = $pair['e'];
}
}
You might also use $last as key instead end() & key() for readability.
Using array pointer functions on $result shortens the code but uses some ugly hidden effects. end($result) returns last element of array (using key bracket with function result is possible since php5.3 I guess), but also sets the pointer, so key($result) will return correct key if needed.
While iterating you process last element of result array - this element might not be valid right away, but you don't need to look ahead. There are two scenarios for last element (+initial state condition for empty $result):
invalid: set e value from current item and process further
valid: leave it and push current item into results for further validation (unless that was the last one).
I took a very brief look at your codepen, I think what you want to achieve is to find out if the start time of a new session is within a given period from the end time of the last session, if so you would like to combine those sessions.
I think you confused yourself by trying to subtract start time of new session from end time of last session, it should be the other way round.
The way you worded the question made it even more confusing for people to understand.
If my interpretation of your question is correct, the below code should work with the test case you posted here.
function combineSession($arr){
$arrCount=count($arr)-1;
for ($i=0; $i<$arrCount; $i++){
//if the difference between s and e is less than or equal to one, then there is a consecutive series
if($arr[$i+1]['s']-$arr[$i]['e'] <= 1){
//assign the value of s at the start of a consecutive series to $temp
if (!isset($temp)){
$temp=$arr[$i]['s'];
}
//if consecutive series ends on the last sub_array, write $temp e pair to output
if ($i==$arrCount-1){
$output[]= array('s'=> $temp, 'e' => $arr[$arrCount]['e']);
}
}
//end of a consecutive series, write $temp and e pair to output, unset $temp
else if (isset($temp) && $i<$arrCount-1){
$output[]=array('s'=> $temp, 'e' => $arr[$i]['e']);
unset($temp);
}
//consecutive series ended at the second last sub-array, write $temp and e pair to output and copy key value pair of the last sub-array to output
else if ($i==$arrCount-1){
$output[]=array('s'=> $temp, 'e' => $arr[$i]['e']);
$output[]=$arr[$arrCount];
}
//not in a consecutive series, simply copy s e key value pair to output
else {
$output[]=$arr[$i];
}
}//end of for loop
print_r($output);
}//end of function
else if ($i==$arrCount-1){ $output[]=!isset($temp) ? $arr[$i] : array('s'=> $temp, 'e' => $arr[$i]['e']); $output[]=$arr[$arrCount]; }

Compare numeric values two arrays

i hace this issue whit two arrays
one array is given by a json like this
array1( [0] => 6, [1] => 1)
and making a query i got this other
array2( [0] => 1, [1] => 1)
I want to compare both arrays and use "if" when numeric values of array1 are less than or equal to array2. Is that possible? like this?
if($array1 <= $array2){do something}
Grettings!
This really depends on what your expected behaviour is, and also the expectations of your two arrays. For the sake of these examples, I'm going to assume that each array is guaranteed to be the same size and you are compairing paired elements (elements from each array at the same index).
If you want to exit the first time a number from array1 is less than its paired element from array2:
for ($i = 0; $i < sizeof($array1); $i++) {
if ($array1[$i] <= $array2[$i]) {
...do something
}
}
If you want to do something if all values of array1 are less than their paired elements from array2:
// Keep track of a flag so we know if we meet our condition once done
$allValuesLessThan = true;
// Check each individual array1 element and its 'paired element' from array2
for ($i = 0; $i < sizeof($array1); $i++) {
// If we break our condition, set our flag and break out of our loop, since we no longer need to check subsequent elements
if ($array1[$i] > $array2[$i]) {
$allValuesLessThan = false;
break;
}
}
// Finally, only do our 'something' if our condition was met
if ($allValuesLessThan) {
...do something
}

create another multi dimensional array from an array

Suppose i have an array
$x= ('A'=>31, 'B'=>12, 'C'=>13, 'D'=>25, 'E'=>18, 'F'=>10);
I need to generate an array somewhat like this
$newx = (0 => array('A'=>31 , 'B' =>1) , 1 => array('B'=>11 , 'C' =>13 , 'D'=>8) , 2 =>array('D'=>17 , 'E'=>15) , 3=>array('E'=>3,'F'=>10);
Now in this case each value of $newx has to be = 32 and this is how it will work $x[A] = 31 , $x[B] = 12 so first of all we have to make the sum quantity to be 32 keeping the index same for the new array i.e
array(0=>array('A'=>31,'B'=>1) , 1=>array('B'=>11) )
the process should continue for each value of $x.
while I'm pretty sure this is a homework assignment and well, you really should provide code of your own, at least try to, I found the thing amusing so I went ahead and gave it a try. I guess I'll be downvoted for his and I probably do deserve it, but here goes anyway.
What you need to do is:
loop through your array,
determine the elements that give you 32 and then store that result in the final array.
subtract the value of the last element from your result from the corresponding element of your working array
shrink your array next by deleting the first elements until the very first element of the array you're still working with equals the last element your last result returned.
if your last result < 32, quit.
With this in mind, please try to find a solution yourself first and don't just copy-paste the code? :)
<?php
$x = array('A'=>31, 'B'=>12, 'C'=>13, 'D'=>25, 'E'=>18, 'F'=>10);
$result = array();
function calc($toWalk){
// walk through the array until we have gathered enough for 32, return result as an array
$result = array();
foreach($toWalk as $key => $value){
$count = array_sum($result);
if($count >= 32){
// if we have more than 32, subtract the overage from the last array element
$last = array_pop(array_keys($result));
$result[$last] -= ($count - 32);
return $result;
}
$result[$key] = $value;
}
return $result;
}
// logic match first element
$last = 'A';
// loop for as long as we have an array
while(count($x) > 0){
/*
we make sure that the first element matches the last element of the previously found array
so that if the last one went from A -> C we start at C and not at B
*/
$keys = array_keys($x);
if($last == $keys[0]){
// get the sub-array
$partial = calc($x);
// determine the last key used, it's our new starting point
$last = array_pop(array_keys($partial));
$result[] = $partial;
//subtract last (partial) value used from corresponding key in working array
$x[$last] -= $partial[$last];
if(array_sum($partial) < 32) break;
}
/*
reduce the array in size by 1, dropping the first element
should our resulting first element not match the previously returned
$last element then the logic will jump to this place again and
just cut off another element
*/
$x = array_slice($x , 1 );
}
print_r($result);

Categories