过滤和排序具有多个值的多维数组

Posted

技术标签:

【中文标题】过滤和排序具有多个值的多维数组【英文标题】:Filter & sort multidimensional array with multiple values 【发布时间】:2014-02-14 16:23:46 【问题描述】:

我有一个大型多维数组,如下所示:

Array( 
    [1] => Array ( [type] => blah1 [category] => cat1 [exp_range] => this_week ) 
    [2] => Array ( [type] => blah1 [category] => cat2 [exp_range] => next week ) 
    [3] => Array ( [type] => blah1 [category] => cat1 [exp_range] => next week ) 
    [4] => Array ( [type] => blah2 [category] => cat2 [exp_range] => this_week )
)

我希望能够使用多个过滤器过滤此数组。 例如。过滤 where category = cat1 and type = blah1 将返回数组 1 和 3。

我有下面的函数会返回键 1,2,3 这是不正确的,因为数组 2 没有 cat1blah1

任何人都可以看到我需要做什么才能使其正常工作吗?

还可以在这个函数中加入sortin吗,如果可以的话怎么办?

function array_searcher($needles, $array)  
    foreach ($needles as $needle) 
        foreach ($array as $key => $value)  
           foreach ($value as $v)  
            if ($v == $needle)  
                $keys[] = $key; 
             
           
        
    
    return $keys;

【问题讨论】:

这似乎最好建模为单个对象数组。 您可能还想看看这个帖子,接受的答案很简单:) ***.com/questions/2699086/… 【参考方案1】:

我决定重写我的答案以适应过滤和排序。我采用了一种高度面向对象的方法来解决这个问题,我将在下面详细介绍。

您可以在ideone.com live demonstration 看到所有这些代码的运行情况。


我做的第一件事是定义两个接口。

interface Filter 
    public function filter($item);


interface Comparator 
    public function compare($a, $b);

顾名思义,Filter 用于过滤,Comparator 用于比较。

接下来,我定义了三个实现这些接口的具体类,并完成了我想要的。

首先是KeyComparator。此类只是将一个元素的键与另一个元素的键进行比较。

class KeyComparator implements Comparator 
    protected $direction;
    protected $transform;
    protected $key;

    public function __construct($key, $direction = SortDirection::Ascending, $transform = null) 
        $this->key = $key;
        $this->direction = $direction;
        $this->transform = $transform;
    

    public function compare($a, $b) 
        $a = $a[$this->key];
        $b = $b[$this->key];

        if ($this->transform) 
            $a = $this->transform($a);
            $b = $this->transform($b);
        

        return $a === $b ? 0 : (($a > $b ? 1 : -1) * $this->direction);
    

您可以指定排序方向,以及在比较每个元素之前对其进行的转换。我定义了一个帮助类来封装我的 SortDirection 值。

class SortDirection 
    const Ascending = 1;
    const Descending = -1;

接下来,我定义了MultipleKeyComparator,它接受多个KeyComparator 实例,并使用它们来比较两个数组。它们添加到MultipleKeyComparator 的顺序是优先顺序。

class MultipleKeyComparator implements Comparator 
    protected $keys;

    public function __construct($keys) 
        $this->keys = $keys;
    

    public function compare($a, $b) 
        $result = 0;

        foreach ($this->keys as $comparator) 
            if ($comparator instanceof KeyComparator) 
                $result = $comparator->compare($a, $b);

                if ($result !== 0) return $result;
            
        

        return $result;
    

最后,我创建了MultipleKeyValueFilter,它旨在根据键/值对数组过滤数组:

class MultipleKeyValueFilter implements Filter 
    protected $kvPairs;

    public function __construct($kvPairs) 
        $this->kvPairs = $kvPairs;
    

    public function filter($item) 
        $result = true;

        foreach ($this->kvPairs as $key => $value) 
            if ($item[$key] !== $value)
                $result &= false;
        

        return $result;
    


现在,给定输入数组(注意我重新排列它们以使排序更明显):

$array = array (
    '1' => array ('type' => 'blah2', 'category' => 'cat2', 'exp_range' => 'this_week' ),
    '2' => array ('type' => 'blah1', 'category' => 'cat1', 'exp_range' => 'this_week' ),
    '3' => array ('type' => 'blah1', 'category' => 'cat2', 'exp_range' => 'next_week' ),
    '4' => array ('type' => 'blah1', 'category' => 'cat1', 'exp_range' => 'next_week' )
);

排序可以通过以下方式实现:

$comparator = new MultipleKeyComparator(array(
    new KeyComparator('type'),
    new KeyComparator('exp_range')
));

usort($array, array($comparator, 'compare'));

echo "Sorted by multiple fields\n";
print_r($array);

