php: Array keys case *insensitive* lookup?
PhpArraysPhp Problem Overview
$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
Php Solutions
Solution 1 - Php
Option 1 - change the way you create the array
You can't do this without either a linear search or altering the original array. The most efficient approach will be to use strtolower on keys when you insert AND when you lookup values.
$myArray[strtolower('SOmeKeyNAme')]=7;
if (isset($myArray[strtolower('SomekeyName')]))
{
}
If it's important to you to preserve the original case of the key, you could store it as a additional value for that key, e.g.
$myArray[strtolower('SOmeKeyNAme')]=array('SOmeKeyNAme', 7);
Option 2 - create a secondary mapping
As you updated the question to suggest this wouldn't be possible for you, how about you create an array providing a mapping between lowercased and case-sensitive versions?
$keys=array_keys($myArray);
$map=array();
foreach($keys as $key)
{
$map[strtolower($key)]=$key;
}
Now you can use this to obtain the case-sensitive key from a lowercased one
$test='somekeyname';
if (isset($map[$test]))
{
$value=$myArray[$map[$test]];
}
This avoids the need to create a full copy of the array with a lower-cased key, which is really the only other way to go about this.
Option 3 - Create a copy of the array
If making a full copy of the array isn't a concern, then you can use array_change_key_case to create a copy with lower cased keys.
$myCopy=array_change_key_case($myArray, CASE_LOWER);
Solution 2 - Php
I know this is an older question but the most elegant way to handle this problem is to use:
array_change_key_case($myArray); //second parameter is CASE_LOWER by default
In your example:
$myArray = array ('SOmeKeyNAme' => 7);
$myArray = array_change_key_case($myArray);
Afterwards $myArray will contain all lowercase keys:
echo $myArray['somekeyname'] will contain 7
Alternatively you can use:
array_change_key_case($myArray, CASE_UPPER);
Documentation be seen here: http://us3.php.net/manual/en/function.array-change-key-case.php
Solution 3 - Php
You could use ArrayAccess
interface to create a class that works with array syntax.
Example
$lower_array_object = new CaseInsensitiveArray;
$lower_array_object["thisISaKEY"] = "value";
print $lower_array_object["THISisAkey"]; //prints "value"
or
$lower_array_object = new CaseInsensitiveArray(
array( "SoMeThInG" => "anything", ... )
);
print $lower_array_object["something"]; //prints "anything"
Class
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;
}
}
Solution 4 - Php
A simple, but maybe expensive way, is to make a copy, then use array_change_key_case($array_copy, CASE_LOWER)
, and after that access array_copy['somekeyname']
Solution 5 - Php
I combined Paul Dixon's idea of creating a mapping for the keys and Kendall Hopkins' idea of using the ArrayAccess interface for retaining the familiar way of accessing a PHP array.
The result is a class that avoids copying the initial array and allows transparent case-insensitive access, while internally preserving the keys' case. Limitation: If case-insensitively equal keys (e.g. 'Foo' and 'foo') are contained in the initial array or are added dynamically, then latter entries will overwrite previous ones (rendering these inaccessible).
Admittedly, in many cases its (imo) much more straight-forward to just lowercase the keys by $lowercasedKeys = array_change_key_case($array, CASE_LOWER);
, as suggested by Mikpa.
The CaseInsensitiveKeysArray class
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;
}
}
}
Solution 6 - Php
From the PHP site
function array_ikey_exists($key, $haystack){
return array_key_exists(strtolower($key), array_change_key_case($haystack));
}
Referance: http://us1.php.net/manual/en/function.array-key-exists.php#108226
Solution 7 - Php
I also needed a way to return (the first) case-insensitive key match. Here's what I came up with:
/**
* 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;
}
So, to answer the original question:
$myArray = array('SOmeKeyNAme' => 7);
$test = 'somekeyname';
$key = get_array_ikey($test, $myArray);
if ($key !== false) {
echo $myArray[$key];
}
Solution 8 - Php
You can lowercase your keys when assigning them to the array and also lowercase them when looking up the value.
Without modifying the array, but the whole data structure:
A really cumbersome way involves creating magic getter/setter methods, but would it really be worth the effort (note that the other methods have to be implemented too)?
<?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;
}
}
Solution 9 - Php
You could loop through the array manually and search for a match.
foreach( $myArray as $key => $value ) {
if( strtolower( $key ) == 'somekeyname' ) {
// match found, $value == $myArray[ 'SOmeKeyNAme' ]
}
}
Solution 10 - Php
In my case I wanted an efficient workaround where my program was already creating the array using a foreach loop from customer data having unknown case, and I wanted to preserve the customer's case for later display in the program.
My solution was to create a separate array $CaseMap to map a given lowercase key to the mixedcase key used in the array (irrelevant code is omitted here):
$CaseMap=[];
foreach ($UserArray as $Key=>$Value)
$CaseMap[strtolower($Key)]=$Key;
Then lookup is like this:
$Value=$UserArray[$CaseMap("key")];
and the memory overhead is just the $CaseMap array, which maps presumably short keys to short keys.
I'm not sure if PHP has a more efficient way to generate $CaseMap in the case where I'n not already using foreach.
Solution 11 - Php
I just had same problem and I could not change original array. I use few array functions for it.
Parameters
$search = "AbCd";
$array = array("AbcD"=>"11","Bb"=>"22");
Solution
$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];
Make it shorter
if(($idx=array_search(strtolower($search),array_map("strtolower",array_keys($array))))!==FALSE)
echo array_values($array)[$idx];
Solution 12 - Php
I know this is old, but in case anyone else needs a quick easy way to do this without actually changing the original array:
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