PHP: Array-Schlüssel Groß- / Kleinschreibung * unempfindlich * Nachschlagen?

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

Ich möchte $myArray['somekeyname']zurückkehren 7.
Gibt es eine Möglichkeit, dies zu tun, ohne das Array zu manipulieren?

Ich erstelle das Array nicht und kann daher seine Schlüssel nicht steuern

Shealtiel
quelle
6
Ich denke, es ist unmöglich, dies zu tun, ohne das Array zu ändern oder zu kopieren. Sie können das Array kopieren und den Schlüsselfall mit der PHP-Funktion array_change_key_case ändern.
ynh
Dies ist die am meisten geschätzte Antwort, aber sie steht in einem Kommentar ... Ich fragte, ob das oben
Genannte
Alles ist möglich. PHP ist Open Source, es geht also nur darum, einige Interna zu ändern und neu zu kompilieren;)
dev-null-
Ich frage mich, ob es möglich ist, mehrere Schlüssel für Ihre eigentliche Aufgabe zuzuordnen. Wenn Sie wissen, dass alle Schlüssel eindeutig sind, beispielsweise nachdem sie alle in Kleinbuchstaben konvertiert wurden, müssen Sie nicht alle Schlüssel aktualisieren, sondern nur das Array iterieren und einen Vergleich der Schlüssel ohne Berücksichtigung der Groß- und Kleinschreibung durchführen . Wichtig ist, dass Sie ein breakoder verwenden return, sobald die Übereinstimmung gefunden wurde, damit keine unnötigen Iterationen durchgeführt werden. Wenn Sie die Möglichkeit des Abgleichs mehrerer Schlüssel berücksichtigen müssen, müssen Sie alle iterieren UND alle nach unten / oben zwingen, um Ihre Daten tatsächlich zu beschädigen.
Mickmackusa

Antworten:

89

Option 1 - Ändern Sie die Art und Weise, wie Sie das Array erstellen

Sie können dies nicht tun, ohne eine lineare Suche durchzuführen oder das ursprüngliche Array zu ändern. Der effizienteste Ansatz besteht darin, strtolower für Tasten zu verwenden, wenn Sie AND einfügen und Werte nachschlagen .

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

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

 }

Wenn es Ihnen wichtig ist, den ursprünglichen Fall des Schlüssels beizubehalten, können Sie ihn als zusätzlichen Wert für diesen Schlüssel speichern, z

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

Option 2 - Erstellen Sie eine sekundäre Zuordnung

Wenn Sie die Frage aktualisiert haben, um darauf hinzuweisen, dass dies für Sie nicht möglich ist, können Sie ein Array erstellen, das eine Zuordnung zwischen Versionen mit Kleinbuchstaben und Versionen mit Groß- und Kleinschreibung bietet.

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

Jetzt können Sie dies verwenden, um die Groß- und Kleinschreibung von einem Kleinbuchstaben zu erhalten

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

Dadurch wird vermieden, dass eine vollständige Kopie des Arrays mit einem Schlüssel in Kleinbuchstaben erstellt werden muss. Dies ist wirklich der einzige andere Weg, um dies zu erreichen.

Option 3 - Erstellen Sie eine Kopie des Arrays

Wenn das Erstellen einer vollständigen Kopie des Arrays kein Problem darstellt , können Sie mit array_change_key_case eine Kopie mit Schlüsseln mit niedrigerem Gehäuse erstellen.

$myCopy=array_change_key_case($myArray, CASE_LOWER);
Paul Dixon
quelle
4
Ich wollte wissen, ob es einen direkten Weg gibt, dies zu tun. Ich sehe, dass es nicht so ist. Ihre Antwort ist eine Möglichkeit für eine
Problemumgehung
14
Es gibt auch array_change_key_case , das hier hilfreich sein kann.
xpy
@xpy: Wollte das gleiche vorschlagen, sollte auch effizienter sein.
Alix Axel
@xpy Das ist die wahre Antwort! Ich verwende Oracle- und MySQL-Datenbanken und sie geben Groß- / Kleinbuchstaben zurück, sodass "Ändern der Art und Weise, wie Sie die Schlüssel erstellen" nicht möglich ist. Dieser Kommentar hat die Inkompatibilität gut gelöst!
Pid
Obwohl Header per se nicht UTF-8 sind, würde ich $ map [mb_strtolower ($ key)] = $ key verwenden; PHPs Multibyte-Funktionen zu nutzen.
Noderman
48

Ich weiß, dass dies eine ältere Frage ist, aber der eleganteste Weg, um dieses Problem zu lösen, ist die Verwendung von:

array_change_key_case($myArray); //second parameter is CASE_LOWER by default

In Ihrem Beispiel:

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

Danach enthält $ myArray alle Kleinbuchstaben:

echo $myArray['somekeyname'] will contain 7

Alternativ können Sie verwenden:

array_change_key_case($myArray, CASE_UPPER);

Die Dokumentation finden Sie hier: http://us3.php.net/manual/en/function.array-change-key-case.php

Shawn
quelle
17

Sie können die ArrayAccessSchnittstelle verwenden, um eine Klasse zu erstellen, die mit der Array-Syntax arbeitet.

Beispiel

$lower_array_object = new CaseInsensitiveArray;
$lower_array_object["thisISaKEY"] = "value";
print $lower_array_object["THISisAkey"]; //prints "value"

