Amiut7 Amiut7 - 4 days ago 5
PHP Question

How to return a sub-array via array_filter

I want to create a function that searches among a large array and returns some fields , the array is for example like this (

var_dump
of the large array ) :

array(3) {
[0]=>
array(5) {
["group_id"]=>
int(87)
["group_name"]=>
string(28) "General Specifictaions"
["group_slug"]=>
string(80) "%da%af%d8%b2%db%8c%d9%86%d9%87-%d9%87%d8%a7%db%8c-%d8%b9%d9%85%d9%88%d9%85%db%8c"
["group_desc"]=>
string(0) ""
["attributes"]=>
array(4) {
[0]=>
array(5) {
["attr_id"]=>
int(95)
["attr_name"]=>
string(23) "Release date"
["attr_slug"]=>
string(67) "%d8%aa%d8%a7%d8%b1%db%8c%d8%ae-%d8%a7%d9%86%d8%aa%d8%b4%d8%a7%d8%b1"
["attr_desc"]=>
string(0) ""
["value"]=>
string(3) "144"
}
[1]=>
array(5) {
["attr_id"]=>
int(96)
["attr_name"]=>
string(21) "Availability"
["attr_slug"]=>
string(5) "stock"
["attr_desc"]=>
string(0) ""
["value"]=>
string(7) "instock"
}
}
}
[1]=>
array(5) {
["group_id"]=>
int(89)
["group_name"]=>
string(19) "Display"
["group_slug"]=>
string(55) "%d8%b5%d9%81%d8%ad%d9%87-%d9%86%d9%85%d8%a7%db%8c%d8%b4"
["group_desc"]=>
string(0) ""
["attributes"]=>
array(0) {
}
}
}


This array is the list of all of the specifications of a product, what i want to do is to search for a specific attribute in
attributes
section and only return that attribute, and this search can be by
attr_id
or
attr_name
or
attr_slug
, so i created this function which uses
array_filter
but i can't return the part i want :

function dw_attr_value_by( $post_id = '', $field, $value ) {
if( !$post_id ){
global $post;
$post_id = $post->ID;
}

if( !$post_id ) return;

$table = dw_get_table_result( $post_id ); // The large array

return array_filter( $table, function( $v, $k ) use( $field, $value ){
$attributes = $v['attributes'];

if( sizeof( $attributes ) == 0 ) return;

for( $i = 0; $i < sizeof( $attributes ); $i++ ) {
if( $field == 'id' ) {
if( $attributes[$i]['attr_id'] == $value ) break;
} elseif( $field == 'slug' ){
if( $attributes[$i]['attr_slug'] == rawurlencode( $value ) ) break;
} elseif( $field == 'name' ){
if( $attributes[$i]['attr_name'] == $value ) break;
}
}

return $attributes[$i];

}, ARRAY_FILTER_USE_BOTH );
}


It does filter the array but it doesn't return the part i want , for example i want the result to be like
var_dump( dw_attr_value_by( $post->ID, 'id', 144 )
:

array(5) {
["attr_id"]=>
int(95)
["attr_name"]=>
string(23) "Release date"
["attr_slug"]=>
string(67) "%d8%aa%d8%a7%d8%b1%db%8c%d8%ae-%d8%a7%d9%86%d8%aa%d8%b4%d8%a7%d8%b1"
["attr_desc"]=>
string(0) ""
["value"]=>
string(3) "144"
}


JSON-formatted of the large array

[{"group_id":87,"group_name":"\u06af\u0632\u06cc\u0646\u0647 \u0647\u0627\u06cc \u0639\u0645\u0648\u0645\u06cc","group_slug":"%da%af%d8%b2%db%8c%d9%86%d9%87-%d9%87%d8%a7%db%8c-%d8%b9%d9%85%d9%88%d9%85%db%8c","group_desc":"","attributes":[{"attr_id":95,"attr_name":"\u062a\u0627\u0631\u06cc\u062e \u0627\u0646\u062a\u0634\u0627\u0631","attr_slug":"%d8%aa%d8%a7%d8%b1%db%8c%d8%ae-%d8%a7%d9%86%d8%aa%d8%b4%d8%a7%d8%b1","attr_desc":"","value":"144"},{"attr_id":96,"attr_name":"\u0648\u0636\u0639\u06cc\u062a \u0628\u0627\u0632\u0627\u0631","attr_slug":"stock","attr_desc":"","value":"instock"},{"attr_id":99,"attr_name":"\u0628\u0644\u0648\u062a\u0648\u062b","attr_slug":"%d8%a8%d9%84%d9%88%d8%aa%d9%88%d8%ab","attr_desc":"","value":"yes"},{"attr_id":100,"attr_name":"\u0648\u0627\u06cc \u0641\u0627\u06cc","attr_slug":"%d9%88%d8%a7%db%8c-%d9%81%d8%a7%db%8c","attr_desc":"","value":"no"}]},{"group_id":89,"group_name":"\u0635\u0641\u062d\u0647 \u0646\u0645\u0627\u06cc\u0634","group_slug":"%d8%b5%d9%81%d8%ad%d9%87-%d9%86%d9%85%d8%a7%db%8c%d8%b4","group_desc":"","attributes":[]},{"group_id":57,"group_name":"\u067e\u0631\u062f\u0627\u0632\u0646\u062f\u0647","group_slug":"%d9%be%d8%b1%d8%af%d8%a7%d8%b2%d9%86%d8%af%d9%87","group_desc":"","attributes":[]}]

Answer

I don't think that array_filter is a proper way to do that. However you just need to create a variable out of callback function and save filtered data there.

$foundAttrs = [];
$filteredArray = array_filter( $table, function( $v, $k ) use( $field, $value, $foundAttrs){
    $attributes = $v['attributes'];

    if( sizeof( $attributes ) == 0 ) return;

    for( $i = 0; $i < sizeof( $attributes ); $i++ ) {
        if( $field == 'id' ) {
            if( $attributes[$i]['attr_id'] == $value ) break;
        } elseif( $field == 'slug' ){
            if( $attributes[$i]['attr_slug'] == rawurlencode( $value ) ) break;
        } elseif( $field == 'name' ){
            if( $attributes[$i]['attr_name'] == $value ) break;
        }
    }
    array_push($foundAttrs, $attributes[$i]);

    return $attributes[$i];

}, ARRAY_FILTER_USE_BOTH );

//now $foundAttrs consists of all found values

If you post origin array(e.g. json string) it will be easier to come up with better idea, because now it is not easy to test possible solution

UPDATE

Firstly a few words about the code posted in question:

  1. if you count something inside 'for' statement (I mean sizeof($attributes)) the loop does that command on every iteration
  2. return $attributes[$i]; will simply return the last element of $attributes if all IF/ELSE statements fail

So I fixed these points and rewrote the function with help of foreach loops. If it doesn't work, please post the given array(the large one) as a json string so I will be able to test my solution with real data

function dw_attr_value_by( $post_id = '', $field, $value ) {
    if( !$post_id ){
        global $post;
        $post_id = $post->ID;
    }

    if( !$post_id ) return;

    $table = dw_get_table_result( $post_id ); // The large array

    foreach($table as $groupKey => $group) {
        if (isset($group['attributes'])) {
            foreach ($group['attributes'] as $attr) {
                if( $field == 'id' && $attr['attr_id'] == $value) {
                    return $attr;
                } elseif( $field == 'slug' && $attr['attr_slug'] == rawurlencode($value)){
                    return $attr;
                } elseif( $field == 'name' && $attr['attr_name'] == $value){
                    return $attr;
                }
            }
        }
    }

    return null;
}
Comments