Iterate a PHP array from a specific key - php

I know how to iterate an array in PHP, but I want to iterate an array from a specific key.
Assume that I have a huge array
$my_array = array(
...
...
["adad"] => "value X",
["yy"] => "value Y",
["hkghgk"] => "value Z",
["pp"] => "value ZZ",
...
...
)
I know the key where to start to iterate ("yy"). Now I want to iterate only from this key to another key.
I know that I don't want to do this:
$start_key = "yy";
foreach ($my_array as $key => $v)
{
if ($key == $start_key)
...
}
I was looking for Iterator, but I don't think this is what I need.

Try combining array_search, array_key, and LimitIterator. Using the example from the LimitIterator page and some extra bits:
$fruitsArray = array(
'a' => 'apple',
'b' => 'banana',
'c' => 'cherry',
'd' => 'damson',
'e' => 'elderberry'
);
$startkey = array_search('d', array_keys($fruitsArray));
$fruits = new ArrayIterator($fruitsArray);
foreach (new LimitIterator($fruits, $startkey) as $fruit) {
var_dump($fruit);
}
Starting at position 'd', this outputs:
string(6) "damson" string(10) "elderberry"
There is a limit to this approach in that it won’t loop around the array until the start position again. It will only iterate to the end of an array and then stop. You would have to run another foreach to do the first part of the array, but that can be easily done with the code we already have.
foreach (new LimitIterator($fruits, 0, $startkey-1) as $fruit) {
var_dump($fruit);
}
This starts from the first element, up to the element before the one we searched for.

foreach always resets the array's array pointer. You just can't do that the way you imagine.
You still have a few ways. The foreach way is just skipping everything until you found the key once:
$start_key = "yy";
$started = false;
foreach ($my_array as $key => $v)
{
if ($key == $start_key) {
$started = true;
}
if (!$started) {
continue;
}
// your code
}
You could as well work with the array pointer and use the while (list($key, $v) = each($array)) method:
$start_key = "yy";
reset($array); // reset it to be sure to start at the beginning
while (list($key, $v) = each($array) && $key != $start_key); // set array pointer to $start_key
do {
// your code
} while (list($key, $v) = each($array));
Alternatively, you can just extract the array you want to iterate over like MarkBaker proposed.

Perhaps something like:
foreach(array_slice(
$my_array,
array_search(
$start_key,array_keys($my_array)
),
null,
true) as $key => $v) {}
Demo

You can use array_keys and array_search.
Like this:
$keys = array_keys( $my_array ); // store all of your array indexes in a new array
$position = array_search( "yy" ,$keys ); // search your starting index in the newly created array of indexes
if( $position == false ) exit( "Index doesn't exist" ); // if the starting index doesn't exist the array_search returns false
for( $i = $position; $i < count( $keys ); $i++ ) { // starting from your desired index, this will iterate over the rest of your array
// do your stuff to $my_array[ $keys[ $i ] ] like:
echo $my_array[ $keys[ $i ] ];
}

Try it like this:
$startkey = array_search('yy', array_keys($my_array));
$endkey = array_search('zz', array_keys($my_array));
$my_array2 = array_values($my_array);
for($i = $startkey; $i<=$endkey; $i++)
{
// Access array like this
echo $my_array2[$i];
}

If this pull request makes it through, you will be able to do this quite easily:
if (seek($array, 'yy', SEEK_KEY)) {
while ($data = each($array)) {
// Do stuff
}
}

Related

Convert a nested Array to Scalar and then reconvert the scalar to nested array

