如何将一个数组包含多个数组或对象转换为关联数组?

20

我习惯于使用perl的map()函数,其中回调函数可以同时分配键和值,从而创建一个关联数组,其输入为平面数组。我知道array_fill_keys()可以用于创建字典风格的哈希表,但如果您不一定希望所有值都相同怎么办?显然,所有事情都可以通过循环迭代来完成,但还有哪些(可能更优雅)的方法存在呢?

编辑:添加一个示例以澄清转换。请不要纠缠于转换,问题在于将扁平列表转换为哈希表,其中我们不能假定所有值都相同。

$original_array: ('a', 'b', 'c', 'd')
$new_hash: ('a'=>'yes', 'b'=>'no', 'c'=>'yes', 'd'=>'no')

*note: the values in this example are arbitrary, governed by some business logic that is not really relevant to this question. For example, perhaps it's based on the even-oddness of the ordinal value of the key

现实世界的例子 所以,使用这里提供的答案,您可以解析$_POST以获取只匹配给定条件的输入字段列表。例如,如果您的表单中有许多输入字段,但某个特定组必须一起处理,则这可能很有用。

在这种情况下,我有许多输入字段,它们表示到数据库的映射。每个输入字段看起来像这样:<input name="field-user_email" value="2" />,其中每种类型的字段都已前缀为“field-”。

我们想要做的是首先获取仅以“field-”开头的那些输入字段的列表,然后我们想要创建一个名为$mapped_fields的关联数组,其将提取的字段名称作为键,并将实际输入字段的值作为值。

$mapped_fields = array_reduce( preg_grep( '/field-.+/', array_keys( $_POST ) ), function( $hash, $field ){ $hash[substr( $field, 6 )] = $_POST[$field]; return $hash; } );

这将输出:

Array ( [date_of_birth] => 1 [user_email] => 2 [last_name] => 3 [first_name] => 4 [current_position] => 6 )

因此,为了预防那些持反对意见的人,让我同意这段紧凑代码可能比一个简单的循环遍历 $_POST 并且对于每个键检查是否具有前缀,并且如果是,则将它和它的值弹出到数组中的方法要不可读得多。


你能否使用print_rvar_dump打印数组的内容? - Florent
4个回答

52

几天前我遇到了完全相同的问题。使用array_map是不可能的,但array_reduce可以解决问题。

$arr = array('a','b','c','d');
$assoc_arr = array_reduce($arr, function ($result, $item) {
    $result[$item] = (($item == 'a') || ($item == 'c')) ? 'yes' : 'no';
    return $result;
}, array());
var_dump($assoc_arr);

结果:

array(4) { ["a"]=> string(3) "yes" ["b"]=> string(2) "no" ["c"]=> string(3) "yes" ["d"]=> string(2) "no" }


1
哇,我从未想过可以这样使用array_reduce - 我认为它总是用于返回标量值。回想起来,这种方法是否比@minitech上面提到的foreach方法更可取还有争议。但很高兴看到你的替代方案!感谢您的发布。 - Tom Auger
巧妙的方法。这经常非常方便。 - Ikke
我实际上同意Tom Auger的观点,使用这种方法是值得怀疑的。即使你在性能方面取得了胜利(我不确定),与常规的foreach语句相比,你失去了可读性。不过,这确实很优雅 ;) - daniel.auener
哇,这是一个很好的解决方案,可以将对象中的元素映射到命名数组中。我有一个类,其中包含十几个属性,需要在写入数据库之前将它们放入命名数组中。问题是我不需要所有的对象属性,只需要一个子集。"生活在数据库中的属性"数组有这个列表,所以这个方法非常有效。function MakePersistent() { $dbData = array_reduce($this->persistentProperties,array($this,'mapFields')); ..在此处写入到数据库... } function mapFields($result,$property) { $result[$property] = $this->$property; return $result; }.... 完成! - Lance Cleveland

3
据我所知,这在一个表达式中完全不可能,因此您可以使用类似于 foreach 循环的方式来完成。
$new_hash = array();

foreach($original_array as $item) {
    $new_hash[$item] = 'something';
}

如果您需要将其放在一个表达式中,可以编写一个函数:
function array_map_keys($callback, $array) {
    $result = array();

    foreach($array as $item) {
        $r = $callback($item);

        $result[$r[0]] = $r[1];
    }

    return $result;
}

2

这是对我在已接受方法中评论的澄清。希望更易读懂。这来自于一个WordPress课程,因此使用$wpdb引用来写入数据:

class SLPlus_Locations {
    private $dbFields = array('name','address','city');

    public function MakePersistent() {
        global $wpdb;
        $dataArray = array_reduce($this->dbFields,array($this,'mapPropertyToField'));
        $wpdb->insert('wp_store_locator',$dataArray);
    }

    private function mapPropertyToField($result,$property) {
        $result[$property] = $this->$property;
        return $result;
    }
}

显然,完整解决方案还有更多细节,但与array_reduce()相关的部分已经存在。比使用foreach或通过array_map()加上自定义插入语句来强制解决问题更易读且更优雅。

不错!


作为一个喜欢一行代码和 Perl 风格混淆代码的人,这一直是我的首选解决方案,即使在生产项目中也是如此。不错,感谢您回来提供另一个解决方案。 - Tom Auger

1
一个很好的使用 yield 运算符的案例!
$arr = array('a','b','c','d');

$fct = function(array $items) {
            foreach($items as $letter)
            {
                yield sprintf("key-%s",
                    $letter
                ) => "yes";
            }
        };

$newArr = iterator_to_array($fct($arr));

which gives:

array(4) {
  'key-a' =>
  string(3) "yes"
  'key-b' =>
  string(3) "yes"
  'key-c' =>
  string(3) "yes"
  'key-d' =>
  string(3) "yes"
}

2
看起来非常啰嗦和复杂,但我喜欢迭代器的例子 - 我已经编程PHP超过10年了,从未使用过yield。谢谢! - Tom Auger
我甚至不知道PHP有生成器! - Charles Wood

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