过滤可以通过以下方式实现:

$filter = new MultipleKeyValueFilter(array(
    'type' => 'blah1'
));

echo "Filtered by multiple fields\n";
print_r(array_filter($array, array($filter, 'filter')));

至此,我已经为您提供了大量代码。我建议您的下一步是将这两个部分组合成一个类。然后,这个单一类将同时应用过滤和排序。

【讨论】:

你真的认为这是“荒谬的理由”吗?在这种情况下使用global?还是一般使用global?没有冒犯,但你有声望。超过 5K ,建议使用global. 我从不在 php 中使用 global。我不建议任何人使用它。 "如果您使用的是 在 >=5.3 中添加了goto...这是关于“它不会成为语言的一部分。” @voodoo417 既然你挑战了我,我就重写了整个东西,使其变得超级扩展和面向对象。它可以在 PHP 5+ 中工作,并且不使用全局变量。没有比这更好的了。希望你满意。 ;)【参考方案2】:

这样做:

$arr =  array(
  1 => array ( "type" => "blah1", "category" => "cat1", "exp_range" => "this_week" ),
  2 => array ( "type" => "blah1", "category" => "cat2", "exp_range" => "next week" ),
  3 => array ( "type" => "blah1", "category" => "cat1", "exp_range" => "this_week" ),
  4 => array ( "type" => "blah2", "category" => "cat2","exp_range" => "next week" ),
);

function filter(array $arr,array $params)
  $out = array();
  foreach($arr as $key=>$item)
     $diff = array_diff_assoc($item,$params);

     if (count($diff)==1) // if count diff == 1 - Ok
        $out[$key] = $item;
 
 return $out;


$out = filter($arr,array("type" => "blah1", "category" => "cat1"));

echo '<pre>';
print_r($out);
echo '</pre>';

// output

Array
(
  [1] => Array
     (
        [type] => blah1
        [category] => cat1
        [exp_range] => this_week
    )

[3] => Array
    (
        [type] => blah1
        [category] => cat1
        [exp_range] => this_week
    )

)

【讨论】:

【参考方案3】:

问题在于您的函数将返回包含“cat1”或“blah1”的每个数组的键。你可以用 array_unique() 修复它:

function array_searcher($needles, $array)  
    foreach ($needles as $needle) 
        foreach ($array as $key => $value)  
           foreach ($value as $v)  
            if ($v == $needle)  
                $keys[] = $key; 
             
           
        
    
    return $keys;


$bigarray = array(
array('type' => 'blah1', 'category' => 'cat1', 'exp_range' => 'this_week'),
array('type' => 'blah1', 'category' => 'cat2', 'exp_range' => 'next week'),
array('type' => 'blah1', 'category' => 'cat1', 'exp_range' => 'next week'),
array('type' => 'blah2', 'category' => 'cat2', 'exp_range' => 'this_week')
);

$result = array_searcher(array('cat1','blah1'), $bigarray);
$unique_result = array_unique($result);
print_r($unique_result);

【讨论】:

【参考方案4】:

所以让我们将你的基本数组分配给一个变量:

$array = Array( 
[1] => Array ( [type] => blah1 [category] => cat1 [exp_range] => this_week ) 
[2] => Array ( [type] => blah1 [category] => cat2 [exp_range] => next week ) 
[3] => Array ( [type] => blah1 [category] => cat1 [exp_range] => next week ) 
[4] => Array ( [type] => blah2 [category] => cat2 [exp_range] => this_week )
)

让我们有一个包含过滤器的数组:

$filter = array(
'type' => 'blah1'
'category' => 'cat1'
)

然后我们开始我们的过滤脚本

foreach ($array as $key => $row)
  $i = 0;
  foreach ($filter as $filterKey => $filterValue)
    if ($row[$filterKey] != $filterValue)
      $i++;
    
  if ($i == 0)
    $filteredArray[] = $row;

如果在针对我们的过滤器测试该行之后 $i 仍然等于 0,我们将该行添加到过滤后的数组中

【讨论】:

【参考方案5】:

我依稀记得很多年前实现过这种功能:

http://forums.devnetwork.net/viewtopic.php?t=47855

查看 sortRows() 和 _quickSort() 方法

HTH

【讨论】:

以上是关于过滤和排序具有多个值的多维数组的主要内容,如果未能解决你的问题,请参考以下文章

C#按多列对多维数组进行排序

PHP array_multisort—对多个数组或多维数组进行排序

php计算多维数组某个值的和

如何使用 jquery 创建具有来自动态创建的表单字段的值的多维数组?

Laravel PHP通过多个键值过滤多维数组

array_multisort — 对多个数组或多维数组进行排序