note 104221 deleted from function.sort by cmb
| From: | cmb@php.net | Date: | Sun, 27 Oct 2019 08:39:42 +0000 |
| Subject: | note 104221 deleted from function.sort by cmb | ||
| References: | 1 | Groups: | php.notes |
| Request: | Send a blank email to php-notes+get-212797@lists.php.net to get a copy of this message | ||
Note Submitter: markrose at markrose dot ca
----
I've yet to come across a fast way to merge sorted arrays by a user value. Merging the arrays
and using PHP's usort isn't efficient, as it doesn't take advantage of the existing
sorting. So I wrote my own merge sorted arrays by user value function. The limit parameter can be
used be used to limit the number of results (there is no point sorting more results than needed).
This sort is stable: values will be taken preferentially from the first, then second, etc., array
inside $merge_arrays.
Note that this function will fail if a user value is boolean false. It could be adapted to hold the
best value inside an array at some performance penalty. It could also be adapted to comparing
strings case-insensitively.
To give you some idea of the performance, my machine will sort out the first 1000 values of 10
sorted arrays of 1000 values each in under 40 ms, using an integer sort field. Not fast by C
standards, but a vast improvement over usort'ing 10,000 values.
<?php
function merge_sorted_arrays_by_field ($merge_arrays, $sort_field, $sort_desc = false, $limit = 0)
{
$array_count = count($merge_arrays);
// fast special cases...
switch ($array_count)
{
case 0: return array();
case 1: return $limit ? array_slice(reset($merge_arrays), 0, $limit) : reset($merge_arrays);
}
if ($limit === 0)
$limit = PHP_INT_MAX;
// rekey merge_arrays array 0->N
$merge_arrays = array_values($merge_arrays);
$best_array = false;
$best_value = false;
$results = array();
// move sort order logic outside the inner loop to speed things up
if ($sort_desc)
{
for ($i = 0; $i < $limit; ++$i)
{
for ($j = 0; $j < $array_count; ++$j)
{
// if the array $merge_arrays[$j] is empty, skip to next
if (false === ($current_value = current($merge_arrays[$j])))
continue;
// if we don't have a value for this round, or if the current value is bigger...
if ($best_value === false || $current_value[$sort_field] > $best_value[$sort_field])
{
$best_array = $j;
$best_value = $current_value;
}
}
// all arrays empty?
if ($best_value === false)
break;
$results[] = $best_value;
$best_value = false;
next($merge_arrays[$best_array]);
}
}
else
{
for ($i = 0; $i < $limit; ++$i)
{
for ($j = 0; $j < $array_count; ++$j)
{
if (false === ($current_value = current($merge_arrays[$j])))
continue;
// if we don't have a value for this round, or if the current value is smaller...
if ($best_value === false || $current_value[$sort_field] < $best_value[$sort_field])
{
$best_array = $j;
$best_value = $current_value;
}
}
// all arrays empty?
if ($best_value === false)
break;
$results[] = $best_value;
$best_value = false;
next($merge_arrays[$best_array]);
}
}
return $results;
}
?>