您想要的本质上是将多个索引放入单个数组中,就像您在关系数据库中的单个表上有多个索引一样。(排序是一个单独但相关的问题。)
让我们从这个基本的数据结构开始,一组简单的数组,对键没有特别的重要性:
$data = array(
array('display_order'=> 0, 'product_id'=>'samgal3', 'display_name' => 'Samsung Galaxy 3'),
array('display_order'=> 1, 'product_id'=>'motorazrh', 'display_name' => 'Motorola Razr HD'),
array('display_order'=> 2, 'product_id'=>'a', 'display_name' => 'a'),
array('display_order'=> 3, 'product_id'=>'c', 'display_name' => 'c'),
array('display_order'=> 4, 'product_id'=>'d', 'display_name' => 'd'),
array('display_order'=> 5, 'product_id'=>'b', 'display_name' => 'b'),
array('display_order'=> 6, 'product_id'=>'q', 'display_name' => 'q'),
array('display_order'=> 7, 'product_id'=>'f', 'display_name' => 'f'),
);
您可以轻松创建显式索引,然后使用它来获取$data
项目:
$product_id_idx = array_flip(array_map(function($item){return $item['product_id'];}, $data));
$samgal3_array = $data[$product_id_idx['samgal3']]; // same as $data[0]
对于排序,您可以使用 oft-forgotten array_multisort
。查看文档中的示例 3。诀窍是创建数组以进行排序,并将完整的数据集作为最后一个参数。例如:
array_multisort(array_keys($product_id_idx), SORT_ASC, SORT_STRING, $data);
$data
现在按产品密钥排序。然而,原来的数字数组键$data
丢失了,这意味着我们$product_id_idx
的不再可用。因此,如果您想继续使用索引,最好对数据数组的副本进行排序。
我们可以将这两种方法组合到一个类中以保持我们的理智:
class MultiIndex {
protected $array;
protected $indexes = array();
protected $indexdefs = array();
function __construct($array, $indexdefs)
{
$this->array = $array;
$this->indexdefs = $indexdefs;
foreach ($indexdefs as $name => $column) {
$this->indexes[$name] = $this->makeIndex($column);
}
}
function makeIndex($column)
{
$index = array();
foreach ($this->array as $k => $v) {
$index[$v[$column]] = $k;
}
return $index;
}
function get($key, $index=null)
{
$datapk = ($index===null) ? $key : $this->indexes[$index][$key];
return $this->array[$datapk];
}
function getIndex($index)
{
return $this->indexes[$index];
}
function getData()
{
return $this->array;
}
function indexedBy($index)
{
$indexed = array();
$indexedcolumn = $this->indexdef[$index];
foreach ($this->indexes[$index] as $indexk => $arrayk) {
$newarray = $this->array[$arrayk];
unset($newarray[$indexedcolumn]);
$indexed[$indexk] = $newarray;
}
return $indexed;
}
function sortedBy(/*multisort args*/)
/* with strings converted to arrays corresponding to index of same name */
{
$args = func_get_args();
foreach ($args as $n => $arg) {
if (is_string($arg)) {
$args[$n] = array_keys($this->indexes[$arg]);
}
}
$sorted = $this->array;
$args[] = $sorted;
call_user_func_array('array_multisort', $args);
return $sorted;
}
}
使用示例:
$dataidx = new MultiIndex($data, array('id'=>'product_id', 'disp'=>'display_order'));
var_export($dataidx->sortedBy('disp', SORT_STRING, SORT_ASC));
var_export($dataidx->indexedBy('id'));
var_export($dataidx->get('samgal3', 'id'));
这应该是一个非常基本的基础,对于小型阵列应该没问题。为简单起见MultiIndex
,数据是不可变的,键始终是数组索引。一些明显的增强方法是:
- 使
$indexdefs
accept 成为一个可调用的,它返回一个项目的键,而不仅仅是一个字符串/int 命名一个数组键。这允许您在任何形状的数据上创建索引,甚至可以创建与数据不直接对应的索引。(例如,按显示名称中的字符数索引,或按保持日期和时间分开的数组的日期+时间等进行索引)
- 删除索引键只有一个值的要求。(现在假设您创建的所有索引都是唯一的。)
- 允许您为索引声明
SORT_*
数据类型并将其自动包含在 in 的参数array_multisort
中MultiIndex::sortedBy()
。
- 包括稀疏索引:如果您有非常常见或非常稀有的值,或者只是有一个非常大的数据集并且您想节省内存,您可以将其设置为仅对某些值进行索引。如果在索引中未找到某个项目,则回退到对数据中未编制索引的项目进行全面扫描。
- 添加合理的接口实现。
- 允许
MultiIndex
有多个后端,因此您可以在任何类似数组的结构(例如,dbm 键值存储、像 DynamoDB、memcached 等云存储)上拥有多个索引,并使用相同的对象接口操作它们。
MultiIndex
保持可变数据并随着数据的变化自动增量更新索引。
我将把这段代码放在一个要点上,以便于分叉。