85
$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

4
  • 6
    I think it is impossible to make this without modifying or copying the array. You can copy the array an change the key case with the php function array_change_key_case. Commented Nov 21, 2010 at 20:10
  • This is the most appreciated answer, but it's written in a comment... I asked whether the above is possible, and the answer is: no Commented Nov 21, 2010 at 20:17
  • Everything is possible. PHP is open-source so it is just a matter of changing some internals and recompiling ;) Commented Nov 21, 2010 at 21:14
  • I wonder about the potential of matching multiple keys for your actual task. If you KNOW that the keys will all be unique, say after they are all converted to lowercase, then you don't need to update all of the keys, you just need to iterate the array and perform a case-insensitive comparison on the keys. Importantly, you should be using a break or return as soon as the match is found so that no unnecessary iterating is done. If you need to accommodate the possibility of matching multiple keys, then you will need to iterate all AND forcing all to lower/upper will actually damage your data. Commented Apr 16, 2020 at 0:13

12 Answers 12

105

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);
Sign up to request clarification or add additional context in comments.

8 Comments

I wanted to know if a direct way to do this exists. I see that it doesn't. Your answer is one possibility for a workaround
There is also array_change_key_case that can be helpful here.
@xpy: Was about to suggest the very same thing, should be more efficient as well.
@xpy This is the real answer! I use oracle and mysql databases and they return upper/lower case keys, so "change the way you create the keys" is not feasible. This comment solved the incompatibility nicely!
Though headers are not UTF-8 per se, I would use $map[mb_strtolower($key)]=$key; to leverage PHPs multibyte functions.
|
56

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: https://www.php.net/manual/en/function.array-change-key-case.php

Comments

21

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

Comments

15

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']

1 Comment

array_change_key_case does not modify the array. It returns a copy of the array.
3

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

Comments

3

From the PHP site

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

Reference: https://www.php.net/manual/en/function.array-key-exists.php#108226

Comments

2

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' ]
    }
}

1 Comment

You could even do this inside a function so it's reusable. The only real downside is that if you need to do it more than once or twice it's wasted effort compared to making a mapping of the keys. But there are a lot of situations where you just need to find one item, or check if the key exists, and for one reason or another you are searching with the wrong case (for instance, based on user input). I landed on this question wondering if PHP just had a built-in function that does this. Maybe the solution is too obvious.
2

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

Comments

1

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

1 Comment

There are tons of ways to achieve this without messing with the data, but are they really worth the trouble?
0

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.

Comments

0

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

2 Comments

It appears that you did not read the top two answers that recommend array_change_key_case() many years earlier.
@mickmackusa You're right. I tried to make code what do not make copy of values. But I didn't catch "array_values" also makes copy. This is nothing better.
-1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.