I have an array that looks like this.
$array = [
0 => 'abc',
1 => [
0 => 'def',
1 => 'ghi'
],
'assoc1' => [
'nassoc' => 'jkl',
'nassoc2' => 'mno',
'nassoc3' => '',
'nassoc4' => false
]
];
The $array can have numeric keys or be an assoc array or a mixed one. The level of nesting is not known. Also the values of the array can also be bool or null or an empty string ''
I need to able to convert this into a scalar array with key value pairs. And then later reconvert it back to the exact same array.
So the scalar array could look like
$arrayScalar = [
'0' => 'abc',
'1[0]' => 'def',
'1[1]' => 'ghi',
'assoc1[nassoc]' => 'jkl',
'assoc1[nassoc2]' => 'mno',
'assoc1[nassoc3]' => '',
'assoc1[nassoc4]' => false
];
And then later be able to get back to the initial $array.
I wrote a parser and it does not currently handle bool values correctly.
I have a feeling this is at best a super hacky method to do what I am after. Also I have been able to test it only so much.
function flattenNestedArraysRecursively($nestedArray, $parent = '', &$flattened = [])
{
$keys = array_keys($nestedArray);
if (empty($keys)) {
$flattened[$parent] = 'emptyarray';
} else {
foreach ($keys as $value) {
if (is_array($nestedArray[$value])) {
$reqParent = (!empty($parent)) ? $parent . '|!|' . $value : $value;
$this->flattenNestedArraysRecursively($nestedArray[$value], $reqParent, $flattened);
} else {
$reqKey = (!empty($parent)) ? $parent . '|!|' . $value : $value;
$flattened[$reqKey] = $nestedArray[$value];
}
}
}
return $flattened;
}
function reCreateFlattenedArray($flatArray): array
{
$arr = [];
foreach ($flatArray as $key => $value) {
$keys = explode('|!|', $key);
$arr = $this->reCreateArrayRecursiveWorker($keys, $value, $arr);
}
return $arr;
}
function reCreateArrayRecursiveWorker($keys, $value, $existingArr)
{
//Outside to Inside
$keyCur = array_shift($keys);
//Check if keyCur Exists in the existingArray
if (key_exists($keyCur, $existingArr)) {
// Check if we have reached the deepest level
if (empty($keys)) {
//Return the Key => value mapping
$existingArr[$keyCur] = $value;
return $existingArr;
} else {
// If not then continue to go deeper while appending deeper levels values to current key
$existingArr[$keyCur] = $this->reCreateArrayRecursiveWorker($keys, $value, $existingArr[$keyCur]);
return $existingArr;
}
} else {
// If Key does not exists in current Array
// Check deepest
if (empty($keys)) {
//Return the Key => value mapping
$existingArr[$keyCur] = $value;
return $existingArr;
} else {
// Add the key
$existingArr[$keyCur] = $this->reCreateArrayRecursiveWorker($keys, $value, []);
return $existingArr;
}
}
}
Is there a better more elegant way of doing this, maybe http_build_query or something else I am not aware of.
Sandbox link -> http://sandbox.onlinephpfunctions.com/code/50b3890e5bdc515bc145eda0a1b34c29eefadcca
Flattening:
Your approach towards recursion is correct. I think we can make it more simpler.
We loop over the array. if the value is an array in itself, we recursively make a call to this new child subarray.
This way, we visit each key and each value. Now, we are only left to manage the keys to assign them when adding to our final resultant array, say $arrayScalar.
For this, we make a new function parameter which takes the parent key into account when assigning. That's it.
Snippet:
$arrayScalar = [];
function flatten($array,&$arrayScalar,$parent_key){
foreach($array as $key => $value){
$curr_key = empty($parent_key) ? $key : $parent_key . '[' . $key . ']';
if(is_array($value)){
flatten($value,$arrayScalar,$curr_key);
}else{
$arrayScalar[$curr_key] = $value;
}
}
}
flatten($array,$arrayScalar,'');
var_export($arrayScalar);
Demo: http://sandbox.onlinephpfunctions.com/code/1e3092e9e163330f43d495cc9d4acb672289a987
Unflattening:
This one is a little tricky.
You might have already noticed that the keys in the flattened array are of the form key1[key2][key3][key4] etc.
So, we collect all these individually in a new array, say $split_key. It might look like this.
array (
'key1',
'key2',
'key3',
'key4',
)
To achieve the above, we do a basic string parsing and added in-between keys to the array whenever we reach the end of the key string or [ or ].
Next, to add them to our final resultant array, we loop over the collected keys and check if they are set in our final array. If not so, set them. We now pass child array reference to our temporary variable $temp. This is to edit the same copy of the array. In the end, we return the result.
Snippet:
<?php
function unflatten($arrayScalar){
$result = [];
foreach($arrayScalar as $key => $value){
if(is_int($key)) $key = strval($key);
$split_key = [];
$key_len = strlen($key);
$curr = '';
// collect them as individual keys
for($i = 0; $i < $key_len; ++$i){
if($key[ $i ] == '[' || $key[ $i ] == ']'){
if(strlen($curr) === 0) continue;
$split_key[] = $curr;
$curr = '';
}else{
$curr .= $key[ $i ];
}
if($i === $key_len - 1 && strlen($curr) > 0){
$split_key[] = $curr;
}
}
// collecting them ends
//add them to our resultant array.
$temp = &$result;
foreach($split_key as $sk){
if(!isset($temp[ $sk ])){
$temp[ $sk ] = [];
}
$temp = &$temp[$sk];
}
$temp = $value;
}
return $result;
}
var_export(unflatten($arrayScalar));
Demo: http://sandbox.onlinephpfunctions.com/code/66136a699c3c5285eed3d3350ed4faa5bbce4b76

array_flip of a key value for letter and numbers

