php: Array keys case *insensitive* lookup?

$myArray = array ('SOmeKeyNAme' => 7);

I want $myArray['somekeyname'] to return 7.
Is there a way to do this, without manipulating the array?

I don't create the array, an thus can not control it's keys

55863 次浏览

您可以在将键分配给数组时将其小写,也可以在查找值时将其小写。

没有修改数组,但是整个数据结构:

一个非常麻烦的方法涉及到创建神奇的 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;
}
}

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

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

 $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);

您可以使用 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;
}
}

您可以手动遍历数组并搜索匹配项。

foreach( $myArray as $key => $value ) {
if( strtolower( $key ) == 'somekeyname' ) {
// match found, $value == $myArray[ 'SOmeKeyNAme' ]
}
}

一个简单但可能代价高昂的方法是制作一个拷贝,然后使用 array_change_key_case($array_copy, CASE_LOWER),然后访问 array_copy['somekeyname']

我结合了 Paul Dixon 为键创建映射的想法和 Kendall Hopkins 使用 ArrayAccess接口保留访问 PHP 数组的熟悉方法的想法。

结果是一个避免复制初始数组并允许透明的不区分大小写访问的类,同时在内部保留键的大小写。限制: 如果不区分大小写的相等键(例如‘ Foo’和‘ Foo’)包含在初始数组中或动态添加,那么后面的条目将覆盖前面的条目(使这些条目无法访问)。

不可否认的是,在许多情况下,按照 Mikpa 的建议,它的(imo)更加直接,只用 $lowercasedKeys = array_change_key_case($array, CASE_LOWER);来小写键。

CaseInsense tiveKeysArray 类

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;
}
}
}

我知道这是一个古老的问题,但处理这个问题最优雅的方法是使用:

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);

文档可以在这里看到: http://us3.php.net/manual/en/function.array-change-key-case.php

来自 PHP 站点

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

参考文献: http://us1.php.net/manual/en/function.array-key-exists.php#108226

我知道这有点老套,但是如果有人需要一个简单快捷的方法来做这件事,而不需要真正改变原始数组的话:

function array_key_i($needle, $haystack){
$key=array_search(strtolower($search), array_combine(array_keys($array),array_map('strtolower', array_keys($array))));
return ($key!==false);
}


$array=array('TeSt1'=>'maybe');
$search='test1';


array_key_i($search, $array); // returns true

我还需要一种方法来返回(第一个)不区分大小写的键匹配:

/**
* 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];
}

在我的例子中,我需要一个有效的解决方案,在这个解决方案中,我的程序已经在使用 foreach 循环从具有未知大小写的客户数据创建数组,并且我希望保留客户的大小写,以便以后在程序中显示。

我的解决方案是创建一个单独的数组 $CaseMap,将给定的小写键映射到数组中使用的 mix edcase 键(这里省略了不相关的代码) :

$CaseMap=[];
foreach ($UserArray as $Key=>$Value)
$CaseMap[strtolower($Key)]=$Key;

然后查找是这样的:

$Value=$UserArray[$CaseMap("key")];

而内存开销只是 $CaseMap 数组,它可能将短键映射到短键。

我不确定在我还没有使用 foreach 的情况下,PHP 是否有更有效的方法来生成 $CaseMap。

我只是有同样的问题,我不能改变原来的数组。我使用一些数组函数为它。

参数

$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];