按子数组值对数组进行分组

Posted

技术标签:

【中文标题】按子数组值对数组进行分组【英文标题】:Group array by subarray values 【发布时间】:2011-11-26 08:29:39 【问题描述】:

我有一个以下格式的子数组:

array
(
    a => array ( id = 20, name = chimpanzee )
    b => array ( id = 40, name = meeting )
    c => array ( id = 20, name = dynasty )
    d => array ( id = 50, name = chocolate )
    e => array ( id = 10, name = bananas )
    f => array ( id = 50, name = fantasy )
    g => array ( id = 50, name = football )
)

我想根据每个子数组中的 id 字段将它分组到一个新数组中。

array
(
    10 => array
          (
            e => array ( id = 10, name = bananas )
          )
    20 => array
          (
            a => array ( id = 20, name = chimpanzee )
            c => array ( id = 20, name = dynasty )
          )
    40 => array
          (
            b => array ( id = 40, name = meeting )
          )
    50 => array
          (
            d => array ( id = 50, name = chocolate )
            f => array ( id = 50, name = fantasy )
            g => array ( id = 50, name = football )
          )
)

【问题讨论】:

【参考方案1】:
$arr = array();

foreach ($old_arr as $key => $item) 
   $arr[$item['id']][$key] = $item;


ksort($arr, SORT_NUMERIC);

【讨论】:

@Herbert,我猜写入不存在的 id 会影响性能?还是会触发 php 警告? @SampleJACK:我的错误。乍一看,我以为他在验证 $old_arr 中是否存在 id。现在我更仔细地检查了它,使用array_key_exists 不会向此代码添加任何内容。没有它的结果是完全一样的。在性能方面:它在循环内调用一个数组上的函数,该函数必须超过写入不存在的键所带来的任何性能损失,所以我建议放弃整个 if() 块。跨度> @Herbert:我添加了它,因为我认为如果错误报告阈值太低会显示错误。我测试了它,似乎没有抱怨。 @Tim:是的,我的错误报告已经加快,以显示一切,你说得对——没有任何抱怨。我并不是要以任何方式暗示这是糟糕的代码。 SampleJACK 带来了性能,经过考虑,放弃它是有道理的。老实说,我认为它正在检查内部数组上的 id。 这将教会我更仔细地阅读。 :p 你仍然会得到我的 +1 好代码。 我为后人添加了一个答案,以澄清我一直在谈论的内容。【参考方案2】:
foreach($array as $key => $value)
   $newarray[$value['id']][$key] = $value;


var_dump($newarray);

小菜一碟;)

【讨论】:

解释你的代码是如何工作的以及为什么你认为它是最好的技术可能同样容易。 但实际上,将这个答案保留在页面上并没有什么新价值。此纯代码答案(与 Tim 的技术完全相同)是在 Tim 发布 10 分钟后发布的。【参考方案3】:

如果内部数组之一不包含 id,则以下代码改编 @Tim Cooper 的代码以减轻 Undefined index: id 错误:

$arr = array();

foreach($old_arr as $key => $item)

    if(array_key_exists('id', $item))
        $arr[$item['id']][$key] = $item;


ksort($arr, SORT_NUMERIC);

但是,它会删除没有 id 的内部数组。

例如

$old_arr = array(
    'a' => array ( 'id' => 20, 'name' => 'chimpanzee' ),
    'b' => array ( 'id' => 40, 'name' => 'meeting' ),
    'c' => array ( 'id' => 20, 'name' => 'dynasty' ),
    'd' => array ( 'id' => 50, 'name' => 'chocolate' ),
    'e' => array ( 'id' => 10, 'name' => 'bananas' ),
    'f' => array ( 'id' => 50, 'name' => 'fantasy' ),
    'g' => array ( 'id' => 50, 'name' => 'football' ),
    'h' => array ( 'name' => 'bob' )
);

将完全删除 'h' 数组。

【讨论】:

这是一个“发明的问题”——在 OP 的问题中没有体现。可能更好地找到另一个提出此问题的问题并将其发布在那里。【参考方案4】:

