在PHP中,是否有一个函数可以返回由关联数组组成的数组中某个键的值?

27

我相信这个问题以前已经被问过了,很抱歉我没有先找到它。

原始数组:

[0] => Array
    (
        [categoryId] => 1
        [eventId] => 2
        [eventName] => 3
        [vendorName] => 4
    )

[1] => Array
    (
        [categoryId] => 5
        [eventId] => 6
        [eventName] => 7
        [vendorName] => 8
    )

[2] => Array
    (
        [categoryId] => 9
        [eventId] => 10
        [eventName] => 11
        [vendorName] => 12
    )

我期望的结果是:print_r(get_values_from_a_key_in_arrays('categoryId', $array));

[0] => 1
[1] => 5
[2] => 9

我只是想要比编写自己的基于foreach函数更清晰的东西。如果foreach是答案,我已经有它了。

编辑: 我不想使用硬编码的键,我只是展示了对解决方案的一个示例调用。谢谢! ^_^

PHP 5.3的快速获取解决方案:

private function pluck($key, $data) {
    return array_reduce($data, function($result, $array) use($key) {
        isset($array[$key]) && $result[] = $array[$key];
        return $result;
    }, array());
}

2
这被称为“拔取”,并不是PHP的默认函数。不过你可以很容易地自己编写它。 - Joost
真酷。我以前做过这个,但我从来不知道它的技术名字叫做“拔取”。 - Moses
这很好,但是array_reduce()是错误的函数。它旨在“将数组减少为单个值”。在其他语言中也是如此。您应该使用array_map()array_walk()进行重写...这也将简化代码。 - doublejosh
@doublejosh 我不同意。array_reduce 在这里很好用。它将一个数组中的数组缩减为一个值的数组。 - Michael
内部操作确实是一个reduce,我在抱怨使用时一定看错了外部操作。 - doublejosh
8个回答

34
所以,高阶 集合/迭代器函数(例如pluckfiltereachmap等)的酷炫之处在于它们可以混合搭配,组成更复杂的操作集合。
大多数语言都提供这些类型的函数(查找类似于collection、iterator或enumeration/enumerable的包),有些语言提供的函数比其他语言更多,并且你通常会看到这些函数在不同语言中的命名不同(例如collect == map,reduce == fold)。如果你的语言中不存在该函数,你可以从已有的函数中创建它
关于您的测试用例...我们可以使用array_reduce来实现pluck。我最初发布的第一个版本依赖于array_map;然而,我同意@salathe的观点,array_reduce对于这个任务来说更加简洁;array_map是一个可以接受的选择,但最终你需要做更多的工作。一开始array_reduce可能看起来有些奇怪,但如果回调函数组织得好,一切都会很顺利。
一个不那么幼稚的pluck也会检查它是否可以在迭代的值上“调用”(一个函数/方法)。在下面的幼稚实现中,我们假设结构是哈希表(关联数组)。 这将设置测试用例数据(固定装置):
<?php

$data[] = array('categoryId' => 1,    'eventId' => 2,  'eventName' => 3,  'vendorName' => 4);
$data[] = array('categoryId' => 5,    'eventId' => 6,  'eventName' => 7,  'vendorName' => 8);
$data[] = array('categoryId' => 9,    'eventId' => 10, 'eventName' => 11, 'vendorName' => 12);
$data[] = array(/* no categoryId */   'eventId' => 10, 'eventName' => 11, 'vendorName' => 12);
$data[] = array('categoryId' => false,'eventId' => 10, 'eventName' => 11, 'vendorName' => 12);
$data[] = array('categoryId' => 0.0,  'eventId' => 10, 'eventName' => 11, 'vendorName' => 12);

选择您喜欢的pluck版本

$preferredPluck = 'pluck_array_reduce'; // or pluck_array_map

PHP 5.3+ 中的 "pluck":array_reduce 提供了一种简洁的实现方法,但不像 array_map 版本那样易于理解:

function pluck_array_reduce($key, $data) {
  return array_reduce($data, function($result, $array) use($key){
    isset($array[$key]) &&
      $result[] = $array[$key];

    return $result;
  }, array());
}

