PHP:数组键不区分大小写查找?

82
$myArray = array ('SOmeKeyNAme' => 7);  
我希望$myArray['somekeyname']返回7。 有没有一种方法可以在不操作数组的情况下实现这一点?
我没有创建数组,因此无法控制其键。

6
我认为如果不修改或复制数组的话是不可能做到的。你可以使用PHP函数array_change_key_case复制数组并改变键的大小写。 - ynh
这是最受欢迎的答案,但它是以评论的形式写出来的...我问过以上内容是否可行,答案是:不行。 - shealtiel
一切皆有可能。PHP 是开源的,所以只需要改变一些内部设置并重新编译即可 ;) - dev-null-dweller
我想知道匹配多个键对于你的实际任务的潜力。如果你知道这些键都是唯一的,比如在它们全部转换为小写后,那么你就不需要更新所有的键,只需要迭代数组并对键执行不区分大小写的比较即可。重要的是,你应该尽快使用breakreturn来找到匹配项,以便不进行不必要的迭代。如果你需要适应匹配多个键的可能性,那么你将需要迭代所有的键,并强制所有键转换为小写/大写实际上会破坏你的数据。 - mickmackusa
12个回答

101

选项1-更改创建数组的方式

如果不进行线性搜索或更改原始数组,无法完成此操作。最有效的方法是在插入和查找值时对键使用strtolower

 $myArray[strtolower('SOmeKeyNAme')]=7;

 if (isset($myArray[strtolower('SomekeyName')]))
 {

 }

如果保留键的原始大小写很重要,您可以将其作为该键的附加值存储,例如:

$myArray[strtolower('SOmeKeyNAme')]=array('SOmeKeyNAme', 7);

选项2 - 创建一个辅助映射

既然您更新了问题,表示对您来说这不是可能的选择,那么您可以创建一个数组,提供小写和区分大小写版本之间的映射关系。

$keys=array_keys($myArray);
$map=array();
foreach($keys as $key)
{
     $map[strtolower($key)]=$key;
}

现在你可以使用这个方法来从小写的键获取区分大小写的键

$test='somekeyname';
if (isset($map[$test]))
{
     $value=$myArray[$map[$test]];
}

这避免了需要创建一个带小写键的完整副本的需要,这实际上是另一种处理方式。

选项3-创建数组副本

如果不担心创建数组的完整副本,则可以使用array_change_key_case来创建具有小写键的副本。

$myCopy=array_change_key_case($myArray, CASE_LOWER);

4
我想知道是否存在一种直接的方法来做这件事。我发现并没有。你的答案是一个解决问题的可能方法。 - shealtiel
21
这里还有一个array_change_key_case函数,可能会对这里有帮助。 - xpy
@xpy 这才是真正的答案!我使用 Oracle 和 MySQL 数据库,它们返回大小写键,因此“更改创建键的方式”是不可行的。这条评论很好地解决了不兼容性问题! - pid
虽然标题本身不是UTF-8格式,但我会使用$map[mb_strtolower($key)]=$key;来利用PHP的多字节函数。 - noderman
为什么要这样复制整个数组?一行代码:array_key_exists(strtolower($key), array_change_key_case($myArray)); - SpYk3HH
显示剩余3条评论

55
我知道这是一个比较旧的问题,但是处理这个问题最优雅的方式是使用:
array_change_key_case($myArray); //second parameter is CASE_LOWER by default

在你的例子中:
$myArray = array ('SOmeKeyNAme' => 7);
$myArray = array_change_key_case($myArray);

之后,$myArray将包含所有小写键:
echo $myArray['somekeyname'] will contain 7

或者你可以使用:
array_change_key_case($myArray, CASE_UPPER);

文档可以在这里查看:https://www.php.net/manual/zh/function.array-change-key-case.php

20
你可以使用 ArrayAccess 接口来创建一个支持数组语法的类。 示例:
$lower_array_object = new CaseInsensitiveArray;
$lower_array_object["thisISaKEY"] = "value";
print $lower_array_object["THISisAkey"]; //prints "value"
或者
$lower_array_object = new CaseInsensitiveArray(
    array( "SoMeThInG" => "anything", ... )
);
print $lower_array_object["something"]; //prints "anything"

class CaseInsensitiveArray implements ArrayAccess
{
    private $_container = array();

    public function __construct( Array $initial_array = array() ) {
        $this->_container = array_map( "strtolower", $initial_array );
    }

    public function offsetSet($offset, $value) {
        if( is_string( $offset ) ) $offset = strtolower($offset);
        if (is_null($offset)) {
            $this->container[] = $value;
        } else {
            $this->container[$offset] = $value;
        }
    }

    public function offsetExists($offset) {
        if( is_string( $offset ) ) $offset = strtolower($offset);
        return isset($this->_container[$offset]);
    }

    public function offsetUnset($offset) {
        if( is_string( $offset ) ) $offset = strtolower($offset);
        unset($this->container[$offset]);
    }

    public function offsetGet($offset) {
        if( is_string( $offset ) ) $offset = strtolower($offset);
        return isset($this->container[$offset])
            ? $this->container[$offset]
            : null;
    }
}

14

一种简单但可能很昂贵的方法是先复制数组,然后使用 array_change_key_case($array_copy, CASE_LOWER),接着访问 array_copy['somekeyname']


