通过包含隐含 AND/OR 逻辑的另一个 2d 规则数组过滤 2d 数组

问题描述 投票:0回答:2

我有数组

$rows = [
    ['k'=>1, 'dop1'=>'take', 'dop2' => 'a', 'dat' => '25-11-2016'],
    ['k'=>2, 'dop1'=>'make', 'dop2' => 'b', 'dat' => '26-11-2016'],
    ['k'=>3, 'dop1'=>'sake', 'dop2' => 'c', 'dat' => '27-11-2016'],
    ['k'=>4, 'dop1'=>'bake', 'dop2' => 'd', 'dat' => '28-11-2016'],
    ['k'=>5, 'dop1'=>'dake', 'dop2' => 'e', 'dat' => '29-11-2016'],
    ['k'=>6, 'dop1'=>'jake', 'dop2' => 'f', 'dat' => '30-11-2016'],
    ['k'=>7, 'dop1'=>'ake', 'dop2' => 'g', 'dat' => '24-11-2016']
];

和另一个用于过滤的数组

$filters = [
    ['dat', '=', '27-11-2016'],
    ['dop1', '=', "bake"],
    ['dop1', '=', "sake"],
    ['dop1', '=', "take"],
];

如果数组过滤器的第一个元素重复,它们之间将是

OR
表达式,否则将是
AND
表达式。 所以这里的结果一定是
['k'=>3, 'dop1'=>'sake', 'dop2' => 'c', 'dat' => '27-11-2016']
,因为
'dat' => '27-11-2016'
存在于过滤器数组中,并且
'dop1'=>'sake''dop1'=>'sake'
也存在。你能帮我吗?

更新 这是我的代码示例,但在评论的地方有问题。

$dop = ['dop1', 'dop2', 'dop3', 'dop4'];
$result = [];

foreach ($rows as $row) {
    foreach ($filters as $filter) {
        if(in_array($filter[0], $dop)){
            $new_filter = [];
            $f = explode("\n", $filter[2]);
            foreach ($f as $item) {
                array_push($new_filter, [$filter[0], $filter[1], $item]);
            }


            if(count($result) == 0){
                foreach ($new_filter as $new) {
                    if($row[$new[0]] == $new[2]){
                        array_push($result, ['r' => $row, 'filter' => $new, 'validity' => 'valid']);
                    }
                }
            } else {
                foreach ($new_filter as $new) {
                    for($i=0;$i<count($result);$i++){
                        if($result[$i]['filter'][0] == $new[0] && $result[$i]['filter'][1] == $new[1] && $result[$i]['filter'][2] != $new[2]){
                            //array_push($result, ['r' => $row, 'filter' => $new, 'validity' => 'valid']);
                        }
                    }
                }
                $invalid = 1;
                $id = 0;
                foreach ($new_filter as $new) {
                    for($i=0;$i<count($result);$i++){
                        if($result[$i]['r'][$new[0]] == $new[2]){
                            $invalid = 0;
                            $id = $i;
                            break 2;
                        }
                    }
                }
                if($invalid == 1){
                    $result[$id]['validity'] = 'invalid';
                }
            }
        } elseif($filter[0] == 'dat') {
            if(count($result) == 0){
                if($row[$filter[0]] == $filter[2]){
                    array_push($result, ['r' => $row, 'filter' => $filter, 'validity' => 'valid']);
                }
            } else {
                if($row[$filter[0]] == $filter[2]){
                    for($i=0;$i<count($result);$i++){
                        if($result[$i]['r'][$filter[0]] != $filter[2]){
                            $result[$i]['validity'] = 'invalid';
                        }
                    }
                }
            }
        }
    }
}
$c = 0;
foreach ($result as $r) {
    if($r['validity'] == 'valid'){
        $c++;
    }
}
echo $c;
php arrays multidimensional-array filtering
2个回答
0
投票

为你做了一些工作,不好的是我的方式需要一个

eval
,这可能很危险,具体取决于你在值中拥有的字符串...

$rows = [
    ['k'=>1, 'dop1'=>'take', 'dop2' => 'a', 'dat' => '25-11-2016'],
    ['k'=>2, 'dop1'=>'make', 'dop2' => 'b', 'dat' => '26-11-2016'],
    ['k'=>3, 'dop1'=>'sake', 'dop2' => 'c', 'dat' => '27-11-2016'],
    ['k'=>4, 'dop1'=>'bake', 'dop2' => 'd', 'dat' => '28-11-2016'],
    ['k'=>5, 'dop1'=>'dake', 'dop2' => 'e', 'dat' => '29-11-2016'],
    ['k'=>6, 'dop1'=>'jake', 'dop2' => 'f', 'dat' => '30-11-2016'],
    ['k'=>7, 'dop1'=>'ake', 'dop2' => 'g', 'dat' => '24-11-2016']
];

$filters = [
    ['dat', '=', '27-11-2016'],
    ['dop1', '=', "bake"],
    ['dop1', '=', "sake"],
    ['dop1', '=', "take"],
];


