php get array element by position instead of numeric key - php

I have a script that sorts input taking into account parent-child relations and the given display-order of each. A simplified array could look like this (the actual array has sub-arrays for children as well)
$output = Array
(
[7] => first array
[3] => second array
[1] => last array
)
In which the keys are the correspondings id's of the input. Now I wish to pass through this array from top to bottom in a while loop. I am not using foreach because if it has children multiple elements should be processed together, and not come again in the next 'loop'.
function recursive_func($array){
while ($i<=count($array)){
if (isset($array[$i]['children'])){
?><div><?php
recursive_function($array[$i]['children']);
$i++;
recursive_function($array[$i]['children']);
$i++;
?></div><?php
}
else{
?><div>Something</div><?php
$i++;
}
}
}
Clearly $array[$i]['children'] aren't the children of the i'th element (by position), but of the key with value i.
How can I pass through this array in the order as in $output?

You can use array_keys to get the keys in sorted order, and iterate through those.
Or can also use array_values, then you can index sequentially. Just do it right at the start.
function recursivefunction( $array ) {
$array = array_values($array);
....
}
I'm not sure why one entry having children means the next does as well, but I'll assume foreach is problematic.

Since the $output keys are not in order, one way is to do the following:
First get all the array keys:
$keys=array_keys($output);
Next, you may call the recursive_func($keys,$output):
function recursive_func($keys,$output){
$size=count($keys);
$i=0;
while ($i<=$size){
if (isset($output[$i]['children'])){
?><div><?php
$a=$output[$i]['children'];
recursive_function(array_keys($a),$a);
$i++;
$a=$output[$i]['children'];
recursive_function(array_keys($a),$a);
$i++;
?></div><?php
}
else{
?><div>Something</div><?php
$i++;
}
}
}
Please note that it is better that you set the $size of the array outside of the loop for better performance

As Garr Godfrey suggested, you can solve this with array_keys() like so
function recursive_func($array){
$keys = array_keys($array);
for ($i=0; $i<count($keys); $i++) {
if (isset($array[ $keys[$i] ]['children'])) {
recursive_func($array[ $keys[$i] ]['children']);
$i++;
recursive_func($array[ $keys[$i] ]['children']);
} else {
//sth, no $i++ here!
}
}
}
Because I am using a for loop here, the $i++ inside the loop will make it possible for you to skip one element

Related

PHP Loop array object once, or wild card key?

I have a php array, and inside the array is a reference to another php object with a numerical value.
How can i access the elements in this array without knowing that numerical id (it could be different for each array)?
In the image below, I need to get the values inside field_collection_item like so....
$content['field_image_columns'][0]['entity']['field_collection_item'][133]['field_image']
For the first array key (0) i have done the following...
$i = 0;
while($i <= 2) {
if(isset($content['field_image_columns'][$i])) {
print '<div class="column-' . $i . '">';
foreach ($content['field_image_columns'][$i]['entity']['field_collection_item'] as $fcid => $values) {
// Print field values
}
print '</div>';
}
$i++;
}
Doing a foreach loop for a single array item seems wrong - is there a method i should be using for this use case?
You can select first item of array for example with:
Use array_shift, but it will modify source array:
$cur = array_shift($content['field_image_columns'][$i]['entity']['field_collection_item']);
print $cur['field_image'];
Get keys of array with array_keys and use first element of result as a key
$ks = array_keys($content['field_image_columns'][$i]['entity']['field_collection_item']);
print $content['field_image_columns'][$i]['entity']['field_collection_item'][$ks[0]]['field_image'];
Use current function:
$cur = current($content['field_image_columns'][$i]['entity']['field_collection_item']);
print $cur['field_image'];
As with most programming, there are quite a few ways you could do it. If a foreach works, then it isn't wrong, but it may not be the best way.
// Get the current key from an array
$key = key($array);
If you don't need the key, then you can just get the value from the array.
// Get the current value from an array
$value = current($array);
Both of these will retrieve the first key/value from the array assuming you haven't advanced the pointer.
current, key, end, reset, next, & prev are all array functions that allow you to manipulate an array without knowing anything about the internals. http://php.net/manual/en/ref.array.php

