0

I have an array like the below:

$arrays = [
    'a' => [
        'name' => "Name 1",
        'age' => "99",
        'add' => ""
    ],
    'b' => [
        'name' => "Name 2",
        'age' => "99",
        'add' => "Add2"
    ],
    'c' => [
        'name' => "Name 3",
        'age' => "99",
        'add' => "Add3"
    ],
    'd' => [
        'name' => "",
        'age' => "",
        'add' => "Add4"
    ]
];

I want to get a result like:

$res = [
    'a' => ['add'],
    'd' => ['name','age']
];

I have tried with the below code, but it returns 1.

$status = array_walk_recursive($arrays, function($v, $k) {
    global $output;
    if (empty($v) && $v !== 0)
        $output[$k] = $v;
});

I want to do it without using any loops because my real input array is very large and I am concerned with performance.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
S Sopno
  • 19
  • 3
  • Is the array always going to be in the format you show in the question. – Nigel Ren Sep 27 '20 at 07:34
  • yes.. Format will be same..as like this – S Sopno Sep 27 '20 at 07:35
  • 3
    You can't iterate over array without a loop. So, in `array_walk_recursive` there's a loop __too__, but it is hidden ubder the hood. – u_mulder Sep 27 '20 at 07:47
  • I know array walk recursive, which is a loop but this loop is faster than php custom loop as i know. This is a sample array, but my real array is very big. Thats why i dont wanna use any custom loop like foreach. – S Sopno Sep 27 '20 at 07:52
  • 1
    Not sure how up to date https://stackoverflow.com/questions/18144782/performance-of-foreach-array-map-with-lambda-and-array-map-with-static-function is, but it seems to contradict your assumption. – Nigel Ren Sep 27 '20 at 07:57

4 Answers4

0

If your input is always of a fixed depth, you can map the existing values to the keys of all empty items:

$output = array_map(function($row) {
    return array_keys(array_filter($row, function ($e) {
        return empty($e) && $e !== 0;
    }));
}, $arrays);

The outer function runs for each "row", the value of which is then converted into a list of all keys with empty values (excluding zeroes, as in the question).

This will keep the outer keys B & C as empty arrays, so if you want them to be removed as well then run another iteration of array_filter over the result:

$output = array_filter($output)

See https://3v4l.org/c23ZB

As mentioned in the comments, there are still several loops going on here, they're just not as visible in the code. A regular foreach loop might end up being a lot easier to read, and possibly perform faster as well.

iainn
  • 16,826
  • 9
  • 33
  • 40
0

You can use next combination of array_walk & array_filter:

    $result = [];

    array_walk(
        $arrays,
        function($el, $key) use(&$result) {
            $empty = array_filter($el, function($el){return $el == "";});
            $empty_keys = array_keys($empty);
            if (count($empty_keys)) $result[$key] = $empty_keys;
        }
    );

Try it here

Slava Rozhnev
  • 9,510
  • 6
  • 23
  • 39
  • Thank you so much. I have big array with at least 1 lac+ data. which one is better, custom loop or using built-in function? Someone suggested to use custom loop and some other suggested for built-in function, though I prefer built-in function for better performance. Any suggestion regarding this issue? – S Sopno Sep 27 '20 at 08:24
  • You can find lot of articles about performance `foreach` vs `array_walk` and in most cases `foreach` is bit a faster when `array_walk`. You can try it on your own datasample and post results here – Slava Rozhnev Sep 27 '20 at 08:38
0

This is another way to achieve your desired output.

$result = [];
 foreach($arrays as $key => $value) {
     $empty_arr = array_filter($value, function ($ele) {
         return empty($ele);
     });
      $empty_arr_keys = array_keys($empty_arr);
      if(!empty($empty_arr_keys)) $result[$key] = $empty_arr_keys;        
  }
        
 print_r($result);
Sapna Dorbi
  • 115
  • 2
  • 12
0

@iainn's answer can be sharpened up by calling !strlen() on the deep values.

Code: (Demo)

var_export(
    array_filter(
        array_map(
            fn($row) => array_keys(
                array_filter(
                    $row,
                    fn($v) => !strlen($v)
                )
            ),
            $array
        )
    )
);

But you will end up making fewer iterations and writing cleaner, more intuitive/readable code if you use classic loops. This is how I would write it in my own project:

Code: (Demo)

$result = [];
foreach ($array as $rowKey => $row) {
    foreach ($row as $key => $value) {
        if (!strlen($value)) {
            $result[$rowKey][] = $key;
        }
    }
}
var_export($result);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136