I have an array like this
array(123=>'c', 125=>'b', 139=>'a', 124=>'c', 135=>'c', 159=>'b');
and I want to flip the key/values so that duplicate values become an index for an array.
array(
'a'=>array(139),
'b'=>array(125, 159),
'c'=>array(123, 124, 135)
);
However, array_flip seems to overwrite the keys and array_chunk only splits it based on number values.
Any suggestions?
I think it's going to need you to loop over the array manually. It really shouldn't be hard though...
$flippedArray = array();
foreach( $arrayToFlip as $key => $value ) {
if ( !array_key_exists( $value, $flippedArray ) {
$flippedArray[ $value ] = array();
}
$flippedArray[ $value ][] = $key;
}
function array_flop($array) {
foreach($array as $k => $v) {
$result[$v][] = $k;
}
return array_reverse($result);
}

Sorting arrays: second last

ksort ($votes);
foreach ($votes as $total => $contestant){
$ordervotes[]= $contestant;
}
echo "<li> And the winner is: {$ordervotes[4]}</li>";
echo "<li> And the loser is: {$ordervotes[0]}</li>";
echo "<li> {$ordervotes[1]} came second last</li>";
This works fine when none of the '$total's are the same, if they are the same i get an error code. I realise I could use the 'max/min' to get the first and last elements of the array, but how do i go about finding the second last?
Thank you
Joe
Why don't you try:
echo $votes[count($votes)-2];
You also don't need to populate another array with the same values - you can keep them in $votes. You might also want to look into sorting your array by value instead of by key (which I assume you're trying to do).
If you're expecting duplicate keys, you need to remodel the way you're storing your data. Consider using a multidimensional array:
$votes = array(
array('name'=>'John','vote'=>10),
array('name'=>'James','vote'=>11),
array('name'=>'Jimmy','vote'=>13),
);
You will be able to sort this array using this function and code:
// This function will sort your array
function aasort (&$array, $key) {
$sorter=array();
$ret=array();
reset($array);
foreach ($array as $ii => $va) {
$sorter[$ii]=$va[$key];
}
asort($sorter);
foreach ($sorter as $ii => $va) {
$ret[$ii]=$array[$ii];
}
$array=$ret;
}
// Sort the array by the 'vote' key
aasort($votes,"vote");
// Echo out the name of the second-last person
echo $votes[count($votes)-2]['name'];
Use this:
function secondMax($arr) {
$max = $second = 0;
$maxKey = $secondKey = null;
foreach($arr as $key => $value) {
if($value > $max) {
$second = $max;
$secondKey = $maxKey;
$max = $value;
$maxKey = $key;
} elseif($value > $secondMax) {
$second = $value;
$secondKey = $key;
}
}
return array($secondKey, $second);
}
Usage:
$second = secondMax($votes);
You can retrieve it by using the function count:
$ordervotes[ (count($ordervotes)-2) ]
// the array starts with index 0, so (count($ordervotes)-1) is the last element
I don't understand what is in your $votes variable ... How could you have multiple contestants with the same votes (and so, with the same key).
I think there is a mistake here.
You $votes should be like this :
$votes = array(
'contestant 1' => 8,
'contestant 2' => 12,
'contestant 3' => 3
);
Then order the array : sort($votes)
Finaly, get the second last : $votes[count($votes) - 2];

php array keys problem

I have the following code:
$rt1 = array
(
'some_value1' => 'xyz1',
'some_value2' => 'xyz2',
'value_1#30'=>array('0'=>1),
'value_2#30'=>array('0'=>2),
'value_3#30'=>array('0'=>3),
'value_1#31'=>array('0'=>4),
'value_2#31'=>array('0'=>5),
'value_3#31'=>array('0'=>6),
'some_value3' => 'xyz3',
'some_value4' => 'xyz4',
);
$array_30 = array
(
'0'=>1,
1=>'2',
2=>'3'
);
$array_31 = array
(
'0'=>4,
'1'=>'5',
'2'=>'6'
);
I need to make it an array and insert the array_30 and array_31 into a DB.
foreach($rt1 as $value){
$rt2[] = $value['0'];
}
The question was updated, so here is an updated answer. Quick check, you should really try and update this to whatever more generic purpose you have, but as a proof of concept, a runnable example:
<?php
$rt1 = array
(
'some_value1' => 'xyz1',
'some_value2' => 'xyz2',
'value_1#30'=>array('0'=>1),
'value_2#30'=>array('0'=>2),
'value_3#30'=>array('0'=>3),
'value_1#31'=>array('0'=>4),
'value_2#31'=>array('0'=>5),
'value_3#31'=>array('0'=>6),
'some_value3' => 'xyz3',
'some_value4' => 'xyz4',
);
$finalArrays = array();
foreach($rt1 as $key=>$value){
if(is_array($value)){
$array_name = "array_".substr($key,-2);
${$array_name}[] = $value['0'];
}
}
var_dump($array_30);
var_dump($array_31);
?>
will output the two arrays with the numbers 1,2,3 and 4,5,6 respectivily
i assume you want to join the values of each of the second-level arrays, in which case:
$result = array();
foreach ($rt1 as $arr) {
foreach ($arr as $item) {
$result[] = $item;
}
}
Inspired by Nanne (which reminded me of dynamically naming variables), this solution will work with every identifier after the \#, regardless of its length:
foreach ( $rt1 as $key => $value )
{
if ( false == strpos($key, '#') ) // skip keys without #
{
continue;
}
// the part after the # is our identity
list(,$identity) = explode('#', $key);
${'array_'.$identity}[] = $rt1[$key]['0'];
}
Presuming that this is your actual code, probably you will need to copy the array somewhere using foreach and afterwards create the new array as you wish:
foreach($arr as $key => $value) {
$arr[$key] = 1;
}

