Last active
September 6, 2019 20:05
-
-
Save fulldecent/a4cac03087cadec4c275a54317a6f623 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
# | |
# Input multiple SELECT statements from different SQL queries | |
# get back a single stream of rows with records merged if | |
# they have the same key | |
# | |
function genOneTwoThree() { | |
foreach ([1=>'one',2=>'two',3=>'three'] as $key => $value) { | |
# echo "...ready to yield: $key => $number\n"; | |
yield $key => $value; | |
} | |
} | |
function genTwoThreeFour() { | |
foreach ([1=>'uno',2=>'does',4=>'quatro'] as $key => $value) { | |
# echo "...ready to yield: $key => $number\n"; | |
yield $key => $value; | |
} | |
} | |
$emptyIterator = new EmptyIterator(); | |
/** | |
* mergeGenerators Combines multiple generators and has unique keys | |
* I use it to merge sort queries from multiple databases and | |
* combine records that have duplicate keys across them | |
* | |
* @access public | |
* @param array $generators Yields key => some object and keys sorted | |
* @param callable $merger Takes arroy of 2 or more objects and returns one | |
* @return void | |
*/ | |
function mergeGenerators(array $generators, callable $merger) { | |
while (count($generators) > 0) { | |
// Find minimum input key | |
$minimumKey = null; | |
foreach ($generators as $i => $generator) { | |
if (!$generator->valid()) { | |
unset($generators[$i]); | |
continue; | |
} | |
if (is_null($minimumKey) || $generator->key() < $minimumKey) { | |
$minimumKey = $generator->key(); | |
} | |
} | |
if (count($generators) == 0) { | |
return; | |
} | |
// Merge all minimum values | |
$outputItems = array(); | |
foreach ($generators as $generator) { | |
while ($generator->key() == $minimumKey) { | |
$outputItems[] = $generator->current(); | |
$generator->next(); | |
} | |
} | |
yield $minimumKey => count($outputItems) > 1 ? $merger($outputItems) : $outputItems[0]; | |
} | |
} | |
function combiner($items) { | |
return max($items); | |
} | |
$a = mergeGenerators([genOneTwoThree(), $emptyIterator, genTwoThreeFour()], 'combiner'); | |
echo "\n\nYIELDED:\n"; | |
foreach ($a as $key => $value) { | |
var_dump($key, $value); | |
} |
Regarding ^^ https://gist.github.com/fulldecent/a4cac03087cadec4c275a54317a6f623#gistcomment-1949315, the latest version now includes this approach
Cleaner function
/**
* Combines multiple generators and has unique keys
* I use it to merge sort queries from multiple databases and
* combine records that have duplicate keys across them
*
* http://stackoverflow.com/q/37973732/300224
*
* @access public
* @param array $generators Yields key => some object and keys sorted by keyComparitor
* @param callable $keyComparitor Puts keys in order, returns <0, 0, >0
* @param callable $merger Takes arroy of 2 or more objects and returns one
* @return void
*/
static function mergeGenerators(array $generators, callable $keyComparitor, callable $merger)
{
while (count($generators) > 0) {
$generators = array_filter($generators, function(Iterator $g){return $g->valid();});
if (empty($generators)) {
return;
}
usort($generators, function(Iterator $a, Iterator $b) use ($keyComparitor) {
return $keyComparitor($a->key(), $b->key());
});
$minimumKey = $generators[0]->key();
$outputItems = [];
foreach ($generators as $generator) {
if ($keyComparitor($generator->key(), $minimumKey) !== 0) break; // Sorted, later ones must also not be same
while ($keyComparitor($generator->key(), $minimumKey) === 0) {
$outputItems[] = $generator->current();
$generator->next();
}
}
yield $minimumKey => count($outputItems) > 1 ? $merger($outputItems) : $outputItems[0];
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Something like this also works