array_change_key_case不会修改数组。它返回数组的一个副本。 - Martin Prikryl

2
我结合了Paul Dixon创建键映射的想法和Kendall Hopkins使用ArrayAccess接口保留访问PHP数组的熟悉方式的想法。
结果是一个类,避免复制初始数组并允许透明的不区分大小写访问,同时在内部保留键的大小写。限制:如果初始数组或动态添加的键(例如“Foo”和“foo”)在不区分大小写的情况下相等,则后面的条目将覆盖先前的条目(使其无法访问)。
诚然,在许多情况下,只需通过$lowercasedKeys = array_change_key_case($array, CASE_LOWER);将键转换为小写即可更加直观,正如Mikpa所建议的那样。 CaseInsensitiveKeysArray类
class CaseInsensitiveKeysArray implements ArrayAccess 
{
    private $container = array();
    private $keysMap   = array();

    public function __construct(Array $initial_array = array()) 
    {
        $this->container = $initial_array;

        $keys = array_keys($this->container);
        foreach ($keys as $key) 
        {
            $this->addMappedKey($key);
        }
    }

    public function offsetSet($offset, $value) 
    {
        if (is_null($offset)) 
        {
            $this->container[] = $value;
        }
        else 
        {
            $this->container[$offset] = $value;
            $this->addMappedKey($offset);
        }
    }

    public function offsetExists($offset) 
    {
        if (is_string($offset)) 
        {
            return isset($this->keysMap[strtolower($offset)]);
        }
        else 
        {
            return isset($this->container[$offset]);
        }
    }

    public function offsetUnset($offset) 
    {
        if ($this->offsetExists($offset)) 
        {
            unset($this->container[$this->getMappedKey($offset)]);
            if (is_string($offset)) 
            {
                unset($this->keysMap[strtolower($offset)]);
            }
        }
    }

    public function offsetGet($offset) 
    {
        return $this->offsetExists($offset) ? 
               $this->container[$this->getMappedKey($offset)] : 
               null;
    }

    public function getInternalArray() 
    {
        return $this->container;
    }

    private function addMappedKey($key) 
    {
        if (is_string($key)) 
        {
            $this->keysMap[strtolower($key)] = $key;
        }
    }

    private function getMappedKey($key) 
    {
        if (is_string($key)) 
        {
            return $this->keysMap[strtolower($key)];
        }
        else 
        {
            return $key;
        }
    }
}

2

来自 PHP 网站

function array_ikey_exists($key, $haystack){
    return array_key_exists(strtolower($key), array_change_key_case($haystack));    
}

参考资料:http://us1.php.net/manual/zh/function.array-key-exists.php#108226

该函数用于检查数组中是否存在指定的键名,如果存在则返回 true,否则返回 false。使用该函数可以避免在访问不存在的键名时出现错误。


2

我还需要一种返回(第一个)不区分大小写的键匹配项的方法。以下是我想出来的方法:

/**
 * Case-insensitive search for present array key
 * @param string $needle
 * @param array $haystack
 * @return string|bool The present key, or false
 */
function get_array_ikey($needle, $haystack) {
    foreach ($haystack as $key => $meh) {
        if (strtolower($needle) == strtolower($key)) {
            return (string) $key;
        }
    }
    return false;
}

因此,回答原始问题:
$myArray = array('SOmeKeyNAme' => 7);
$test = 'somekeyname';
$key = get_array_ikey($test, $myArray);
if ($key !== false) {
    echo $myArray[$key];
}

1

在将键分配给数组时,您可以将它们转换为小写,并在查找值时也将它们转换为小写。

而不修改数组,但修改整个数据结构:

一种非常繁琐的方法涉及创建魔术 getter/setter 方法,但这真的值得付出努力吗(请注意,其他方法也必须实现)?

<?php 

class CaseInsensitiveArray
{ 

  protected $m_values;

  public function __construct()
  {
    $this->m_values = array();
  } 

  public function __get($key)
  { 
    return array_key_exists($key, $this->m_values) ? $this->m_values[$key] : null;
  } 

  public function __set($key, $value)
  { 
    $this->m_attributes[$key] = $value;
  } 
} 

有很多方法可以在不干扰数据的情况下实现这一点,但它们真的值得麻烦吗? - icanhasserver

1
你可以手动遍历数组并搜索匹配项。
foreach( $myArray as $key => $value ) {
    if( strtolower( $key ) == 'somekeyname' ) {
        // match found, $value == $myArray[ 'SOmeKeyNAme' ]
    }
}

0

我曾经遇到过同样的问题,但是我无法更改原始数组。我使用了一些数组函数来解决它。

参数

$search = "AbCd";
$array = array("AbcD"=>"11","Bb"=>"22");

解决方案

$lower_search = strtolower($search);
$array_of_keys = array_map("strtolower",array_keys($array));
$idx = array_search($lower_search,$array_of_keys);
if($idx !== FALSE)
    echo array_values($array)[$idx];

让它更短

if(($idx=array_search(strtolower($search),array_map("strtolower",array_keys($array))))!==FALSE)
    echo array_values($array)[$idx];

看起来你没有阅读前两个回答,它们在很多年前就推荐了 array_change_key_case() - mickmackusa
@mickmackusa 你是对的。我尝试编写不复制值的代码。但我没有注意到 "array_values" 也会复制。这并没有更好。 - CN Lee

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