-
-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SimpleCachingIteratorAggregate doesn't handle nested traversals #49
Comments
Thanks for reporting the issue, I'll try to have a look tonight. Have you already made some checks to try to find where the issue comes from? |
I guess the inner loop iterates through the same iterator until the end, thereby leaving outer loop without remaining results |
A possible solution to this (but I don't like it) would be to consume the cache in the constructor, as such: public function __construct(Iterator $iterator, bool $cacheNow = false)
{
$this->iterator = new CachingIterator(
$iterator,
CachingIterator::FULL_CACHE
);
if ($cacheNow) {
iterator_to_array($this->iterator);
}
} Could you let me know if this would fix the issue for you? Eventually, we could add a method in the class: public function consume(): void {
iterator_to_array($this->iterator);
} Thx |
Yes, though if cached right away we'll lose lazy nature of given collection. For my specific case I can't really use it like this. Anyway, adding Maybe it would make sense to keep track of iterated items in the |
Hi there! Actually I have found the solution. Could you check it out? The solution comes to keeping iteration map in the stack trace so that cached state may be quickly compared with items having already been iterated over. Anything extra found in the cache is the result of side effects coming from the inner loop. These values must be yielded as well. /** @return Generator<array-key|TKey, T> */
public function getIterator(): Generator
{
$iterationMap = [];
$initiallyCachedItems = $this->iterator->getCache();
yield from $initiallyCachedItems;
if ($this->iterator->hasNext()) {
$iterationMap = array_fill_keys(array_keys($initiallyCachedItems), true);
}
while ($this->iterator->hasNext()) {
$this->iterator->next();
$iterationMap[$key = $this->iterator->key()] = true;
yield $key => $this->iterator->current();
$latestCachedItems = $this->iterator->getCache();
if (count($latestCachedItems) === count($iterationMap)) {
// No extra items were added to the cache since last yield iteration
continue;
}
// The cache pointer has shifted since last iteration (code inside loop traversed part of cache iterator),
// hence these items must be yielded as well
$extraCachedItems = array_diff_key($latestCachedItems, $iterationMap);
$iterationMap += array_fill_keys(array_keys($extraCachedItems), true);
yield from $extraCachedItems;
}
} Since Also, regarding memory usage, no extra memory is allocated once iteration has completed due to Thank you for your time and have a nice evening. |
Have you had a chance to check it out? |
Hello there, I don't have time to test and give some feedback. Don't worry, as soon as I'll get back on a computer I will check it out. By the way, I wrote you an email early December, did you get it? |
I'm not sure regarding email, since I don't see anything in my mailbox. BTW, where could've you get my address? Anyway, sorry for disappearing from my side. |
I've emailed you on the 6th of December, at 9.34, on the address: Basically, I was worried to not have any more feedback, I was wondering if it was me who did something wrong. |
Hello, I quickly checked your proposal, but I'm afraid this is not going to make it.
In overall, I have the feeling that this proposal is going to fix an issue that pretty much nobody will ever encounter. IMHO, the best option we have here is to update the documentation and mention that it doesn't work with nested traversals, unless we use the method I proposed in #49 (comment) |
Thanks for your response!
Isn't purpose of |
Absolutely, you're correct. The My experience in designing iterators for a broad spectrum of types momentarily diverted my attention. Thank you for pointing out the oversight in my previous response. |
Regarding the usage of Instead of keeping track of all previously iterated items, it is possible to keep track of last yielded key only (since keys of array are unique). Hence, if last yielded key is not the same as last key in cached items array, inner iterator has already been iterated over and remaining items have to be yielded from the cache. Please, let me know your thoughts on this approach. |
Keeping track of the last item is definitely a much better solution... going from |
Since this issue has not had any activity within the last 5 days, I have marked it as stale. |
Hi there again. Could you check out #50 ? |
Since this issue has not had any activity within the last 5 days, I have marked it as stale. |
Steps required to reproduce the problem
This test shows the expected behavior and it fails on the last assert, since the actual result is 3 instead of 6.
Expected Result
It is expected that one may iterate through iterator until the end regardless of whether inner iterations happen or not,
Actual Result
Test fails on final
self::assertSame(6, $total);
, because outer loop iterated only over 3 items instead of 6.The text was updated successfully, but these errors were encountered: