1

我已经打了几次,但从未找到解决它的最佳方法。用一个具体的例子来说明是最容易的。样本数据:

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'] 上搜索/排序嵌套数组?

4

2 回答 2

0

使用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);

http://codepad.viper-7.com/3mY8nU

于 2013-04-03T23:36:56.827 回答
0

您想要的本质上是将多个索引放入单个数组中,就像您在关系数据库中的单个表上有多个索引一样。(排序是一个单独但相关的问题。)

让我们从这个基本的数据结构开始,一组简单的数组,对键没有特别的重要性:

$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,数据是不可变的,键始终是数组索引。一些明显的增强方法是:

  1. 使$indexdefsaccept 成为一个可调用的,它返回一个项目的键,而不仅仅是一个字符串/int 命名一个数组键。这允许您在任何形状的数据上创建索引,甚至可以创建与数据不直接对应的索引。(例如,按显示名称中的字符数索引,或按保持日期和时间分开的数组的日期+时间等进行索引)
  2. 删除索引键只有一个值的要求。(现在假设您创建的所有索引都是唯一的。)
  3. 允许您为索引声明SORT_*数据类型并将其自动包含在 in 的参数array_multisortMultiIndex::sortedBy()
  4. 包括稀疏索引:如果您有非常常见或非常稀有的值,或者只是有一个非常大的数据集并且您想节省内存,您可以将其设置为仅对某些值进行索引。如果在索引中未找到某个项目,则回退到对数据中未编制索引的项目进行全面扫描。
  5. 添加合理的接口实现
  6. 允许MultiIndex有多个后端,因此您可以在任何类似数组的结构(例如,dbm 键值存储、像 DynamoDB、memcached 等云存储)上拥有多个索引,并使用相同的对象接口操作它们。
  7. MultiIndex保持可变数据并随着数据的变化自动增量更新索引。

我将把这段代码放在一个要点上,以便于分叉。

于 2013-04-04T01:37:57.480 回答