你也可以从ouzo-goodies使用Arrays::groupBy():

$groupBy = Arrays::groupBy($array, Functions::extract()->id);

print_r($groupBy);

结果:

Array
(
    [20] => Array
        (
            [0] => Array
                (
                    [id] => 20
                    [name] => chimpanzee
                )

            [1] => Array
                (
                    [id] => 20
                    [name] => dynasty
                )

        )

    [40] => Array
        (
            [0] => Array
                (
                    [id] => 40
                    [name] => meeting
                )

        )

    [50] => Array
        (
            [0] => Array
                (
                    [id] => 50
                    [name] => chocolate
                )

            [1] => Array
                (
                    [id] => 50
                    [name] => fantasy
                )

            [2] => Array
                (
                    [id] => 50
                    [name] => football
                )

        )

    [10] => Array
        (
            [0] => Array
                (
                    [id] => 10
                    [name] => bananas
                )

        )

)

这里是Arrays 和Functions 的文档。

【讨论】:

【参考方案5】:

这是一个函数,它将一个数组作为第一个参数,将一个条件(字符串或回调函数)作为第二个参数。该函数返回一个新数组,该数组根据要求对数组进行分组。

/**
 * Group items from an array together by some criteria or value.
 *
 * @param  $arr array The array to group items from
 * @param  $criteria string|callable The key to group by or a function the returns a key to group by.
 * @return array
 *
 */
function groupBy($arr, $criteria): array

    return array_reduce($arr, function($accumulator, $item) use ($criteria) 
        $key = (is_callable($criteria)) ? $criteria($item) : $item[$criteria];
        if (!array_key_exists($key, $accumulator)) 
            $accumulator[$key] = [];
        

        array_push($accumulator[$key], $item);
        return $accumulator;
    , []);

这是给定的数组:

$arr = array(
    'a' => array ( 'id' => 20, 'name' => 'chimpanzee' ),
    'b' => array ( 'id' => 40, 'name' => 'meeting' ),
    'c' => array ( 'id' => 20, 'name' => 'dynasty' ),
    'd' => array ( 'id' => 50, 'name' => 'chocolate' ),
    'e' => array ( 'id' => 10, 'name' => 'bananas' ),
    'f' => array ( 'id' => 50, 'name' => 'fantasy' ),
    'g' => array ( 'id' => 50, 'name' => 'football' )
);

以及使用带有字符串和回调函数的函数的示例:

$q = groupBy($arr, 'id');
print_r($q);

$r = groupBy($arr, function($item) 
    return $item['id'];
);
print_r($r);

两个例子的结果是一样的:

Array
(
    [20] => Array
        (
            [0] => Array
                (
                    [id] => 20
                    [name] => chimpanzee
                )

            [1] => Array
                (
                    [id] => 20
                    [name] => dynasty
                )

        )

    [40] => Array
        (
            [0] => Array
                (
                    [id] => 40
                    [name] => meeting
                )

        )

    [50] => Array
        (
            [0] => Array
                (
                    [id] => 50
                    [name] => chocolate
                )

            [1] => Array
                (
                    [id] => 50
                    [name] => fantasy
                )

            [2] => Array
                (
                    [id] => 50
                    [name] => football
                )

        )

    [10] => Array
        (
            [0] => Array
                (
                    [id] => 10
                    [name] => bananas
                )

        )

)

在上面的例子中传递回调是多余的,但是当你传入一个对象数组、多维数组或有一些你想要分组的任意东西时,使用回调就可以了。

【讨论】:

以上是关于按子数组值对数组进行分组的主要内容,如果未能解决你的问题,请参考以下文章

如何按特定的子数组值对多维数组进行分组?

PHP:按子数组的出现次数对多维数组进行排序

XSLT 1.0 按子节点的值对 xml 节点进行分组

按子数组的大小对多维数组进行排序

linq 按子字符串对数组中的字符串进行排序

PHP按子数组值排序数组