This question already has answers here:
Get next element in foreach loop
(11 answers)
Closed 9 years ago.
I am looking for a way to get the next and next+1 key/value pair in a foreach(). For example:
$a = array('leg1'=>'LA', 'leg2'=>'NY', 'leg3'=>'NY', 'leg4'=>'FL');
foreach($a AS $k => $v){
if($nextval == $v && $nextnextval == $v){
//staying put for next two legs
}
}
You can't access that way the next and next-next values.
But you can do something similar:
$a = array('leg1'=>'LA', 'leg2'=>'NY', 'leg3'=>'NY', 'leg4'=>'FL');
$keys = array_keys($a);
foreach(array_keys($keys) AS $k ){
$this_value = $a[$keys[$k]];
$nextval = $a[$keys[$k+1]];
$nextnextval = $a[$keys[$k+2]];
if($nextval == $this_value && $nextnextval == $this_value){
//staying put for next two legs
}
}
I've found the solution with complexity O(n) and does not require seeking through array back and forward:
$a = array('leg1'=>'LA', 'leg2'=>'NY', 'leg3'=>'NY', 'leg4'=>'FL');
// initiate the iterator for "next_val":
$nextIterator = new ArrayIterator($a);
$nextIterator->rewind();
$nextIterator->next(); // put the initial pointer to 2nd position
// initiaite another iterator for "next_next_val":
$nextNextIterator = new ArrayIterator($a);
$nextNextIterator->rewind();
$nextNextIterator->next();
$nextNextIterator->next(); // put the initial pointer to 3rd position
foreach($a AS $k => $v){
$next_val = $nextIterator->current();
$next_next_val = $nextNextIterator->current();
echo "Current: $v; next: $next_val; next_next: $next_next_val" . PHP_EOL;
$nextIterator->next();
$nextNextIterator->next();
}
Just remember to test for valid() if you plan to relay on the $next_val and $next_next_val.
Here's one way to do it:
while($current = current($a)) {
$next = next($a);
$nextnext = next($a);
// Comparison logic here
prev($a); // Because we moved the pointer ahead twice, lets back it up once
}
Example: http://3v4l.org/IGCXW
Note that the loop written this way will never examine the last element in your original array. That could be fixed, although with your current logic it doesn't seem to matter since there are no "more" elements to compare the last one to.
Have a look at CachingIterator, as described in this answer:
Peek ahead when iterating an array in PHP
Or use array_keys() is in another answer posted for the same question, e.g.
$keys = array_keys($array);
for ($i = 0; $i < count($keys); $i++) {
$cur = $array[$keys[$i]];
$next = $array[$keys[$i+1]];
}
You can't simply "stay put" in a loop. I suspect you're looking to do something a lot easier than write custom iterators. If you simply want to ignore entries with duplicate keys, then track the last key and compare it to the current one.
$a = array('leg1'=>'LA', 'leg2'=>'NY', 'leg3'=>'NY', 'leg4'=>'FL');
// Prints LA NY FL
$last_v = null;
foreach ( $a as $k => $v ){
if ( $last_v == $v ) {
/**
* Duplicate value, so skip it
*/
continue;
}
echo $v.' ';
$last_v = $v;
}
Funny, I'm programming PHP for a decade (with years pause), but I needed one-by-one walk functions just a week ago.
Here you are: next, prev, reset etc. See "see also" section. Also, check array_keys()
Related
I have an array $scripts_stack = []; that holds arrays:
$array_item = array('script' => $file_parts, 'handle' => $file_name, 'src' => $url);
array_push($scripts_stack, $array_item);
<?php
for ($i = 0; $i < sizeof($scripts_stack); $i++) {
$child_array = $scripts_stack[$i];
if (is_array($child_array)) {
// Do things with $child_array,
// then remove the child array from $scripts_stack when done with (BELOW)
unset($scripts_stack[$i]);
}
}
echo "Array Size : " . (sizeof($scripts_stack)); // AT THE END
However, my attemts only remove half the elements. No matter what I try, it's only half the items that get removed. sizeof($scripts_stack) is always half the size of what it was at the start.
I'm expecting that it would be empty // AT THE END
Why is it that I only get half the elements in the array removed?
Thank you all in advance.
As mentioned in other answers, $i increments but the sizeof() the array shrinks. foreach() is probably the most flexible looping for arrays as it exposes the actual key (instead of hoping it starts at 0 and increments by 1) and the value:
foreach ($scripts_stack as $key => $child_array) {
if (is_array($child_array)) {
// Do things with $child_array,
// then remove the child array from $scripts_stack when done with (BELOW)
unset($scripts_stack[$key]);
}
}
Just FYI, the way you're doing it with for almost works. You just need to establish the count before the loop definition, rather than recounting in the continuation condition.
$count = sizeof($scripts_stack);
for ($i = 0; $i < $count; $i++) { // ...
Still, I think it would be better to just use a different type of loop as shown in the other answers. I'd personally go for foreach since it should always iterate every element even if some indexes aren't present. (With the way you're building the array, it looks like the indexes should always be sequential, though.)
Another possibility is to shift elements off of the array rather than explicitly unsetting them.
while ($child_array = array_shift($scripts_stack)) {
// Do things with $child_array,
}
This will definitely remove every element from the array, though. It looks like $child_array should always be an array, so the is_array($child_array) may not be necessary, but if there's more to it that we're not seeing here, and there are some non-array elements that you need to keep, then this won't work.
You advanced $i while the array is getting shrinked, but in the same time you jump over items in your array.
The first loop is where $i == 0, and then when you removed item 0 in your array, the item that was in the second place has moved to the first place, and your $i == (so you will not remove the item in the current first place, and so on.
What you can do is use while instead of for loop:
<?php
$i = 0;
while ($i < sizeof($scripts_stack)) {
$child_array = $scripts_stack[$i];
if (is_array($child_array)) {
// Do things with $child_array,
// then remove the child array from $scripts_stack when done with (BELOW)
unset($scripts_stack[$i]);
} else {
$i++;
}
}
echo "Array Size : " . (sizeof($scripts_stack)); // AT THE END
May be you can use this script.It's not tested.
foreach($array as $key => $value ) {
unset($array[$key]);
echo $value." element is deleted from your array</br>";
}
I hope , it will help you.
The problem root is in comparing $i with sizeof($scripts_stack). Every step further sizeof($scripts_stack) becomes lower (it calculates at every step) and $i becomes higher.
The workaround may look like this:
<?php
$scripts_stack = [];
$array_item = array('script' => 1, 'handle' => 2, 'src' => 3);
array_push($scripts_stack, $array_item);
array_push($scripts_stack, $array_item);
array_push($scripts_stack, $array_item);
array_push($scripts_stack, $array_item);
array_push($scripts_stack, $array_item);
while (sizeof($scripts_stack) > 0) {
$child_array = array_shift($scripts_stack);
if (is_array($child_array)) {
// Do things with $child_array,
// then remove the child array from $scripts_stack when done with (BELOW)
}
}
echo "Array Size : " . (sizeof($scripts_stack)); // AT THE END
https://3v4l.org/N2p3v
Hi I'm looking forward do find an easy solution to find and enumerate double values.
array("Papples", "Gelato", "Gelato", "Banana", "Papples","Papples")
to:
array("Papples", "Gelato", "Gelato2", "Banana", "Papples2","Papples3")
I could do it with a loop with if and write to second array procedure but isn't there a better solution for it?
Thank you!
you can try
<?php
$arr = array("Papples", "Gelato", "Gelato", "Banana", "Papples","Papples");
$countarray = array_count_values($arr);
$resultarray = array();
foreach ($countarray as $key=>$value) {
for ($i = 1; $i <= $value; $i++) {
$resultarray[]=$key.$i;
}
}
print_r($resultarray);
?>
Go with the loop and if, it's not difficult and will be pretty fast.
$delicatessen = [
"Papples", "Gelato", "Gelato", "Banana", "Papples","Papples"
];
foreach ($delicatessen as $e) {
if (#$counter[$e]++) $e .= $counter[$e];
$new[] = $e;
}
Basically what this does is to always add the element to the new array,
clearly, but modifying it or not. The condition is the $counter array
which will store the amount of times an item appears. All is created "on
the fly", PHP allows that.
When we retrieve $counter[$e] this element does not exist yet, so the
returned value makes the condition fail. However there is a side effect
that after the undef is returned, it will be increased (with ++) so
now $counter[$e] will be 1.
If on a future iteration this is accessed again the returned value of
1 will make the condition pass, with the side affect that at the point
the if statement is executed $counter[$e] will already be 2. The
statement concatenated this number to the end of the element.
This way, in the first time nothing is concatenated but there is the
side effect. On next iterations the number is concatenated to the
element.
The # operator is used here to suppress PHP notices. Since you're
dealing with undefined elements on first pass, you would get notices.
Script wouldn't break though. Of course this operator should be used
with caution, but in this case it just really helps simplifying code,
making it less strict.
$arr = array("Papples", "Gelato", "Gelato", "Banana", "Papples","Papples");
$arr_count = array_count_values($arr);
$new_arr = array();
foreach($arr_count as $key=>$val){
if($val > 1){
$k = 1;
for($i=0; $i<$val; $i++){
if($i==0){
$new_arr[] = $key;
}else{
$new_arr[] = $key.++$k;
}
}
}else{
$new_arr[] = $key;
}
}
echo "<pre>"; print_r($new_arr);
Test script: https://3v4l.org/iGf1s
This question already has answers here:
Removing a value from a PHP Array
(3 answers)
Closed 8 years ago.
I have an array that looks like this
$hours_worked = array('23',0,'24',0)
What want to do is to loop through the array and remove the element that contains 0
What I have so far is:
for($i = 0; $i < count($hours_worked); $i++)
{
if($hours_worked[$i] === 0 )
{
unset($hours_worked[$i]);
}
}
However the result I get is $hours_worked = array(23,24,0). I.e. it does not remove the final element from the array. Any ideas?
The problem with this code is that you are calculating count($hours_worked) on each iteration; once you remove an item the count will decrease, which means that for each item removed the loop will terminate before seeing one item at the end of the array. So an immediate way to fix it would be to pull the count out of the loop:
$count = count($hours_worked);
for($i = 0; $i < $count; $i++) {
// ...
}
That said, the code can be further improved. For one you should always use foreach instead of for -- this makes the code work on all arrays instead of just numerically indexed ones, and is also immune to counting problems:
foreach ($hours_worked as $key => $value) {
if ($value === 0) unset($hours_worked[$key]);
}
You might also want to use array_diff for this kind of work because it's an easy one-liner:
$hours_worked = array('23',0,'24',0);
$hours_worked = array_diff($hours_worked, array(0)); // removes the zeroes
If you choose to do this, be aware of how array_diff compares items:
Two elements are considered equal if and only if (string) $elem1 ===
(string) $elem2. In words: when the string representation is the
same.
Try foreach loops instead
$new_array = array();
foreach($hours_worked as $hour) {
if($hour != '0') {
$new_array[] = $hour;
}
}
// $new_array should contain array(23, 24)
this is my first php script and problem, I've searched hours with no conclusion other than looping a function" too many laterations". but it doesn't solve my problem I've never studied programming or what ever so I'm hoping that there is an educated person to fill me in on this:
I have an array that contains 120 elements; consists of duplicates eg:
myArray = [0]= item_1, [1] = item _1, [2] = item_2, [3] = item_3 ect..
Briefly I'm trying to make a flash php pokermachine but I need these items in the array to be shuffled BUT I do not want the duplicates to be next to each other after the shuffle but I need the duplicates to be still in the array
I can't do a loop function to check this because it will change the shuffle too many times which will effect the odds of the game: below is what I currently have:
/ * Removed the link here that is no longer available */
you may notice at times it will double up with 2 items in the same reel
Basically I created the virtual reel dynamically with php.ini file
these values are repeatedly pushed into an array($virtualreel) so the value may appear 10 times in the reel and another value will appear 5 times variating the odds. Then after I take a random slice() from the $virtualreel to display 3 vars from this reel and repeat the loop 4 more times for the other reels, also I only can shuffle once as I want the slice() to be from the same reels array order
I only shuffle every new spin not running loop functions to shuffle if I double up on a slice(array,3 items).
hope I've explained what I'm after well enough to give you guys an idea.
You can use this function:
<?php
function shuffleArray($myArray) {
$value_count = array_count_values($myArray);
foreach($value_count as $key=>$value) {
if ($value > count($myArray)/2) {
return false;
}
}
$last_value = $myArray[count($myArray) - 1];
unset($myArray[count($myArray) - 1]);
$shuffle = array();
$last = false;
while (count($myArray) > 0) {
$keys = array_keys($myArray);
$i = round(rand(0, count($keys) - 1));
while ($last === $myArray[$keys[$i]]) {
$i = round(rand(0, count($keys) - 1));
}
$shuffle[] = $myArray[$keys[$i]];
$last = $myArray[$keys[$i]];
unset($myArray[$keys[$i]]);
}
if ($last_value === $last) {
$i = 0;
foreach($shuffle as $key=>$value) {
if ($value !== $last_value) {
$i = $key;
break;
}
}
array_splice($shuffle, $i + 1, 0, $last_value);
} else {
$shuffle[] = $last_value;
}
return $shuffle;
}
print_r(shuffleArray(array(1,5,5,3,7,7)));
Why not just:
Edit :
$shuffled = array();
while(count($to_shuffle) > 0):
$i = rand(0, count($to_shuffle)-1);
$shuffled[] = $to_shuffle[$i];
array_splice($to_shuffle, $i, 1,null);
endwhile;
I think this is what you were expecting, if you don't mind not preserving the association between keys and values.
I need to combine two foreach statement into one for example
foreach ($categories_stack as $category)
foreach ($page_name as $value)
I need to add these into the same foreach statement
Is this possible if so how?
(I am not sure I have understood your question completely. I am assuming that you want to iterate through the two lists in parallel)
You can do it using for loop as follows :
$n = min(count($category), count($value));
for($c = 0; $c < $n; $c = $c + 1){
$categories_stack = $category[$c];
$pagename = $value[$c];
...
}
To achieve the same with foreach you need a function similar to Python's zip() function.
In Python, it would be :
for categories_stack, pagename in zip(categories, values):
print categories_stack, pagename
Since PHP doesn't have a standard zip() function, you'll have to write such a function on your own or go with the for loop solution.
You can do nested foreachs if that's what you want. But without knowing more of your data, it's impossible to say if this helps:
foreach ($categories_stack as $category) {
foreach ($page_name as $value) {
}
}
Probably you want to print out all pages in a category? That probably won't work, so can you give a bit more info on how the arrays look like and relate to each other?
This loop will continue to the length of the longest array and return null for where there are no matching elements in either of the arrays. Try it out!
$a = array(1 => "a",25 => "b", 10 => "c",99=>"d");
$b = array(15=>1,5=>2,6=>3);
$ao = new ArrayObject($a);
$bo = new ArrayObject($b);
$ai = $ao->getIterator();
$bi = $bo->getIterator();
for (
$ai->rewind(),$bi->rewind(),$av = $ai->current(),$bv = $bi->current();
list($av,$bv) =
array(
($ai->valid() ? $ai->current() : null),
($bi->valid() ? $bi->current() : null)
),
($ai->valid() || $bi->valid());
($ai->valid() ? $ai->next() : null),($bi->valid() ? $bi->next() : null))
{
echo "\$av = $av\n";
echo "\$bv = $bv\n";
}
I cannot really tell from the question exactly how you want to traverse the two arrays. For a nested foreach you simply write
foreach ($myArray as $k => $v) {
foreach ($mySecondArray as $kb => $vb {
}
}
However you can do all sorts of things with some creative use of callback functions. In this case an anonymous function returning two items from each array on each iteration. It's then easy to use the iteration value as an array or split it into variables using list() as done below.
This also has the added benefit of working regardless of key structure. I's purely based on the ordering of array elements. Just use the appropriate sorting function if the elements are out of order.
It does not worry about the length of the arrays as there is no error reported, so make sure you keep an eye out for empty values.
$a = array("a","b","c");
$b = array(1,2,3);
foreach (
array_map(
create_function(
'$a,$b', 'return array($a,$b);'
)
,$a,$b
)
as $value
)
{
list($a,$b) = $value;
echo "\$a = $a\n";
echo "\$b = $b\n";
}
Output
$a = a
$b = 1
$a = b
$b = 2
$a = c
$b = 3
Here's another one for you that stops on either of the lists ending. Same as using min(count(a),count(b). Useful if you have arrays of same length. If someone can make it continue to the max(count(a),count(b)) let me know.
$ao = new ArrayObject($a);
$bo = new ArrayObject($b);
$ai = $ao->getIterator();
$bi = $bo->getIterator();
for (
$ai->rewind(),$bi->rewind();
$av = $ai->current(),$bv=$bi->current();
$ai->next(),$bi->next())
{
echo "\$av = $av\n";
echo "\$bv = $bv\n";
}
This is where the venerable for loop comes in handy:
for(
$i = 0,
$n = sizeof($categories_stack),
$m = sizeof($page_name);
$i < $n && $i < $m;
$i++
) {
$category = $categories_stack[$i];
$value = $page_name[$i];
// do stuff here ....
}
Surely you can just merge the arrays before looping?
$data = array_merge($categories_stack, $page_name);
foreach($data AS $item){
...
}
Do the array elements have a direct correspondence with one another, i.e. is there an element in $page_name for each element in $categories_stack? If so, just iterate over the keys and values (assuming they have the same keys):
foreach ($categories_stack as $key => $value)
{
$category = $value;
$page = $page_name[$key];
// ...
}
Could you just nest them with variables outside the scope of the foreach, or prehaps store the content as an array similar to a KVP setup? My answer is vague but I'm not really sure why you're trying to accomplish this.