oder

$lower_array_object = new CaseInsensitiveArray(
    array( "SoMeThInG" => "anything", ... )
);
print $lower_array_object["something"]; //prints "anything"

Klasse

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;
    }
}
Kendall Hopkins
quelle
14

Eine einfache, aber möglicherweise teure Möglichkeit besteht darin, eine Kopie zu erstellen, diese dann zu verwenden array_change_key_case($array_copy, CASE_LOWER)und anschließend darauf zuzugreifenarray_copy['somekeyname']

Mikpa
quelle
array_change_key_caseändert das Array nicht. Es wird eine Kopie des Arrays zurückgegeben.
Martin Prikryl
2

Ich kombinierte Paul Dixons Idee, ein Mapping für die Schlüssel zu erstellen, und Kendall Hopkins 'Idee, die ArrayAccess- Schnittstelle zu verwenden, um die vertraute Art des Zugriffs auf ein PHP-Array beizubehalten .

Das Ergebnis ist eine Klasse, die das Kopieren des ursprünglichen Arrays vermeidet und einen transparenten Zugriff ohne Berücksichtigung der Groß- und Kleinschreibung ermöglicht, während die Groß- und Kleinschreibung der Schlüssel intern beibehalten wird. Einschränkung: Wenn Groß- und Kleinschreibung ohne Berücksichtigung der Groß- und Kleinschreibung (z. B. 'Foo' und 'foo') im ursprünglichen Array enthalten sind oder dynamisch hinzugefügt werden, überschreiben letztere Einträge die vorherigen (wodurch diese nicht mehr zugänglich sind).

Zugegeben, in vielen Fällen ist es (imo) viel einfacher, die Tasten nur in Kleinbuchstaben zu $lowercasedKeys = array_change_key_case($array, CASE_LOWER);schreiben, wie von Mikpa vorgeschlagen.

Die CaseInsensitiveKeysArray-Klasse

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;
        }
    }
}
Argonym
quelle
2

Ich brauchte auch eine Möglichkeit, um (die erste) Schlüsselübereinstimmung ohne Berücksichtigung der Groß- und Kleinschreibung zurückzugeben. Folgendes habe ich mir ausgedacht:

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

Um die ursprüngliche Frage zu beantworten:

$myArray = array('SOmeKeyNAme' => 7);
$test = 'somekeyname';
$key = get_array_ikey($test, $myArray);
if ($key !== false) {
    echo $myArray[$key];
}
Bitweises Creative
quelle
1

Sie können Ihre Schlüssel in Kleinbuchstaben schreiben, wenn Sie sie dem Array zuweisen, und sie auch in Kleinbuchstaben, wenn Sie den Wert nachschlagen.

Ohne das Array zu ändern, aber die gesamte Datenstruktur:

Ein wirklich umständlicher Weg besteht darin, Magic Getter / Setter-Methoden zu erstellen, aber wäre es wirklich die Mühe wert (beachten Sie, dass auch die anderen Methoden implementiert werden müssen)?

<?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
quelle
Es gibt unzählige Möglichkeiten, dies zu erreichen, ohne die Daten zu beeinträchtigen, aber sind sie die Mühe wirklich wert?
icanhasserver
0

Sie können das Array manuell durchlaufen und nach einer Übereinstimmung suchen.

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

In meinem Fall wollte ich eine effiziente Problemumgehung, bei der mein Programm das Array bereits mithilfe einer foreach-Schleife aus Kundendaten mit unbekanntem Fall erstellt hat, und ich wollte den Fall des Kunden für die spätere Anzeige im Programm beibehalten.

Meine Lösung bestand darin, ein separates Array $ CaseMap zu erstellen, um einen bestimmten Kleinbuchstabenschlüssel dem im Array verwendeten Mixedcase-Schlüssel zuzuordnen (irrelevanter Code wird hier weggelassen):

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

Dann ist die Suche wie folgt:

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

und der Speicheraufwand ist nur das $ CaseMap-Array, das vermutlich kurze Tasten kurzen Tasten zuordnet.

Ich bin mir nicht sicher, ob PHP eine effizientere Möglichkeit zum Generieren von $ CaseMap bietet, wenn ich foreach noch nicht verwende.

David Spector
quelle
0

Ich hatte gerade das gleiche Problem und konnte das ursprüngliche Array nicht ändern. Ich benutze einige Array-Funktionen dafür.

Parameter

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

Lösung

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

Mach es kürzer

if(($idx=array_search(strtolower($search),array_map("strtolower",array_keys($array))))!==FALSE)
    echo array_values($array)[$idx];
CN Lee
quelle
Es scheint, dass Sie die beiden wichtigsten Antworten, die array_change_key_case()viele Jahre zuvor empfohlen wurden, nicht gelesen haben.
Mickmackusa
@ Mickmackusa Du hast recht. Ich habe versucht, Code zu erstellen, der keine Kopie von Werten erstellt. Aber ich habe nicht gefangen "array_values" macht auch kopieren. Das ist nichts besseres.
CN Lee
-1

Ich weiß, dass dies alt ist, aber falls jemand anderes eine schnelle und einfache Möglichkeit benötigt, dies zu tun, ohne das ursprüngliche Array tatsächlich zu ändern:

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
Trey
quelle