How can I get the current array index in a foreach loop?

How do I get the current index in a foreach loop?
foreach ($arr as $key => $val)
{
// How do I get the index?
// How do I get the first element in an associative array?
}
In your sample code, it would just be $key.
If you want to know, for example, if this is the first, second, or ith iteration of the loop, this is your only option:
$i = -1;
foreach($arr as $val) {
$i++;
//$i is now the index. if $i == 0, then this is the first element.
...
}
Of course, this doesn't mean that $val == $arr[$i] because the array could be an associative array.
This is the most exhaustive answer so far and gets rid of the need for a $i variable floating around. It is a combo of Kip and Gnarf's answers.
$array = array( 'cat' => 'meow', 'dog' => 'woof', 'cow' => 'moo', 'computer' => 'beep' );
foreach( array_keys( $array ) as $index=>$key ) {
// display the current index + key + value
echo $index . ':' . $key . $array[$key];
// first index
if ( $index == 0 ) {
echo ' -- This is the first element in the associative array';
}
// last index
if ( $index == count( $array ) - 1 ) {
echo ' -- This is the last element in the associative array';
}
echo '<br>';
}
Hope it helps someone.
foreach($array as $key=>$value) {
// do stuff
}
$key is the index of each $array element
$i = 0;
foreach ($arr as $key => $val) {
if ($i === 0) {
// first index
}
// current index is $i
$i++;
}
The current index is the value of $key. And for the other question, you can also use:
current($arr)
to get the first element of any array, assuming that you aren't using the next(), prev() or other functions to change the internal pointer of the array.
You can get the index value with this
foreach ($arr as $key => $val)
{
$key = (int) $key;
//With the variable $key you can get access to the current array index
//You can use $val[$key] to
}
$key is the index for the current array element, and $val is the value of that array element.
The first element has an index of 0. Therefore, to access it, use $arr[0]
To get the first element of the array, use this
$firstFound = false;
foreach($arr as $key=>$val)
{
if (!$firstFound)
$first = $val;
else
$firstFound = true;
// do whatever you want here
}
// now ($first) has the value of the first element in the array
You could get the first element in the array_keys() function as well. Or array_search() the keys for the "index" of a key. If you are inside a foreach loop, the simple incrementing counter (suggested by kip or cletus) is probably your most efficient method though.
<?php
$array = array('test', '1', '2');
$keys = array_keys($array);
var_dump($keys[0]); // int(0)
$array = array('test'=>'something', 'test2'=>'something else');
$keys = array_keys($array);
var_dump(array_search("test2", $keys)); // int(1)
var_dump(array_search("test3", $keys)); // bool(false)
well since this is the first google hit for this problem:
function mb_tell(&$msg) {
if(count($msg) == 0) {
return 0;
}
//prev($msg);
$kv = each($msg);
if(!prev($msg)) {
end($msg);
print_r($kv);
return ($kv[0]+1);
}
print_r($kv);
return ($kv[0]);
}
based on #fabien-snauwaert's answer but simplified if you do not need the original key
$array = array( 'cat' => 'meow', 'dog' => 'woof', 'cow' => 'moo', 'computer' => 'beep' );
foreach( array_values( $array ) as $index=>$value ) {
// display the current index + value
echo $index . ':' . $value;
// first index
if ( $index == 0 ) {
echo ' -- This is the first element in the associative array';
}
// last index
if ( $index == count( $array ) - 1 ) {
echo ' -- This is the last element in the associative array';
}
echo '<br>';
}

Categories