"

PHP 5.3+中的“pluck”:array_map对此并不完美,因此我们需要进行更多的检查(它仍然不能处理许多潜在情况):

"
function pluck_array_map($key, $data) {
  $map = array_map(function($array) use($key){
    return isset($array[$key]) ? $array[$key] : null;
  }, $data);

  // is_scalar isn't perfect; to make this right for you, you may have to adjust
  return array_filter($map, 'is_scalar');
}

对于PHP5.3以下版本的“pluck”

我们本可以使用旧版create_function; 但这是不好的形式,不被推荐,也不够优雅,因此我决定不展示它。

function pluck_compat($key, $data) {
  $map = array();
  foreach ($data as $array) {
    if (array_key_exists($key, $array)) {
      $map[] = $array[$key];
    }
  }
  unset($array);

  return $map;
}

在这里,我们选择调用“pluck”的版本,根据我们正在运行的PHP版本。如果您运行整个脚本,则无论您使用哪个版本,都应该得到正确的答案。
$actual   = version_compare(PHP_VERSION, '5.3.0', '>=')
          ? $preferredPluck('categoryId', $data)
          : pluck_compat('categoryId', $data);
$expected = array(1, 5, 9, false, 0.0);
$variance = count(array_diff($expected, $actual));

var_dump($expected, $actual);
echo PHP_EOL;
echo 'variance: ', $variance, PHP_EOL;

print @assert($variance)
    ? 'Assertion Failed'
    : 'Assertion Passed';

注意,结尾处没有'?>'。这是因为它不需要。去掉它比保留它更有好处。
顺便说一句,看起来这将作为array_column添加到PHP 5.5中。

1
哇,这是什么版本的PHP?我从未见过“use”,这太棒了。 - Caleb Gray
为了其他遇到这个问题的人,您介意使用create_function,并将array_map放在array_filter调用中(摆脱$map变量)吗? - Caleb Gray
1
PS:array_reduce()函数可以同时完成映射和筛选步骤。 - salathe
1
嗨@salathe; 你提出了一个很好的观点。我几乎使用array_reduce()来实现我的原始实现; 然而,我通常保留array_reduce用于无法轻松处理其他情况的情况。array_reduce非常棒; 但是,它需要更多的认知开销来推理。 - Wil Moore III
@Jonz 我已经很久没有写 PHP 了;不过,我还记得 foreach 会将变量泄漏到块外面。当然,一旦函数超出范围,垃圾回收器应该会清除。明确取消设置是不必要的;但是,虽然我知道它最终可能会被拾起,但我也不喜欢乱扔垃圾,所以就这样 :) - Wil Moore III
显示剩余20条评论

9

您需要的是映射(Mapping):


$input = array(
    array(
        'categoryId' => 1,
        'eventId' => 2,
        'eventName' => 3,
        'vendorName' => 4,
    ),
    array(
        'categoryId' => 5,
        'eventId' => 6,
        'eventName' => 7,
        'vendorName' => 8,
    ),
    array(
        'categoryId' => 9,
        'eventId' => 10,
        'eventName' => 11,
        'vendorName' => 12,
    ),
);

$result = array_map(function($val){
    return $val['categoryId'];
}, $input);

或者创建想要的函数:

function get_values_from_a_key_in_arrays($key, $input){
    return array_map(function($val) use ($key) {
        return $val[$key];
    }, $input);
};

然后使用它:

$result = get_values_from_a_key_in_arrays('categoryId', $array);

它适用于PHP版本>=5.3,在这个版本中允许使用匿名回调函数。对于早期版本,您需要提前定义回调函数并传递其名称而不是匿名函数。


