在多维数组中提取动态嵌套元素进行排序的更干净的方法


cleaner way to pull dynamic nested elements in multidimensional array for sorting

有没有一种更干净的方法可以从3级深度多维数组中提取嵌套值,我想在其中提取堆叠在第3级内部的结果,无论如何,我想保持这种动态,这样我也可以通过使用数组作为参数来确定这一点,从而从第2级或第4级获取elem。

我最后想做的是使用这个元素进行SORT,但除了我自己创建的之外,我找不到一种方便地指示元素链的方法

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;
}

例如使用这种方法:

假设以下数据

         $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;
}

这给了我类似的东西

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

现在我可以访问我的子元素值,并与它的顶级父关键字绑定,我可以使用它按关键字对顶级数组进行排序,使用我的新数组$sortData作为引用关键字,可以根据我想要排序的子元素的值对其进行重排序。接下来,我只是根据新的键值或其他东西对原始数组进行重新排序。

我看到了几个潜在的好例子,但我没能让它们发挥作用。这些例子如下:

[PHP排序:用户函数][1]

例如1)http://php.net/manual/en/function.sort.php#99419

例如2)按子值对php多维数组进行排序

例如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;
}

例如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;
}

由于他们返回的数据结构相当难看,而且不利于排序,我的第一步是将其重新格式化为易于排序的内容。例如:

# 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);

生成的数据结构:

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

然后很容易创建一个排序函数,该函数将对该数组进行操作,根据"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;
}

排序后的数组顺序:

two, 5
three, 2
one, 1

按标题排序:

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

输出:

one, 1
three, 2
two, 5