Array permutations while maintaining headings

I have an array that contains any number of elements, and is allowed to be a multidimensional array, too. My testing example of such array data is:
$arr = array(
array('Material-A', 'Material-B'),
array('Profile-A', 'Profile-B', 'Profile-C'),
array('Thread-A', 'Thread-B'),
// ... any number of elements
);
From this multidimensional array I need to create a single array that is linear in the following format.
$arrFormated = array(
'Material-A',
'Material-A_Profile-A',
'Material-A_Profile-A_Thread-A',
'Material-A_Profile-A_Thread-B',
'Material-A_Profile-A_Thread-C',
'Material-A_Profile-B',
'Material-A_Profile-B_Thread-A',
'Material-A_Profile-B_Thread-B',
'Material-A_Profile-B_Thread-C',
'Material-A_Profile-C',
'Material-A_Profile-C_Thread-A',
'Material-A_Profile-C_Thread-B',
'Material-A_Profile-C_Thread-C',
'Material-B',
'Material-B_Profile-A',
'Material-B_Profile-A_Thread-A'
// Repeat similar pattern found above, etc...
);
For a recursive function, the best that I've been able to come up with thus far is as follows:
private function showAllElements($arr)
{
for($i=0; $i < count($arr); $i++)
{
$element = $arr[$i];
if (gettype($element) == "array") {
$this->showAllElements($element);
} else {
echo $element . "<br />";
}
}
}
However, this code is no where close to producing my desired results. The outcome from the above code is.
Material-A
Material-B
Profile-A
Profile-B
Profile-C
Thread-A
Thread-B
Could somebody please help me with the recursive side of this function so I may get my desired results?
I'd generally recommend thinking about what you want to be recursive. You tried to work with the current element in every recursion step, but your method needs to look at the next array element of the original Array in each recursion step. In this case, it's more useful to pass an index to your recursive function, because the 'current element' (the $arr in showAllElements($arr)) is not helpful.
I think this code should do it:
$exampleArray = array(
array('Material-A', 'Material-B'),
array('Profile-A', 'Profile-B', 'Profile-C'),
array('Thread-A', 'Thread-B','Thread-C'),
// ... any number of elements
);
class StackOverflowQuestion37823464{
public $array;
public function dumpElements($level = 0 /* default parameter: start at first element if no index is given */){
$return=[];
if($level==count($this->array)-1){
$return=$this->array[$level]; /* This is the anchor of the recursion. If the given index is the index of the last array element, no recursion is neccesarry */
}else{
foreach($this->array[$level] as $thislevel) { /* otherwise, every element of the current step will need to be concatenated... */
$return[]=$thislevel;
foreach($this->dumpElements($level+1) as $stringifyIt){ /*...with every string from the next element and following elements*/
$return[]=$thislevel.'_'.$stringifyIt;
}
}
}
return $return;
}
}
$test=new StackOverflowQuestion37823464();
$test->array=$exampleArray;
var_dump($test->dumpElements());

Remove first element from simple array in loop

