この両方のイテレータは常にソートされている、あなたは1つが等しくない場合は(最初に来る各反復ごとに比較し、それらの両方をキャッシュすることができ)、それを処理します。等しい場合、両方を等しく処理します。
等しくない:
$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
と[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
にこの処理をラップすることができます(一回の反復は省略しました)。与えられた情報が限られていたため、問題のドメインの日付を減らす簡単な例を作成しました。一度に2つのイテレータを反復処理します。両方のイテレータが等しい場合は、両方を返します。等しくない場合は、両方が比較される場合に最初になるものを返します。使用
簡略化されたデータ:コンペア値を含む
$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');
ちょうど2のアレイ。それらは2つのイテレータに変わります。
$it1 = new ArrayIterator($ar1);
$it2 = new ArrayIterator($ar2);
書き出される問題は2つのイテレータに制限されています。この問題をより一般的に解決するには、0以上のイテレータで動作する必要があります。つまり、各反復ごとに、イテレータは現在の値に基づいて相互に比較されます。そのために比較関数が使用されます。関数はAとBを比較し、2に基づいて、それは整数値を返します:あなたはどのようにusort
Docs作品であること比較することができ
- < B:-1(AがBよりも小さい、戻り値は以下でありますゼロ)
- A = B 0(AはBに等しく、戻り値がゼロに等しい)
- A> B:1(AがBより大きい場合、戻り値がゼロよりも大きい)
これは可能無限の数の対を互いに比較することができる。 2つの関数しか必要ありません:使用するイテレータから現在の値を取得する関数と、AとBの実際の比較を行う関数です(実際には両方を1つの関数にマージできますが、イテレーターは少し違うので、後でもっと簡単に変更できるように、分ける価値があると思いました)。だから、最初のイテレータのうちの値を取得する機能、私はシンプルなstrcmp
でこれを行うことができますので、私はISO日付時刻値との比較:
/**
* 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;
};
注:私は知りませんあなたが使用している日付形式、おそらく私は月ごとに1日を混ぜて、変数を交換するだけで、かなり自己記述的です。
このすべての機能は、イテレータと簡単に比較できる値を取得することです。これはまだ、上記の比較を行いませんので、別の関数が依存性として、この比較値関数を使用しますが必要とされる:
/**
* 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
文字列比較関数に基づいて、今の機能を比較し、使用します$compareValue
関数は比較のために文字列を取得します。
これで、2つのイテレーターを持つ配列があるとしましょう。これでソートすることができます。最初の要素と次の要素を比較して、それらが等しいかどうかを判断することもできます。
これで、複数のイテレーターで構成されたイテレーターを作成することができました。各イテレーターはソートされ、最初のイテレーター(およびそれに相当するイテレーター)のみが現在として返され、転送されます。この流れのような何か:
Src
ソートがすでに比較機能で行われているとして、これだけ反復ロジックをカプセル化する必要があります。任意のサイズ(0以上の要素)の配列のソートが機能するので、すでに一般化されています。使用例:
/**
* 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);
}
}
この使用例は、その反復とそれに関連する値をその反復に出力します。この場合、例の配列としての時間情報だけがこれらから構成されます。また、角かっこの中にあるイテレータは(最初は0、2番目は1)です。これは以下の出力を生成します。あなたが見ることができるように、両方の(事前ソート)イテレータに等しいもの比較値について、ペアとして返される
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
の使い方です。実装はかなり単純ですが、これはソート/現在のイテレータをキャッシュしません。これを改善したい場合は、これを練習問題として残します。
フルコード:
<?php
/**
* @link http://stackoverflow.com/q/10024953/367456
* @author hakre <http://hakre.wordpress.com/>
*/
$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');
$it1 = new ArrayIterator($ar1);
$it2 = new ArrayIterator($ar2);
/**
* 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));
};
/**
* Iterator with a comparison based merge-append strategy over 0 or more iterators.
*
* Compares 0 or more iterators with each other. Returns the one that comes first
* and any additional one that is equal to the first as an array of their current()
* values in this current().
* next() forwards all iterators that are part of current().
*/
class MergeCompareIterator implements Iterator
{
/**
* @var Iterator[]
*/
private $iterators;
/**
* @var callback
*/
private $compareFunction;
/**
* @var int
*/
private $index;
/**
* @param callback $compareFunction (same sort of usort()/uasort() callback)
* @param Iterator[] $iterators
*/
public function __construct($compareFunction, array $iterators = array())
{
$this->setCompareFunction($compareFunction);
foreach ($iterators as $iterator) {
$this->appendIterator($iterator);
}
}
/**
* @param callback $compareFunction
*/
public function setCompareFunction($compareFunction)
{
if (!is_callable($compareFunction)) {
throw new InvalidArgumentException('Compare function is not callable.');
}
$this->compareFunction = $compareFunction;
}
public function appendIterator(Iterator $it)
{
$this->iterators[] = $it;
}
public function rewind()
{
foreach ($this->iterators as $it) {
$it->rewind();
}
$this->index = 0;
}
/**
* @return Array one or more current values
* @throws RuntimeException
*/
public function current()
{
$current = array();
foreach ($this->getCurrentIterators() as $key => $value) {
$current[$key] = $value->current();
}
return $current;
}
/**
* @return Iterator[]
*/
private function getCurrentIterators()
{
/* @var $compareFunction Callable */
$compareFunction = $this->compareFunction;
$iterators = $this->getValidIterators();
$r = uasort($iterators, $compareFunction);
if (FALSE === $r) {
throw new RuntimeException('Sorting failed.');
}
$compareAgainst = reset($iterators);
$sameIterators = array();
foreach ($iterators as $key => $iterator) {
$comparison = $compareFunction($iterator, $compareAgainst);
if (0 !== $comparison) {
break;
}
$sameIterators[$key] = $iterator;
}
ksort($sameIterators);
return $sameIterators;
}
/**
* @return Iterator[]
*/
private function getValidIterators()
{
$validIterators = array();
foreach ($this->iterators as $key => $iterator) {
$iterator->valid() && $validIterators[$key] = $iterator;
}
return $validIterators;
}
/**
* @return int zero based iteration count
*/
public function key()
{
return $this->index;
}
public function next()
{
foreach ($this->getCurrentIterators() as $iterator) {
$iterator->next();
}
$this->index++;
}
public function valid()
{
return (bool)count($this->getValidIterators());
}
}
/**
* 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);
}
}
希望はこれが便利です。それはあなたの "内側の"イテレータ内のプリコーディングされたデータでのみ機能します。そうしないと、現在の要素の比較によるマージ/アペンド戦略が意味をなさない。
これらは常にソートされていますか? – hakre
あなたはPHPであなたの正確な配列を追加することができます..入力を開始したくない.. – Baba