[英]PHP: sorting/searching data that has 2 keys (or an array of arrays by a sub-array key)?
我已經打過幾次了,但是從來沒有找到解決它的最佳方法。 用一個具體的例子來說明是最容易的。 樣本數據:
product_id display_name display_order
---------- ------------ -------------
"samgal3" "Samsung Galaxy 3" 0
"motorazrh" "Motorola Razr HD" 1
"iphone5" "Apple iphone 5" 2
etc
實際的數組通常很小(<20個項),盡管並非總是如此,並且保證了唯一的鍵/值。 每個項目都有一個唯一的排序鍵(用於html表/枚舉的順序在其中列出),一個唯一的內部鍵(用於項目查找)和易於理解的顯示名稱。
通常,在表單上使用選項列表時會遇到此問題。 相同的數據既用於填充表單上的下拉框,也用於驗證提交的$ GET / POST數據。 生成表單時,需要按“排序”順序進行枚舉/列出,以按順序創建SELECT框選項。 提交表單后,需要使用“ product_id”進行搜索(以驗證“ ...&action = view&product_id = elephant ...”是否為列表中的產品)。
如果我使用'sort'=> array(other data)作為鍵,則按'sort'進行顯示很容易,但是在$ data [ * ] ['product_id']中搜索是很難的*(即,確定$ KEY是否存在,具有$ data [$ KEY] ['product_id'] =='htcvox')*。 如果我使用'product_id'=> array(other data)作為鍵,則搜索'samgal3'是否在數組中並查找其數據很容易,但是沒有簡單的方法通過'sort'遍歷/枚舉數組來創建形成。
我想我可以做一個自定義搜索/排序,其中$ data中任何成員$ i的搜索/排序鍵都是$ i ['product_id']或$ i ['sort'],但這很笨拙,我從來沒有做過之前。 簡單性很重要,因為代碼將是開源的。
我期望將數據編碼為數組數組,如下所示:
$data = array(
0 => array('product_id'=>'samgal3', 'display_name' => 'Samsung Galaxy 3'),
1 => array('product_id'=>'motorazrh', 'display_name' => 'Motorola Razr HD'),
...
要么
$data = array(
'samgal3' => array('sort'=>0, 'display_name' => 'Samsung Galaxy 3'),
'motorazrh' => array('sort'=>1, 'display_name' => 'Motorola Razr HD'),
...
給定二維數組,以不同的方式提出相同的問題: $ data = array(array1,array2,array3,....); 其中所有array1,array2,array3等都包含具有固定名稱的鍵/字段,是否有一種簡單的方法來搜索/排序$ ARRAY [ * *] ['named_field']上的嵌套數組?
使用http://www.php.net/usort生成自定義的用戶定義排序。
例:
<?php
//added a few more values
$data = array(
0 => array('product_id'=>'samgal3', 'display_name' => 'Samsung Galaxy 3'),
1 => array('product_id'=>'motorazrh', 'display_name' => 'Motorola Razr HD'),
2 => array('product_id'=>'a', 'display_name' => 'a'),
3 => array('product_id'=>'c', 'display_name' => 'c'),
4 => array('product_id'=>'d', 'display_name' => 'd'),
5 => array('product_id'=>'b', 'display_name' => 'b'),
6 => array('product_id'=>'q', 'display_name' => 'q'),
7 => array('product_id'=>'f', 'display_name' => 'f'),
);
function cmp($a,$b){
return strcasecmp($a['display_name'],$b['display_name']);
}
usort($data,'cmp');
var_export($data);
實際上,您想要的是在單個數組中有多個索引,就像在關系數據庫中的單個表上有多個索引一樣。 (排序是一個單獨的但相關的問題。)
讓我們從基本數據結構開始,這是一組簡單的數組,對鍵沒有特別的重視:
$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
接受一個callable,它返回一個項的鍵,而不僅僅是命名數組鍵的字符串/ int。 這使您可以在任何形狀的數據上創建索引,甚至可以創建不直接與數據對應的索引。 (例如,按顯示名稱中的字符數或按將日期和時間分開的數組的日期和時間進行索引等) SORT_*
數據類型,並將其自動包含在MultiIndex::sortedBy()
array_multisort
的參數中。 MultiIndex
具有多個后端,因此您可以在任何類似數組的結構(例如dbm鍵值存儲,DynamoDB這樣的雲存儲,memcached等)上具有多個索引,並使用相同的對象接口對它們進行操作。 MultiIndex
保存可變數據,並隨着數據的變化而自動遞增和自動更新索引。 為了方便分叉,我會保留此代碼的要點 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.