必要なのは、リレーショナル データベースの 1 つのテーブルに複数のインデックスを配置するのと同じように、基本的に複数のインデックスを 1 つの配列に配置することです。(並べ替えは別ですが、関連する問題です。)
この基本的なデータ構造、つまりキーに特に重要性がない単純な配列のセットから始めましょう。
$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]
並べ替えには、よく忘れられる を使用できます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
配列キーに名前を付けるだけの文字列/int ではなく、項目のキーを返す callable を accept にします。これにより、任意の形状のデータにインデックスを作成したり、データに直接対応しないインデックスを作成したりすることができます。(たとえば、表示名の文字数によるインデックス、または日付と時刻を別々に保持する配列の日付と時刻によるインデックスなど)
- インデックス キーが 1 つの値のみを持つという要件を削除します。(現在、作成するすべてのインデックスは一意であると想定されています。)
- インデックスのデータ型を宣言し、in
SORT_*
への引数に自動的に含めることができます。array_multisort
MultiIndex::sortedBy()
- まばらなインデックスを含める: 非常に一般的または非常にまれな値がある場合、または非常に大きなデータセットがあり、メモリを節約したい場合は、特定の値のみがインデックス付けされるようにすることができます。アイテムがインデックスに見つからない場合は、データ内のインデックスが作成されていないアイテムのフル スキャンにフォールバックします。
- 適切なインターフェースの実装を追加します。
- に複数のバックエンド
MultiIndex
を持たせることができるため、配列のような構造 (dbm キー値ストア、DynamoDB のようなクラウド ストア、memcached など) に複数のインデックスを持ち、それらすべてを同じオブジェクト インターフェイスで操作できます。
- 変更可能なデータを
MultiIndex
保持し、データの変更に応じて増分的かつ自動的にインデックスを更新します。
簡単にフォークできるように、このコードを Gist に保持します。