【问题标题】:Default value for an associative array in PHP, if key not presentPHP 中关联数组的默认值,如果键不存在
【发布时间】:2022-01-07 13:34:32
【问题描述】:

注意:我已经阅读了Function to set default value of associative array if the key is not presentDefault values for Associative array in PHP

假设:

$a = array('foo' => '123');
$b = $a['bar'];       // PHP Notice:  Undefined index: bar

如果键不存在,我希望默认有一个空字符串 "",而不是 PHP 通知。

我知道解决办法是:

$b = isset($a['bar']) ? $a['bar'] : "";
// or, since PHP 5.3:
$b = $a['bar'] ?: "";      // but this still produces a "PHP Notice"!

有没有办法避免?: ""直接拥有:

$b = $a['bar'];

在没有通知的情况下获取""


注意:这存在于 Python 中:

import collections
d = collections.defaultdict(str)
print(d['bar'])   # empty string, no error

【问题讨论】:

  • 如果你已经阅读了这些问题的答案,并且很清楚地表明不存在这样的功能,你为什么还要问同样的问题?
  • @miken32 因为我想通过删除 PHP 通知来进一步设置默认值(详细信息 here)。这个细节对我很重要。此链接问题未讨论此问题。
  • 无法设置默认值。你的问题是“有没有办法......让$b = $a['bar'];得到"" 而不通知?”这与另一个问题相同。答案仍然是否定的。您最接近的是$b = $a['bar'] ?? "";
  • @miken32 嗯,我还没想过这个。魔鬼在细节中,这和?: ""有什么区别?编辑:我明白了,没有更多的通知,太好了!也许你可以用这个发布答案!
  • 我不想回答这个问题,我正在尝试确定为什么不应该将其作为重复项关闭。

标签: php arrays associative-array key-value defaultdict


【解决方案1】:

(TL;DR 最后一行的答案是“是的,有可能 - 请参阅 Scuzzy 的答案”)

没有。 PHP 数组不能那样工作。

如果您需要 已知 键的默认值,那么您可以分配它们:

$defaults = [
    'foo'  => 'foo',
    'bar'  => 'bar',
];

$a = [ 'bar' => 'baz' ];

$a = array_merge($defaults, $a);

