Talgat Baltasov Talgat Baltasov - 4 days ago 6
PHP Question

Filter multidimensional array by another array

I have array

$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']
];


and another array for filtering

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


if the first element of array filters are repeated it will be
OR
expression between them, else will be
AND
expression.
so here the result must be
['k'=>3, 'dop1'=>'sake', 'dop2' => 'c', 'dat' => '27-11-2016']
because
'dat' => '27-11-2016'
exists in filters array and
'dop1'=>'sake''dop1'=>'sake'
also exists. Can you help me, please?

UPDATE
Here is my example of code, but have problem in place where commented.

$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;

Answer

Done some work for you, the bad thing is my way needs an eval which can be dangerous, depending on the string you will have in values...

$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;
}

Works fine here.

See online : http://sandbox.onlinephpfunctions.com/code/185658700006fe913e6592a7fc51d4cce0db6a68

First of all I'm building an array of filters that I can use, then I'm applying it to all rows. As simple as that :p

Hope it helped :)

Comments