I have two iterators which i have to merge into one result.
Here are data samples:
ArrayIterator Object
(
[storage:ArrayIterator:private] => Array
(
[0] => Array
(
[period] => 04/04/2012 16:00:00
[bl_subs] => 1
[bl_unsubs] => 1
[bl_block_total] => 1
)
[1] => Array
(
[period] => 04/04/2012 17:00:00
[bl_subs] => 1
[bl_unsubs] => 2
[bl_block_total] => 0
)
[2] => Array
(
[period] => 04/04/2012 18:00:00
[bl_subs] => 0
[bl_unsubs] => 0
[bl_block_total] => -1
)
[3] => Array
(
[period] => 04/04/2012 19:00:00
[bl_subs] => 2
[bl_unsubs] => 0
[bl_block_total] => -2
)
[4] => Array
(
[period] => 04/04/2012 20:00:00
[bl_subs] => 2
[bl_unsubs] => 0
[bl_block_total] => 1
)
)
)
ArrayIterator Object
(
[storage:ArrayIterator:private] => Array
(
[0] => Array
(
[period] => 04/04/2012 15:00:00
[bl_avg] => 5
[bl_full] => 0
)
[1] => Array
(
[period] => 04/04/2012 17:00:00
[bl_avg] => 0
[bl_full] => 7
)
[2] => Array
(
[period] => 04/04/2012 18:00:00
[bl_avg] => 1
[bl_full] => 0
)
)
)
I would like to merge them by the key ‘period’ into a summary iterator.
The final result should look like:
ArrayIterator Object
(
[storage:ArrayIterator:private] => Array
(
[0] => Array
(
[period] => 04/04/2012 15:00:00
[bl_subs] => 0
[bl_unsubs] => 0
[bl_avg] => 5
[bl_full] => 0
[bl_block_total] => 0
)
[1] => Array
(
[period] => 04/04/2012 16:00:00
[bl_subs] => 1
[bl_unsubs] => 1
[bl_avg] => 0
[bl_full] => 0
[bl_block_total] => 1
)
[2] => Array
(
[period] => 04/04/2012 17:00:00
[bl_subs] => 1
[bl_unsubs] => 2
[bl_avg] => 0
[bl_full] => 7
[bl_block_total] => 0
)
[3] => Array
(
[period] => 04/04/2012 18:00:00
[bl_subs] => 0
[bl_unsubs] => 0
[bl_avg] => 1
[bl_full] => 0
[bl_block_total] => -1
)
[4] => Array
(
[period] => 04/04/2012 19:00:00
[bl_subs] => 2
[bl_unsubs] => 0
[bl_avg] => 0
[bl_full] => 0
[bl_block_total] => -2
)
[5] => Array
(
[period] => 04/04/2012 20:00:00
[bl_subs] => 2
[bl_unsubs] => 0
[bl_avg] => 0
[bl_full] => 0
[bl_block_total] => 1
)
)
)
It will be best if we are not using foreach,for,while or any other loop. That’s because the data will be big and we don’t want to have memory issues. I was trying using current() and next() to use the inner array pointers.
If anyone knows a way out of this, please advise.
It both iterators are always sorted, you could cache them both, compare per each iteration which one comes first (if not equal) and process that one. If equal, process both equally.
Not equal:
Equal: (one iteration skipped)
You can wrap this processing into an
Iteratorof it’s own so it’s nicely encapsulated. Because the information given was limited, I created a simplified example that reduces the date to the problem’s domain: Iterate over two iterators at once. If both iterators are equal, return both. If not equal, return the one that would be first when both are compared.The simplified data used:
Just two arrays that contain the compare-value. Those are turned into the two iterators:
The problem written out is limited to two iterators. To solve the problem more generically it should work with 0 or more iterators. So what happens is that per each iteration the iterators are compared with each other based on their current value. For that a comparison function is used. You could compare that with how
usortDocs works: A function compares A and B and based on the two it returns an integer value:This allows to compare an unlimited number of pairs with each other. It only needs two functions: One that obtains the current value from an iterator we use, and the other one that does the actual comparison between A and B (actually you can merge both into one function, but as this is exemplary and your arrays/iterators differ a bit, I thought it’s worth to separate so you can modify it more easily later on). So first the function to get a value out of an iterator, I compare with ISO date-time values because I can do this with a simple
strcmp:All this function does is to get a value that can be easily compared from an iterator. This does not yet do the comparison described above, so another function is needed which will use this comparison-value function as a dependency:
And that’s the compare function now, based on the
strcmpstring comparison function and using the$compareValuefunction to obtain the strings for comparison.So let’s say you have an array with two iterators, it’s now possible to sort it. It’s also possible to compare the first element with the next element to say whether those are equal.
That done, it’s now possible to create an iterator that consists of multiple iterators and at each iteration, the attached iterators are sorted and only the first iterator (and those equal to it) will be returned as current and also forwarded. Something like this flow:
As the sorting is done with the comparison function already, only this iteration logic needs to be encapsulated. As the sorting works for an array of any size (0 or more elements), it’s already generalized. The usage example:
This usage example will output the iteration it’s in and the associated value(s) to that iteration. In this case only the time information as the example arrays only consist of these. It also puts into the square brackets of which iterators it is from (0 for the first, 1 for the second). This generates the following output:
As you can see, for those comparison values that are equal in both (pre-sorted) iterators, are returned as a pair. In your case you would need to further process these values, e.g. merge them while providing default values:
So that’s actually the usage of that
MergeCompareIterator. The implementation is rather straight forward, this one does not cache the sorting/current iterators so far, I leave this as an exercise if you want to improve it.The full code:
Hope this is helpful. It does only work with presorted data within your “inner” iterators, otherwise the merge/append strategy with the comparison of current elements does not make sense.