function marrfilter($filters_arr, $rows)
{
    /*
     * Building filters to eval
     */
    $buildFilter = function($key, $op, $val)
    {
        switch($op)
        {
            case '=':
                $op = '==';
                break;
            case '==':
            case '===':
            case '!=':
            case '!==':
            case '>':
            case '>=':
            case '<':
            case '<=':
                break;

            default:
                throw new Exception('Unknown operand');
        }

        $val = addslashes($val);

        return '$val ' . $op . " '" . $val . "'";
    };

    $filters = array();
    foreach($filters_arr as $k => $v)
    {
        list($key, $op, $val) = $v;

        if( !isset($filters[$key]) )
        {
            $filters[$key] = $buildFilter($key, $op, $val);
        }
        else
        {
            if( !is_array($filters[$key]) )
            {
                $filters[$key] = array($filters[$key]);
            }

            $filters[$key][] = $buildFilter($key, $op, $val);
        }
    }

    /*
     * Filter function
     */
    $applyFilter = function($row, $fk, $fv)
    {
        if( !array_key_exists($fk, $row) )
        {
            return false;
        }

        $val = $row[$fk];

        if( is_array($fv) )
        {
            $test = false;
            foreach($fv as $ft)
            {
                eval('$test = (' . $ft . ');');

                if( $test ) return true;
            }

            return false;
        }
        else
        {
            $test = false;
            // thats why you realy have to care about what you put in filters and data :)
            eval('$test = (' . $fv . ');');

            if( $test ) return true;
        }

        return false;
    };

    /*
     * Filtering
     */
    $result = array();
    foreach($rows as $row)
    {
        $r = array();
        foreach($filters as $fk => $fv)
        {
            $r[] = $applyFilter($row, $fk, $fv);
        }

        $r = array_filter($r);

        if( count($r) == count($filters) )
        {
            $result[] = $row;
        }
    }

    return $result;
}

在这里工作得很好。

在线查看:http://sandbox.onlinephpfunctions.com/code/185658700006fe913e6592a7fc51d4cce0db6a68

首先,我正在构建一个可以使用的过滤器数组,然后将其应用到所有行。就这么简单:p

希望有帮助:)


编辑:

重建了功能的一些糟糕部分,删除了

eval
以防止严重的安全风险。

  • buildFilter
    现在返回一个函数,该函数返回一个简单的测试,具体取决于 您设置的操作(您甚至可以定义自定义测试呵呵)
  • ApplyFilter
    结果已修改:)

这是新代码

$rows = [
    ['k'=>1, 'dop1'=>'take', 'dop2' => 'a', 'dat' => '25-11-2016'],
    ['k'=>2, 'dop1'=>'make', 'dop2' => 'b', 'dat' => '26-11-2016'],
    ['k'=>3, 'dop1'=>'sake', 'dop2' => 'c', 'dat' => '27-11-2016'],
    ['k'=>4, 'dop1'=>'bake', 'dop2' => 'd', 'dat' => '28-11-2016'],
    ['k'=>5, 'dop1'=>'dake', 'dop2' => 'e', 'dat' => '29-11-2016'],
    ['k'=>6, 'dop1'=>'jake', 'dop2' => 'f', 'dat' => '30-11-2016'],
    ['k'=>7, 'dop1'=>'ake', 'dop2' => 'g', 'dat' => '24-11-2016']
];

$filters = [
    ['dat', '=', '27-11-2016'],
    ['dop1', '=', "bake"],
    ['dop1', '=', "sake"],
    ['dop1', '=', "take"],
];


function marrfilter($filters_arr, $rows)
{
    /*
     * Building filters to eval
     */
    $buildFilter = function($op, $val)
    {
        return function($data) use ($val, $op)
        {
            switch($op)
            {
                case '=':
                case '==':
                    return ($data == $val);
                case '===':
                    return ($data === $val);

                case '!=':
                    return ($data != $val);
                case '!==':
                    return ($data !== $val);

                case '>':
                    return ($data > $val);
                case '>=':
                    return ($data >= $val);

                case '<':
                    return ($data < $val);
                case '<=':
                    return ($data <= $val);

                default:
                    throw new Exception('Unknown operand ' . $op);
            }
        };
    };

    $filters = array();
    foreach($filters_arr as $k => $v)
    {
        list($key, $op, $val) = $v;

        if( !isset($filters[$key]) )
        {
            $filters[$key] = $buildFilter($op, $val);
        }
        else
        {
            if( !is_array($filters[$key]) )
            {
                $filters[$key] = array($filters[$key]);
            }

            $filters[$key][] = $buildFilter($op, $val);
        }
    }

    /*
     * Filter function
     */
    $applyFilter = function($row, $fk, $fv)
    {
        // Key not found in row
        if( !array_key_exists($fk, $row) )
        {
            return false;
        }

        // Get row[key] value
        $val = $row[$fk];

        // if we have multiple tests for the filter it's an OR
        if( is_array($fv) )
        {
            foreach($fv as $ft)
            {
                if( $ft($val) === true ) return true; // return true if filter passed

                // or continue if not
            }
        }
        else
        {
            if( $fv($val) === true ) return true; // return true if filter passed
        }

        // if no filter passed, return false
        return false;
    };

    /*
     * Filtering
     */
    $result = array();
    foreach($rows as $row)
    {
        $r = array();
        foreach($filters as $fk => $fv)
        {
            $r[] = $applyFilter($row, $fk, $fv);
        }

        $r = array_filter($r);

        if( count($r) == count($filters) )
        {
            $result[] = $row;
        }
    }

    return $result;
}

0
投票

所以不用帮你写代码,直接看一下PHP的array_filter()

您需要的伪代码如下所示:

var_dump(array_filter($rows, function($row) {
  global $filtered;

  $match = 0;

  foreach ($filtered as $index=>$filter) {
    // run code here to determine if this is an OR or AND and apply matching to the $row
    // $row will be like ['k'=>1, 'dop1'=>'take', 'dop2' => 'a', 'dat' => '25-11-2016'],
    // match based on the index of your $filtered ($index===0 AND, otherwise OR)
  }

  return $match;
});
© www.soinside.com 2019 - 2024. All rights reserved.