2

I have a multi dimensional array where I want to retrieve the key of each element that matches '116234':

$test = Array
(
    [8708] => Array
    (
        [ref_id] => 93939
    )

    [8709] => Array
    (
        [ref_id] => 116234
    )

    [8710] => Array
    (
        [ref_id] => 116234
    )
)

In that case the desired keys are: 8709, 8710.

Somehow the filter function does not work:

$data['attr'][8] = '116234';
$filtered = array_filter($test, function($v) { return $v['ref_id'] == $data['attr'][8]; });
print_r($filtered);

response: 
Array
(
)

According to an answer within another question at SO this should work.

Is this somehow wrong, or does the filter function rely on a certain PHP version?

merlin
  • 2,717
  • 3
  • 29
  • 59

3 Answers3

1

The code you provided seems to be working, however, if you just want the keys of the associative array as the output you can use array_keys to get the keys of your filtered result.

$test = Array(
    8708 => Array('ref_id' => 93939),
    8709 => Array('ref_id' => 116234),
    8710 => Array('ref_id' => 116234)
);
$filtered = array_filter($test, function($v) { return $v['ref_id'] == '116234'; });
print_r(array_keys($filtered)); // array_keys to get the keys from filtered

Output:

Array ( [0] => 8709 [1] => 8710 )

EDIT: The issue that you're having with your updated question is that $test cannot be seen within filter's anonymous function, and so you need to use the use keyword to give access to it. Read more about use here.

Working code:

$test = Array(
    8708 => Array('ref_id' => 93939),
    8709 => Array('ref_id' => 116234),
    8710 => Array('ref_id' => 116234)
);

$data['attr'][8] = '116234';

$filtered = array_filter($test, function($v) use (&$data) { 
    return $v['ref_id'] == $data['attr'][8]; 
});

print_r(array_keys($filtered));

Output:

Array ( [0] => 8709 [1] => 8710 )
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
1

Your code doesn't work because $data is not in scope in your anonymous function. If you had PHP error reporting enabled you would have seen 3 error messages like this:

Notice: Undefined variable: data in /in/RNpEd on line nnn

You need to rewrite your code like this (note the use ($data) in the definition of the anonymous function):

$test = array('8708' => array('ref_id' => '93939'), '8709' => array('ref_id' => '116234'), '8710' => array('ref_id' => '116234'));
$data['attr'][8] = '116234';
$filtered = array_filter($test, function($v) use($data) { return $v['ref_id'] == $data['attr'][8]; });
print_r($filtered);

Output:

Array (
    [8709] => Array (
        [ref_id] => 116234
    )
    [8710] => Array (
        [ref_id] => 116234
    )
)

Demo on 3v4l.org

Nick
  • 138,499
  • 22
  • 57
  • 95
  • That seems to be the issue. Thank you. Is there any advantage of using "use" instead of declaring $datas as global within the function? – merlin Dec 22 '18 at 10:34
  • @merlin see this [question](https://stackoverflow.com/questions/39741443/php-difference-between-use-or-global-to-access-a-global-variable-in-a-cl) for a discussion of the difference. In this type of case (where the function is applied at the same time as it is defined) it makes no difference. I prefer `use` as it makes for cleaner code. – Nick Dec 22 '18 at 10:36
  • Thank you. That makes it very clear. I did accept the answer from Nick Parsons as he answered almost simultaniasly and provides the additional info on array keys. Hope that's OK for you. – merlin Dec 22 '18 at 10:42
  • @merlin no problem. You should accept the answer that was most useful to you. – Nick Dec 22 '18 at 13:05
0

I would rather filter it anther way such as this:

$a = array (
    8708 => 
    array (
        'ref_id' => 93939
    ),
    8709 => 
    array (
        'ref_id' => 116234
    ),
    8710 => 
    array (
        'ref_id' => 116234
    )
);

$data['attr'][8] = '116234';

$filtered = array_intersect(array_combine(array_keys($a),array_column($a,'ref_id')),[$data['attr'][8]]);

print_r($filtered);

Output

Array
(
    [8709] => 116234
    [8710] => 116234
)

Sandbox

But I am probably just being weird.

Oh and as a Bonus this also makes it possible to match multiple values, where the filter method was limited to one (use ($data) and dependent condition). The last array [$data['attr'][8]] can have multiple values to match with. In this case it's [116234], but it could just as easily be this [116234,93939] which would return the full array.

To explain how it works:

  $filtered = array_intersect(
      array_combine(
          array_keys($a),
          array_column($a, 'ref_id')
      ),
      [$data['attr'][8]]
 );

Will work our way through it:

array_column($a, 'ref_id')
//Output
Array
(
    [0] => 93939
    [1] => 116234
    [2] => 116234
)

This flattens out our array, to make it easier to work with. Next we combine that with the array keys of the original array:

array_keys($a)
//Output
Array
(
    [0] => 8708
    [1] => 8709
    [2] => 8710
)

This is an important step, because both arrays are the same size, same order, we can combine them and basically un-nest the ref_id column.

array_combine(
     array_keys($a),
     array_column($a, 'ref_id')
),
//Output
Array
(
    [8708] => 93939
    [8709] => 116234
    [8710] => 116234
)

Now it's a trivial matter of using array intersect to retrieve the parts we want.

If you wanted to retrieve just the ID's such as this output:

Array
(
    [0] => 8709
    [1] => 8710
)

This can be done by wrapping the whole thing in another array keys, like this:

$filtered = array_keys(array_intersect(array_combine(array_keys($a),array_column($a,'ref_id')),[$data['attr'][8]]));

And the last parting gift I will give is this:

function filter_mutidimensional(array $array, $match){
   if(!is_array($match)) $match = [$match];

   return array_keys(array_intersect(array_combine(array_keys($array),array_column($array,'ref_id')),$match));
}

//which you can call like

filter_mutidimensional($a,$data['attr'][8]);
//or
filter_mutidimensional($a,[$data['attr'][8]]);
//or
filter_mutidimensional($a,[$data['attr'][8],93939]);

Hope that all makes sense!

ArtisticPhoenix
  • 21,464
  • 2
  • 24
  • 38