This question has been asked a thousand times, but each question I find talks about associative arrays where one can delete (unset) an item by using they key as an identifier. But how do you do this if you have a simple array, and no key-value pairs?
Input code
$bananas = array('big_banana', 'small_banana', 'ripe_banana', 'yellow_banana', 'green_banana', 'brown_banana', 'peeled_banana');
foreach ($bananas as $banana) {
// do stuff
// remove current item
}
In Perl I would work with for and indices instead, but I am not sure that's the (safest?) way to go - even though from what I hear PHP is less strict in these things.
Note that after foreach has run, I expected var_dump($bananas) to return an empty array (or null, but preferably an empty array).
1st method (delete by value comparison):
$bananas = array('big_banana', 'small_banana', 'ripe_banana', 'yellow_banana', 'green_banana', 'brown_banana', 'peeled_banana');
foreach ($bananas as $key=>$banana) {
if($banana=='big_banana')
unset($bananas[$key]);
}
2nd method (delete by key):
$bananas = array('big_banana', 'small_banana', 'ripe_banana', 'yellow_banana', 'green_banana', 'brown_banana', 'peeled_banana');
unset($bananas[0]); //removes the first value
unset($bananas[count($bananas)-1]); //removes the last value
//unset($bananas[n-1]); removes the nth value
Finally if you want to reset the keys after deletion process:
$bananas = array_map('array_values', $bananas);
If you want to empty the array completely:
unset($bananas);
$bananas= array();
it still has the indexes
foreach ($bananas as $key => $banana) {
// do stuff
unset($bananas[$key]);
}
for($i=0; $i<count($bananas); $i++)
{
//doStuff
unset($bananas[$i]);
}
This will delete every element after its use so you will eventually end up with an empty array.
If for some reason you need to reindex after deleting you can use array_values
How about a while loop with array_shift?
while (($item = array_shift($bananas)) !== null)
{
//
}
Your Note: Note that after foreach has run, I expected var_dump($bananas) to return an empty array (or null, but preferably
an empty array).
Simply use unset.
foreach ($bananas as $banana) {
// do stuff
// remove current item
unset($bananas[$key]);
}
print_r($bananas);
Result
Array
(
)
This question is old but I will post my idea using array_slice for new visitors.
while(!empty($bananas)) {
// ... do something with $bananas[0] like
echo $bananas[0].'<br>';
$bananas = array_slice($bananas, 1);
}

Function is changing array from numeric to associative

This is supposed to take any rows where ing_name is duplicated, combine the eff_name fields and delete the duplicate but it also has the side effect of changing the array from numeric to associative. My ajax is expecting numeric array.
for($i=count($recipe)-1; $i>0; $i--) {
if($recipe[$i]['ing_name'] == $recipe[$i-1]['ing_name']) { //check for duplicate. **array must be sorted by ing_name**
$recipe[$i-1]['eff_name'] .= ', '.$recipe[$i]['eff_name']; //Combine eff_name of duplicates
$recipe[$i-1]['link'] = true;
unset($recipe[$i]); //remove duplicate index
}
}
examples: NUM, ASSOC
Source
EDIT: So i figured it must have something to do with unsetting the index so I did this and it seems to work ok:
$newRecipe = array();
foreach($recipe as $r) {
$newRecipe[] = $r;
}
New question, is there a better way?
unset works with named keys. You could use array_splice instead, or get a brand new array after the loop with array_values (but that would be ugly!).
array_values() Will return a numerically indexed array

Can items in PHP associative arrays not be accessed numerically (i.e. by index)?

I'm trying to understand why, on my page with a query string,
the code:
echo "Item count = " . count($_GET);
echo "First item = " . $_GET[0];
Results in:
Item count = 3
First item =
Are PHP associative arrays distinct from numeric arrays, so that their items cannot be accessed by index? Thanks-
They can not. When you subscript a value by its key/index, it must match exactly.
If you really wanted to use numeric keys, you could use array_values() on $_GET, but you will lose all the information about the keys. You could also use array_keys() to get the keys with numerical indexes.
Alternatively, as Phil mentions, you can reset() the internal pointer to get the first. You can also get the last with end(). You can also pop or shift with array_pop() and array_shift(), both which will return the value once the array is modified.
Yes, the key of an array element is either an integer (must not be starting with 0) or an associative key, not both.
You can access the items either with a loop like this:
foreach ($_GET as $key => $value) {
}
Or get the values as an numerical array starting with key 0 with the array_values() function or get the first value with reset().
You can do it this way:
$keys = array_keys($_GET);
echo "First item = " . $_GET[$keys[0]];
Nope, it is not possible.
Try this:
file.php?foo=bar
file.php contents:
<?php
print_r($_GET);
?>
You get
Array
(
[foo] => bar
)
If you want to access the element at 0, try file.php?0=foobar.
You can also use a foreach or for loop and simply break after the first element (or whatever element you happen to want to reach):
foreach($_GET as $value){
echo($value);
break;
}
Nope -- they are mapped by key value pairs. You can iterate the they KV pair into an indexed array though:
foreach($_GET as $key => $value) {
$getArray[] = $value;
}
You can now access the values by index within $getArray.
As another weird workaround, you can access the very first element using:
print $_GET[key($_GET)];
This utilizes the internal array pointer, like reset/end/current(), could be useful in an each() loop.

Categories