我有两个迭代器,我必须合并到一个结果中。
以下是数据样本:
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
)
)
)
我想通过关键'句号'将它们合并到摘要迭代器中。
最终结果如下:
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
)
)
)
如果我们不使用foreach,for,while或任何其他循环,那将是最好的。那是因为数据很大,我们不想有内存问题。我正在尝试使用current()
和next()
来使用内部数组指针。
如果有人知道如何解决这个问题,请告知。
答案 0 :(得分:7)
这两个迭代器总是被排序,你可以将它们都缓存,每次迭代比较哪一个(如果不相等)并处理那个迭代器。如果相等,则同等处理。
不相等:
$it1[[period] => 04/04/2012 16:00:00] > $it2[[period] => 04/04/2012 15:00:00]
=> process $it2 data:
[period] => 04/04/2012 15:00:00
[bl_avg] => 5
[bl_full] => 0
as current():
[period] => 04/04/2012 15:00:00
[bl_subs] => 1
[bl_unsubs] => 1
[bl_avg] => 5
[bl_full] => 0
[bl_block_total] => 1
+ $it2->next();
注意:我不知道源数据(
$it2[0] (15:00)
)[bl_subs => 1]
,[bl_unsubs] => 1
和{{1}中不存在的元素}} 来自。这是默认值吗?
等于:(跳过一次迭代)
[bl_block_total] => 1
您可以将此处理包装到它自己的$it1[[period] => 04/04/2012 17:00:00] == $it2[[period] => 04/04/2012 17:00:00]
=> process $it1 and $it2 data:
$it1:
[period] => 04/04/2012 17:00:00
[bl_subs] => 1
[bl_unsubs] => 2
[bl_block_total] => 0
$it2:
[period] => 04/04/2012 17:00:00
[bl_avg] => 0
[bl_full] => 7
as current():
[period] => 04/04/2012 17:00:00
[bl_subs] => 1
[bl_unsubs] => 2
[bl_avg] => 0
[bl_full] => 7
[bl_block_total] => 0
+ $it1->next(); $it2->next();
中,以便将其封装得很好。因为给出的信息是有限的,我创建了一个简化的示例,减少了问题域的日期:一次迭代两个迭代器。如果两个迭代器都相等,则返回两者。如果不相等,则返回比较两者时的第一个。
使用的简化数据:
Iterator
只有两个包含比较值的数组。这些变成了两个迭代器:
$ar1 = array('04/04/2012 16:00:00', '04/04/2012 17:00:00', '04/04/2012 18:00:00', '04/04/2012 19:00:00', '04/04/2012 20:00:00');
$ar2 = array('04/04/2012 15:00:00', '04/04/2012 17:00:00', '04/04/2012 18:00:00');
写出来的问题仅限于两个迭代器。为了更一般地解决问题,它应该与0或更多迭代器一起使用。所以会发生的是,每次迭代时,迭代器会根据它们的当前值进行相互比较。为此,使用比较功能。您可以将其与usort
Docs的工作方式进行比较:函数比较A和B,并根据它返回一个整数值:
这允许比较无限数量的对。它只需要两个函数:一个从我们使用的迭代器获取当前值,另一个在A和B之间进行实际比较(实际上你可以将它们合并到一个函数中,但这是示例性的,你的数组/迭代器有点不同,我认为值得分开,以便稍后可以更容易地修改它。首先是从迭代器中获取值的函数,我与ISO日期时间值进行比较,因为我可以使用简单的$it1 = new ArrayIterator($ar1);
$it2 = new ArrayIterator($ar2);
执行此操作:
strcmp
注意:我不知道您使用的是哪种日期格式,也许我每月都会混合使用,只需交换变量,它就是自我描述性的。
所有这个函数都是为了获得一个可以从迭代器中轻松比较的值。这还没有进行上述比较,因此需要另一个函数来使用这个比较值函数作为依赖:
/**
* Get Comparison-Value of an Iterator
*
* @param Iterator $iterator
* @return string
*/
$compareValue = function(Iterator $iterator) {
$value = $iterator->current();
sscanf($value, '%d/%d/%d %s', $month, $day, $year, $timeISO);
$dateISO = sprintf('%04d-%02d-%02d %s', $year, $month, $day, $timeISO);
return $dateISO;
};
这就是比较函数,基于/**
* Compare two Iterators by it's value
*
* @param Iterator $a
* @param Iterator $b
* @return int comparison result (as of strcmp())
*/
$compareFunction = function(Iterator $a, Iterator $b) use ($compareValue) {
return strcmp($compareValue($a), $compareValue($b));
};
字符串比较函数,并使用strcmp
函数获取字符串进行比较。
所以假设你有一个带有两个迭代器的数组,现在可以对它进行排序。也可以将第一个元素与下一个元素进行比较,以确定它们是否相等。
完成后,现在可以创建一个由多个迭代器组成的迭代器,并且在每次迭代时,附加的迭代器都会被排序,只有第一个迭代器(和那些等于它的迭代器)将作为当前迭代器返回并转发。像这样的流程:
由于已经使用比较函数完成排序,因此只需要封装此迭代逻辑。由于排序适用于任何大小(0或更多元素)的数组,因此它已经推广。用法示例:
$compareValue
此用法示例将输出它所在的迭代以及与该迭代相关联的值。在这种情况下,只有作为示例数组的时间信息才包含这些。它还放入方括号中的迭代器(第一个为0,第二个为1)。这会生成以下输出:
/**
* Usage
*/
$it = new MergeCompareIterator($compareFunction, array($it1, $it2));
foreach ($it as $index => $values) {
printf("Iteration #%d:\n", $index);
foreach ($values as $iteratorIndex => $value) {
printf(" * [%d] => %s\n", $iteratorIndex, $value);
}
}
如您所见,对于那些(预先排序的)迭代器中相同的比较值,将作为一对返回。在您的情况下,您需要进一步处理这些值,例如在提供默认值的同时合并它们:
Iteration #0:
* [1] => 04/04/2012 15:00:00
Iteration #1:
* [0] => 04/04/2012 16:00:00
Iteration #2:
* [0] => 04/04/2012 17:00:00
* [1] => 04/04/2012 17:00:00
Iteration #3:
* [0] => 04/04/2012 18:00:00
* [1] => 04/04/2012 18:00:00
Iteration #4:
* [0] => 04/04/2012 19:00:00
Iteration #5:
* [0] => 04/04/2012 20:00:00
所以这实际上是$defaults = array('bl_subs' => 0, ...);
foreach ($it as $values) {
array_unshift($values, $default);
$value = call_user_func_array('array_merge', $values);
}
的用法。实现是相当直接的,到目前为止,这个并不缓存排序/当前迭代器,如果你想改进它,我把它留作练习。
完整代码:
MergeCompareIterator
希望这有帮助。它只适用于“内部”迭代器中的预分类数据,否则合并/追加策略与当前元素的比较没有意义。
答案 1 :(得分:1)
好的,这很简单。假设两个迭代器都已排序,您需要做的就是对它们进行合并排序(基本上):
function mergeIterators(Iterator $it1, Iterator $it2, $compare, $merge) {
$result = array();
//rewind both itertators
$it1->rewind();
$it2->rewind();
while ($it1->valid() || $it2->valid()) {
if (!$it1->valid()) {
$cmp = 1;
} elseif (!$it2->valid()) {
$cmp = -1;
} else {
$cmp = $compare($it1->current(), $it2->current());
}
if ($cmp === 0) {
// equal, merge together
$result[] = $merge($it1->current(), $it2->current());
$it1->next();
$it2->next();
} elseif ($cmp < 0) {
//first is less than second
$result[] = $it1->current();
$it1->next();
} else {
$result[] = $it2->current();
$it2->next();
}
}
return $result;
}
这里唯一的技巧是传递正确的$compare
函数和$merge
函数...
以下是一个快速示例:
$compare = function(array $val1, array $val2) {
return strtotime($val1['period']) - strtotime($val2['period']);
};
$merge = function(array $val1, array $val2) {
return array_merge($val1, $val2);
};
合并函数可能需要更改,因为它只是进行粗暴合并,你可能想做更复杂的事情(比如一起添加键等)......
但是这个函数足够通用,可以用来合并任何2个迭代器,而不仅仅是你的用例......
答案 2 :(得分:0)
您可以使用的是AppendIterator
,请参阅http://php.net/manual/en/class.appenditerator.php了解更多文档。
示例:http://php.net/manual/en/function.iterator-to-array.php
$first = new ArrayIterator ( array (
'k1' => 'a',
'k2' => 'b',
'k3' => 'c',
'k4' => 'd'
) );
$second = new ArrayIterator ( array (
'k1' => 'X',
'k2' => 'Y',
'Z'
) );
$combinedIterator = new AppendIterator ();
$combinedIterator->append ( $first );
$combinedIterator->append ( $second );
var_dump ( iterator_to_array ( $combinedIterator, false ) );
由于
:)