0

Is there a cleaner way to extract a nested value from a 3 level deep multidimensional array, where i want to pull the result stacked inside the 3rd level, hwoever, i want to keep this dynamic so i can also grab elem from 2nd or 4th level by using an array as a parameter to determine this.

what im trying to do in the end is SORT using this element, but i cant find a way to conveniently indicate the element chain except for this way which i had to create myself:

public function keyBySubElement($nestedArray, array $subElemStack){

    //essentially the loop below is doing this, but it is dynamic now so a user can specify different nested levels in the $subElemStack param.
    //$nestedValue = $nestedArray[$subElemStack[0]][$subElemStack[1]];

    foreach($subElemStack as $nestedElement){

        if(isset($nestedValue) && is_array($nestedValue))
        {
            $nestedValue = $nestedValue[$nestedElement];
        }
        else
        {
            $nestedValue = $nestedArray[$nestedElement];
        }
    }

    return $nestedValue;
}

e.g. to use this method:

assume the following data

         $searchResults = array(
            0 => array(
                'title' => 'one',
                array(
                    'ratings' => array(
                        'count' => '1'
                    )
                )
            ),
            1 => array(
                'title' => 'two',
                array(
                    'ratings' => array(
                        'count' => '5'
                    )
                )
            ),
            2 => array(
                'title' => 'three',
                array(
                    'ratings' => array(
                        'count' => '2'
                    )
                )
            ),
        );

foreach($searchResults as $k => $v){

    $count = $this->keyBySubElement($v, array('ratings','count'));
    $sortData[$k] = $count;
}

this gives me something like this

array(4) {
  [0]=>
  int(1)
  [1]=>
  int(5)
  [2]=>
  int(2)
}

now that i have access to my sub-sub elements value, tied in with its top level parent key, i can use it to sort the top level array by key using my new array $sortData as the reference key which can be reordered by the sub elements value that i want to sort with. i was next going just re-sort the original array by the new key values or something.

i saw a couple potential good examples, but i wasn't able to make them work. those examples are as follows:

[PHP sort: user function][1]

e.g. 1) http://php.net/manual/en/function.sort.php#99419

e.g. 2) Sort php multidimensional array by sub-value

e.g. 3)

/**
 * Sort a 2 dimensional array based on 1 or more indexes.
 *
 * msort() can be used to sort a rowset like array on one or more
 * 'headers' (keys in the 2th array).
 *
 * @param array        $array      The array to sort.
 * @param string|array $key        The index(es) to sort the array on.
 * @param int          $sort_flags The optional parameter to modify the sorting
 *                                 behavior. This parameter does not work when
 *                                 supplying an array in the $key parameter.
 *
 * @return array The sorted array.
 */
public function msort($array, $key, $sort_flags = SORT_REGULAR) {
    if (is_array($array) && count($array) > 0) {
        if (!empty($key)) {
            $mapping = array();
            foreach ($array as $k => $v) {
                $sort_key = '';
                if (!is_array($key)) {
                    $sort_key = $v[$key];
                } else {
                    // @TODO This should be fixed, now it will be sorted as string
                    foreach ($key as $key_key) {
                        $sort_key .= $v[$key_key];
                    }
                    $sort_flags = SORT_STRING;
                }
                $mapping[$k] = $sort_key;
            }
            asort($mapping, $sort_flags);
            $sorted = array();
            foreach ($mapping as $k => $v) {
                $sorted[] = $array[$k];
            }
            return $sorted;
        }
    }
    return $array;
}

e.g. 4)

/**
 * @param $array
 * @param $cols
 * @return array
 */
public function array_msort($array, $cols)
{
    $colarr = array();
    foreach ($cols as $col
    => $order) {
        $colarr[$col] = array();

        foreach ($array as $k => $row) {
            $colarr[$col]['_'.$k] = strtolower($row[$col]);
        }
    }

    $eval = 'array_multisort(';

    foreach ($cols as $col => $order) {
        $eval .= '$colarr[\''.$col.'\'],'.$order.',';
    }

    $eval = substr($eval,0,-1).');';
    eval($eval);
    $ret = array();

    foreach ($colarr as $col => $arr) {

        foreach ($arr as $k => $v) {
            $k = substr($k,1);
            if (!isset($ret[$k])) $ret[$k] = $array[$k];
            $ret[$k][$col] = $array[$k][$col];
        }

    }
    return $ret;
}
Community
  • 1
  • 1
blamb
  • 4,220
  • 4
  • 32
  • 50
  • What is the actual question you want answered? Your method of accessing elements in a nested array is fine. If you want advice on sorting the array elements, you need to state which part of your example array you're trying to sort, and how you want to sort it. The code that you've copied and pasted from other sites isn't needed. – i alarmed alien Oct 16 '14 at 19:51
  • ok should i delete that code? i will, and can. the actual question i guess is there a php function that sorts already this way eliminating the need for the method i created? i would rather not run this stuff through this loop if there was a multisort way of doing this in php (by sub-sub element value), where i can just run in one line. From there i was planning on using my experience to sort. "What i need is the searchResults sorted by rating,count" – blamb Oct 16 '14 at 22:11
  • Where is this data coming from? The array could be rearranged to make it much easier for you to get at the data and sort it. As-is, it requires unnecessarily complicated manipulation. – i alarmed alien Oct 16 '14 at 22:29
  • 1
    right, its coming from a 3rd party API, and they dont support the sort by rating option, and im trying to engineer one. i know what your saying... by the way, i found the best way to sort this after is like this `array_multisort($sortData, SORT_DESC, $searchResults);` perfect unless you see a problem.. – blamb Oct 16 '14 at 22:32

1 Answers1

1

Since the data structure that they return is rather ugly and not condusive to sorting, my first move would be to reformat it into something that can be easily sorted. For example:

# create a new key, 'ratings', and put the contents of [0][ratings][count] in it
foreach ($searchResults as &$s) {
    print_r($s);
    # you could use your keybysubelement function to retrieve the value here
    # rather than hardcoding it
    $s['ratings'] = $s[0]['ratings']['count'];
    unset($s[0]);
}
print_r($searchResults);

resulting data structure:

Array
(
    [0] => Array
        (
            [title] => one
            [ratings] => 1
        )
    [1] => Array
        (
            [title] => two
            [ratings] => 5
        )
    [2] => Array
        (
            [title] => three
            [ratings] => 2
        )
)

It's then easy to create a sort function that will operate on this array to sort it according to the value in 'ratings':

# create a closure that will sort by a given key and in a given direction
# by default the order is ascending   
function by_key($key, $dir = 'asc') {
    return function ($a, $b) use ($key, $dir) {
        if ($a[$key] > $b[$key]) {
            if ($dir === 'asc')
                return 1;
            return -1;
        }
        elseif ($a[$key] < $b[$key]) {
            if ($dir === 'asc')
                return -1;
            return 1;
        }
        return 0;
    };
}

# sort by ratings, descending, using uasort and the custom search function:
uasort( $searchResults, by_key('ratings','desc') );
# print the results
foreach ($searchResults as $i) {
    echo $i['title'] . ', ' . $i['ratings'] . PHP_EOL;
}

array order after sort:

two, 5
three, 2
one, 1

Sort by title:

uasort( $searchResults, by_key('title') );

Output:

one, 1
three, 2
two, 5
i alarmed alien
  • 9,412
  • 3
  • 27
  • 40
  • thanks this is a useful idea. i will consider that. their data is not condusive at all to sorting, one of their elements is called taxonomy, and it contains a very nested json string inside it. UGH!!! – blamb Oct 17 '14 at 00:31