现在$a['bar] 将是'baz'(不是默认的'bar'),但$a['foo'] 将产生'foo'。

我认为使用尚未定义的键实现类似词法简单性的唯一方法是使用一个类并让 $a 使用魔法 getter 返回您选择的默认值。甚至不同键的默认值也不同。

那你就可以写了,

class FakeArray {
    private array $values;
    private array $defaults;
    private $default = null;

    public function __get($key) {
        if (array_key_exists($key, $this->values)) {
            return $this->values[$key];
        }
        if (array_key_exists($key, $this->defaults)) {
            return $this->defaults[$key];
        }
        return $this->default;
    }
    public function __construct(array $values = [ ]) {
        $this->values = $values;
    }
    public function setDefault($default) {
        $this->default = $default;
    }
    public function setDefaults(array $default = []) {
        $this->defaults = $defaults;
    }
};


$a = new FakeArray([ 'foo' => '123' ]);
$a->setDefaults([ 'joe' => 'jim' ]);
$a->setDefault('baz');

$c = $a->foo; // $c is now '123'.
$b = $a->bar; // $b is now 'baz' (no error).
$d = $a->joe; // $d is now 'jim' (not 'baz').

我原以为扩展 ArrayObject 会非常接近您想要的,允许将 $a['key'] 替换为 $a->key,但事实并非如此:

class FakeArray extends ArrayObject {
    ...
    public function __get($key) {
         return $this->default;
    }
}

但是虽然$a['foo'] = 'bar' 确实存储了值,但实际上不仅访问不存在的索引,而且甚至访问不存在的属性(否则将在其他类中工作) 现在触发索引通知

$b = $a->baz;

PHP Notice:  Undefined index: baz in ./test.php ...

所以,ArrayObject 实际上的行为比 StdClass 更糟糕

可能意味着ArrayObject中有一些神奇的优点可以被访问和利用,但我不确定这是如何以及是否属实,或可能。

【讨论】:

  • 是的,创建一个类并在找不到密钥时使用__get() 返回空字符串,最大的烦恼是 is_array() 会失败,而你必须开始使用is_iterable() 无处不在
  • 谢谢@LSerni。我们能否以仍使用$a['bar'] 语法的方式扩展ArrayObject
  • 我一直在尝试这样做,但即使提供了一个魔法吸气剂,$a['nonexistent'] 仍然会抛出一个通知。
  • 我正在查看 offsetGet()offsetexists() 以使用自定义行为对其进行包装
  • @LSerni 我已经发布了另一个可能有助于使用 ArrayObject 概念的答案
【解决方案2】:

您必须使用您的默认值预先填充您的数组,或者按照说明创建一个对象类来处理默认响应。

$array = array('foo' => '123') + array('foo' => '', 'bar' => '');

var_dump( $array );

array(2) {
  ["foo"]=>string(3) "123"
  ["bar"]=> string(0) ""
}

【讨论】:

  • 在您的第一行@Scuzzy,为什么+ array(...) 不覆盖键foo 的值?
  • 它在执行合并之前检查 'foo' 是否存在 stackoverflow.com/a/2140094/61795 这就是为什么它是一种非常简单的方法来使用默认值填充您希望存在于数组中的键。
【解决方案3】:

你可以使用这个方法@before variable with key:

echo @$a['bar'];

【讨论】:

  • 这是一个糟糕的答案。你不是隐藏错误,而是修复它们。
【解决方案4】:

如上所述,我想使用数组ArrayObject 提交另一个解决方案

这扩展了类并稍微调整了offsetGet 的行为以始终返回一个空字符串

这里的parent::语句调用了扩展类的函数

class MyArrayObject extends ArrayObject
{
   function offsetGet( $key )
   {
     return parent::offsetExists( $key ) ? parent::offsetGet( $key ) : '';
   }
}

$myArray = new MyArrayObject( array( 'foo' => '123') );

var_dump( $myArray['foo'] ); // '123' provided string
var_dump( $myArray['bar'] ); // '' our new default
var_dump( isset( $myArray['foo'] ) ); // true
var_dump( isset( $myArray['bar'] ) ); // false

foreach( $myArray as $key )
{
    var_dump( $key ); // only one iteration of '123'
}

var_dump( is_array( $myArray ) ); // false unfortunately
var_dump( is_iterable( $myArray ) ); // true however

var_dump( array_merge( array(), $myArray ) ); // will fail
var_dump( array_merge( array(), (array) $myArray ) ); // will work

最后

$myArray['bar'] = '456'; // now if we set bar
var_dump( $myArray['bar'] ); // '456' new value
var_dump( isset( $myArray['bar'] ) ); // now true

作为@miken32 的建议设置默认值

class MyArrayObject extends ArrayObject
{
    protected $default;
    public function __construct($array = [], $default = null, $flags = 0, $iteratorClass = "ArrayIterator")
    {
        parent::__construct( $array, $flags, $iteratorClass );
        $this->default = $default;
    }
    function offsetGet( $key )
    {
        return parent::offsetExists( $key ) ? parent::offsetGet( $key ) : $this->default;
    }
}

您可以通过设置默认值

$myArray = new MyArrayObject( array( 'foo' => '123'), 'mydefault' );

【讨论】:

  • 可以通过在构造函数中设置默认值并访问它而不是硬编码空间来改进。
  • 我喜欢它,我可能必须创建一个setDefault($value) 方法,因为构造函数已经分配了以下内容:__construct($array = [], $flags = 0, $iteratorClass = "ArrayIterator")
  • 原来它不使用接口,所以我们可以直接使用自定义构造函数。
  • 效果很好。 +1。看来 offsetGet() 是我不知道的魔法:-D
【解决方案5】:

1 步 - 添加此功能:

function getIfIsset(array $array, string $key)
{
    return $array[$key] ?? '';
}

2 步 - 使用这个:

echo getIfIsset($mayArray, 'bar');

【讨论】:

  • 是的,但我想避免使用像getIsset(...) 这样的奇怪语法,我更喜欢使用$a['key'] 标准语法。
猜你喜欢
  • 1970-01-01
  • 2012-03-22
  • 2023-03-18
  • 1970-01-01
  • 2019-04-23
  • 1970-01-01
  • 2017-02-07
  • 2021-05-09
  • 1970-01-01
相关资源
最近更新 更多