3

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.

2

7 Answers 7

4

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.

Sign up to request clarification or add additional context in comments.

5 Comments

At that point you're better with array_slice instead of the iterator. But +1 as the OP explicitly asked for Iterators.
@bwoebi: array_slice makes a needless extra copy of everything.
@Jon I thought the Iterator variant would do a copy too, but source tells me that it's just copy on write, so, my fault, you're right.
But if you really care about performance, don't use array_keys() either, it creates a whole new array. (see my answer for the alternatives).
array_keys is only used in this approach as the key is non-numerical. If it is numerical, you can simply use array_search
2

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.

1 Comment

The downvoter would had only upvoted it if it would have been encalsulated :) - eval.in/151642 (just an assumption, I can not speak for her)
1

Perhaps something like:

foreach(array_slice(
    $my_array,
    array_search(
        $start_key,array_keys($my_array)
    ),
    null,
    true) as $key => $v) {}

Demo

Comments

0

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 ] ];
}

Comments

0

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];
}

5 Comments

@Jon array_values() uses numeric keys. And order is preserved compared to the original array.
@bwoebi: Yes, I just saw the array_values. Leaving the DV because this still loses the key information, so is not equivalent to the foreach shown in the question.
@Jon you can still get the key value of current element unless it has duplicates in array.
Note: To get current key value use $key = array_search($my_array2[$i], $my_array);
@krishna: And even then, you have to do a linear search over the array to find the key. Not good.
0

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
    }
}

Comments

0

If you create the array once and then use it many times, you can index the array keys and that would be more efficient and clean than iterating all the elements every time. See this class as an example:

<?php


class IndexedArrayIterator implements Iterator {
    
    protected $data=array();
    protected $keyIndex=array();
    protected $reverseIndex=array();
    protected $position=0;
    protected $startIndex=0;
    protected $endIndex=0;
    
    function __construct($source) {
        $this->data=$source;
        $this->reindex();
    }
    
    function reindex() {
        // Reindexing is expensive, don't do it often
        // If you need to reindex every time you search, you loose the advantage
        // If you add elements, you must reindex
        $this->keyIndex = array_keys($this->data);
        $this->reverseIndex = array_flip($this->keyIndex);
    }
    
    function setStartKey($start) {
        $this->startIndex=$this->reverseIndex[$start];
    }
    
    function setEndKey($end) {
        $this->endIndex=$this->reverseIndex[$end];
    }
    

    /////
    ///      Iterator Interface
    //

    #[\ReturnTypeWillChange]
    public function rewind() {
        $this->position=$this->startIndex;
    }

    #[\ReturnTypeWillChange]
    public function current() {
        if($this->valid()) return $this->data[$this->key()];
        else return NULL;
    }

    #[\ReturnTypeWillChange]
    public function key() {
        if($this->valid()) return $this->keyIndex[$this->position];
        else return NULL;
    }

    #[\ReturnTypeWillChange]
    public function next() {
        ++$this->position;
    }

    #[\ReturnTypeWillChange]
    public function valid() {
        if($this->position > $this->endIndex) return FALSE;
        return isset($this->keyIndex[$this->position]);
    }
    
}

Usage example:

$iterator=new IndexedArrayIterator([
    'one'=>'Item One',
    'two'=>'Item Two',
    'twoandhalf'=>'Item Two And Half',
    'three'=>'Item Three',
    'four'=>'Item Four',
]);

$iterator->setStartKey('two');
$iterator->setEndKey('three');

foreach($iterator as $key=>$value) {
    echo($key.': '.$value."\r\n");
}

Working code:

https://3v4l.org/EP0fL

4 Comments

Your echo does not benefit from the use of parentheses. There is no benefit to prepending an empty string to your other concatenated values. Please add sensible spacing to your code in accordance with PSR-12 coding standards.
@mickmackusa are you serious?
I don't personally think my first comment would make a very good joke.
@mickmackusa oh, my fault. I really think it did. (I did remove the empty string though, thanks for that)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.