PHP Array Filter Multi Level

$users = array();
$users[0]['id'] = 7;
$users[0]['username'] = 'coin';
$users[0]['rooms'][0]['socket'] = 'XXXX';
$users[0]['rooms'][0]['room'] = 1;
$users[1]['id'] = 8;
$users[1]['username'] = 'asdasd';
$users[1]['rooms'][0]['socket'] = 'AAAA';
$users[1]['rooms'][0]['room'] = 2;

I want to get the room value of the member whose socket value is AAAA.

With Array filter I can do it when there is only one depth, but I am stuck at multiple depths.

I'd appreciate it if you could offer something other than foreach. Thanks.

The following attempt is unsuccessful.

$socket = 'AAAA';
$room_detect = array_filter($users, function($v, $k) use ($socket) {
if(array_search($socket, $v['rooms']))
{
    return $v;
}
}, ARRAY_FILTER_USE_BOTH);

Answer

Solution:

You can adopt a recursive approach to make it work for any depth.

  • Loop over the current level of the array.
  • If the key in the current loop at hand is a socket and it matches the value you are searching for, access the room key instantly and return the value.
  • If you can't find the socket at all, return false.

Snippet:

<?php

function findSocket($data,$socket_value){
    foreach($data as $key => $value){
        if(is_array($value)){
            $res = findSocket($value,$socket_value);
            if($res !== false) return $res;
        }else if($key == 'socket' && $value === $socket_value){
            return $data['room']; // because socket and room keys are on the same level
        }
    }
    
    return false;
}

$room_no = findSocket($users,'AAAA');
if($room_no == false){
    die("Room not found");
}

echo $room_no;

Answer

Solution:

Option 1: array_filter - you were very close in your attempt. I added array_merge to flatten the array_column and simplified your logic a bit

<?php    
$users = array();
$users[0]['id'] = 7;
$users[0]['username'] = 'coin';
$users[0]['rooms'][0]['socket'] = 'XXXX';
$users[0]['rooms'][0]['room'] = 1;
$users[1]['id'] = 8;
$users[1]['username'] = 'asdasd';
$users[1]['rooms'][0]['socket'] = 'AAAA';
$users[1]['rooms'][0]['room'] = 2;
$users[1]['rooms'][1]['socket'] = 'BBBB';
$users[1]['rooms'][1]['room'] = 4;

$socket = 'AAAA';
$room_detect = array_filter(array_merge(...array_column($users,'rooms')), function($v, $k) use ($socket) 
{ return $v['socket']===$socket;} , ARRAY_FILTER_USE_BOTH);
print_r($room_detect);

Example: https://www.tehplayground.com/KVK1Ur0KTJYn0H3L


Option 2: a hack - but if you have a zillion users in that array, this might be the fastest. I'll probably get into trouble but:

<?php
// example code

$users = array();
$users[0]['id'] = 7;
$users[0]['username'] = 'coin';
$users[0]['rooms'][0]['socket'] = 'XXXX';
$users[0]['rooms'][0]['room'] = 1;
$users[1]['id'] = 8;
$users[1]['username'] = 'asdasd';
$users[1]['rooms'][0]['socket'] = 'AAAA';
$users[1]['rooms'][0]['room'] = 2;


function getRoomOf($user, $u) {
   return preg_replace('/\D/', '', explode('"room":',explode('"'.$user.'"', $u)[1])[1]);
}
print_r(getRoomOf('AAAA', json_encode($users)));

Example at: https://www.tehplayground.com/zpsDkRkPCEAXmQpY

Answer

Solution:

$users = array();
$users[0]['id'] = 7;
$users[0]['username'] = 'coin';
$users[0]['rooms'][0]['socket'] = 'XXXX';
$users[0]['rooms'][0]['room'] = 1;
$users[1]['id'] = 8;
$users[1]['username'] = 'asdasd';
$users[1]['rooms'][0]['socket'] = 'AAAA';
$users[1]['rooms'][0]['room'] = 2;
$users[1]['rooms'][1]['socket'] = 'BBBB';
$users[1]['rooms'][1]['room'] = 4;

$socket = 'AAAA';
$room_detect = array_filter(array_column($users,'rooms'), function($v, $k) use ($socket) 
{
    foreach($v as $room)
    {
        if($room['socket']===$socket)
        {
            return true;
        }
    }
    
}, ARRAY_FILTER_USE_BOTH);
print_r($room_detect);

RESULT

(
    [1] => Array
        (
            [0] => Array
                (
                    [socket] => AAAA
                    [room] => 2
                )

            [1] => Array
                (
                    [socket] => BBBB
                    [room] => 4
                )

        )

)

I want it as below.

[0] => Array
                (
                    [socket] => AAAA
                    [room] => 2
                )

Source