你能把 return $val['categoryId']; 改成 return $val[$key]; 吗? - Songo
1
@Songo:如果不创建一个由count($input)$key值组成的数组,一个解决方案是array_map(function($val, $key = 'categoryId') {return $val[$key];}, $input)--但我看不出在lambda函数中这样做有什么好处。 - Alix Axel
@Songo: 另一种解决方案是:array_map(function($val, $key) {return $val[$key];}, $input, array_fill(0, count($input), 'categoryId')) - Alix Axel
1
@Songo:我已经按照原帖的要求添加了一个函数,所以你应该会满意的 ;) - Tadeck
1
дёҚиҰҒдҪҝз”Ёcreate_function()пјҢеҰӮжһңдёҚжғідҪҝз”ЁеёҰжңүеҢҝеҗҚеҮҪж•°зҡ„array_map()пјҡзӣёеҸҚпјҢдҪҝз”ЁеҫӘзҺҜпјҲиҝҷжҜ”и°ғз”Ёжҳ е°„еӣһи°ғжӣҙвҖңеҝ«вҖқпјүгҖӮ - salathe
显示剩余6条评论

6

没有针对此的内置函数,但通常称之为“pluck”。


从PHP 5.5开始,有一个内置函数:array_column - bishop

5
<?php
$a = array(
        array('a' => 1, 'b' => 2),
        array('a' => 2, 'b' => 2),
        array('a' => 3, 'b' => 2),
        array('a' => 4, 'b' => 2)
);

function get_a($v) {
        return $v['a'];
}

var_dump(array_map('get_a', $a));

您可以使用create_function或匿名函数(PHP 5.3及以上版本)。
<?php
$a = array(
        array('a' => 1, 'b' => 2),
        array('a' => 2, 'b' => 2),
        array('a' => 3, 'b' => 2),
        array('a' => 4, 'b' => 2)
);

var_dump(array_map(create_function('$v', 'return $v["a"];'), $a));

我会写一个回调函数,如上所述,并将其与array_map一起使用。

如果您使用的是PHP5.3或更高版本,您可以将get_a编写为lambda函数,并使其更加紧凑。(编辑...因为我注意到其他答案正在这样做) - Spudley
@Spudley:请看我的回答,它以你提到的方式完美地实现了。 - Tadeck
@Tadeck - 是的,我看到了(并给了你+1),但是在我先评论这个答案之后才看到的。 - Spudley
create_function很糟糕,而且在PHP 5.3之前就已经可用了。 - Alix Axel

4

从PHP 5.5开始,使用array_column函数:

$events = [
    [ 'categoryId' => 1, 'eventId' =>  2, 'eventName' =>  3, 'vendorName' =>  4 ],
    [ 'categoryId' => 5, 'eventId' =>  6, 'eventName' =>  7, 'vendorName' =>  8 ],
    [ 'categoryId' => 9, 'eventId' => 10, 'eventName' => 11, 'vendorName' => 12 ],
];

print_r(array_column($events, 'categoryId'));

在3v4l上在线查看。

在5.5版本之前,您可以考虑使用polyfill


3

没有内置的函数。但可以很容易地使用array_map()函数来创建一个。

$array = array(
    array(
        "categoryID"   => 1,
        "CategoryName" => 2,
        "EventName"    => 3,
        "VendorName"   => 4
    ),
    array(
        "categoryID"   => 5,
        "CategoryName" => 6,
        "EventName"    => 7,
        "VendorName"   => 8
    ),
    array(
        "categoryID"   => 9,
        "CategoryName" => 10,
        "EventName"    => 11,
        "VendorName"   => 12
    )
);

$newArray = array_map(function($el) {
    return $el["categoryID"];
}, $array);

var_dump($newArray);

2

当你需要Lisp时,它在哪里?实际上,在PHP中也很容易管理。只需使用如下所示的array_map函数即可。

# bunch o data
$data = array();
$data[0] = array("id" => 100, "name" => 'ted');
$data[1] = array("id" => 200, "name" => 'mac');
$data[2] = array("id" => 204, "name" => 'bub');

# what you want to do to each bit of it
function pick($n) { return($n['id']); }
# what you get after you do that
$map = array_map("pick", $data);
# see for yourself
print_r($map);

0

你可以使用array_filter,并传入基于所需键的函数。

Tadeck的答案更优秀。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接