Числовая строка как ключ массива в PHP

104

Можно ли использовать числовую строку, например, "123"в качестве ключа в массиве PHP, без ее преобразования в целое число?

$blah = array('123' => 1);
var_dump($blah);

печатает

array(1) {
  [123]=>
  int(1)
}

я хочу

array(1) {
  ["123"]=>
  int(1)
}
Догберт
источник
7
Поскольку PHP типизирован слабо, "123"== 123почти для всех целей. По какой причине вы хотите, чтобы он был именно строкой (а иметь int - это плохо)?
ircmaxell
17
Причина, которая приходит мне в голову, связана с функциями массива, такими как array_merge «Если входные массивы имеют одинаковые строковые ключи, то более позднее значение для этого ключа перезапишет предыдущее. Однако если массивы содержат числовые ключи , более позднее значение не будет перезаписать исходное значение, но будет добавлено ".
ficuscr 05
8
Другой пример, когда числовые строки в качестве ключей массива проблематичны:asort
swenedo
4
Другой вариант использования: модульное тестирование передачи данных JSON. Преобразование такого массива в JSON и обратно не позволит вам утверждать, что и оригинал, и результат полностью совпадают.
Дэвид

Ответы:

90

Нет; нет, это не так:

Из руководства :

Ключ может быть целым или строковым. Если ключ является стандартным представлением целого числа, он будет интерпретирован как таковой (т.е. «8» будет интерпретироваться как 8, а «08» будет интерпретироваться как «08»).

Дополнение

Из-за комментариев ниже я подумал, что было бы забавно указать, что поведение похоже, но не идентично ключам объектов JavaScript.

foo = { '10' : 'bar' };

foo['10']; // "bar"
foo[10]; // "bar"
foo[012]; // "bar"
foo['012']; // undefined!
Хэмиш
источник
107
PHP, серверный IE.
Марек Маурицио
5
Похоже, что способ есть! Вы не согласны с этим ответом? stackoverflow.com/a/35180513/247696
Flimm
1
Как?! .. foo [012] return "bar"
Химаншу
2
@Himanshu: потому что php интерпретирует числа, начинающиеся с 0, как восьмеричные. поэтому 012 - это восьмеричное число 10.
Еасир Арафат Маджумдер
49

Да, это возможно путем преобразования stdClassобъекта в массив :

$data =  new stdClass;
$data->{"12"} = 37;
$data = (array) $data;
var_dump( $data );

Это дает вам (до версии PHP 7.1):

array(1) {
  ["12"]=>
  int(37)
}

(Обновление: мой первоначальный ответ показал более сложный способ использования json_decode()иjson_encode() что не нужно.)

Обратите внимание на комментарий : к сожалению, невозможно напрямую ссылаться на значение:$data['12'] приведет к уведомлению.

Обновление :
с PHP 7.2 также можно использовать числовую строку в качестве ключа для ссылки на значение:

var_dump( $data['12'] ); // int 32
Дэвид
источник
2
Однако прямой доступ к значению с помощью строкового ключа не работает. Добавьте эту строку в вашем примере: echo $data['12'];. Будет выдана ошибка «Примечание: неопределенное смещение: 12 дюймов - в строке 5».
LS
Да, ты прав. Предполагая, что это было возможно в прошлом.
Дэвид
1
когда вы используете laravel dd ($ data), он вылетает: P
вылетает Камил Келчевски
2
В PHP 7.2.0RC2 поведение такое же, как и раньше.
dev0
1
Судя по всему array_key_exists, и в старых версиях не найду.
Don't Panic
12

Если вам нужно использовать числовой ключ в структуре данных php, объект будет работать. А объекты сохраняют порядок, поэтому вы можете выполнять итерацию.

$obj = new stdClass();
$key = '3';
$obj->$key = 'abc';
на паровой тяге
источник
Это очень хорошее предложение. Я пишу код фреймворка и сталкиваюсь с тем, что кто-то передает массив, который может иметь "случайное" индексирование: array ('this', 'that') или "ассоциативное" индексирование: array (123 => array ('this', 'that ')). Теперь, благодаря вам, я могу просто напечатать;) +1
Just Plain High
Но возможно ли использовать числовую строку, например «123», в качестве ключа в массиве PHP без преобразования в целое число?
Flimm
@Flimm нет, это невозможно, поэтому я предлагаю свое решение.
Steampowered
@Flimm, этот ответ, похоже, противоречит руководству по PHP : строки, содержащие действительные целые числа, будут преобразованы в целочисленный тип. Например, ключ «8» будет фактически сохранен под номером 8. С другой стороны, «08» не будет приведено, так как это недопустимое десятичное целое число. , хотя я не проверял его ответ.
Steampowered
Это предложение находится в разделе «Указание с помощью» array(), поэтому я предполагаю, что в этом контексте строки, определяющие допустимые целые числа, будут преобразованы в целочисленный тип. Но оказывается, что есть другие способы создания массивов, где этого не происходит, например, в ответе Дэвида, который я проверил.
Flimm
10

Мое решение:

$id = 55;
$array = array(
  " $id" => $value
);

Пробел char (prepend) - хорошее решение, потому что сохраните преобразование int:

foreach( $array as $key => $value ) {
  echo $key;
}

Вы увидите 55 как int.

Undolog
источник
4
Или "0$id" => $value. Подготовка 0тоже работает.
nawfal
Значит, вы говорите, что невозможно использовать числовую строку типа «123» в качестве ключа в массиве PHP без ее преобразования в целое число?
Flimm
5

Вы можете привести ключ к строке, но в конечном итоге он будет преобразован в целое число из-за свободной типизации PHP. Посмотреть на себя:

$x=array((string)123=>'abc');
var_dump($x);
$x[123]='def';
var_dump($x);

Из руководства PHP:

Ключ может быть целым или строковым. Если ключ является стандартным представлением целого числа, он будет интерпретирован как таковой (т.е. «8» будет интерпретироваться как 8, а «08» будет интерпретироваться как «08»). Плавающие в ключе усекаются до целого числа. Индексированные и ассоциативные типы массивов - это один и тот же тип в PHP, который может содержать как целые, так и строковые индексы.

Bcosca
источник
1
Преобразование происходит не из-за неправильного набора текста; php определяет, выглядит ли строка числовой, а затем преобразует ее.
Ja͢ck
1
Так ты говоришь, что это невозможно? Этот ответ показывает, что есть способ использовать строку, которая выглядит как целое число, в качестве ключа в массиве.
Flimm
ИМХО проблема в интерпретаторе PHP. Невозможно даже представить себе язык, который сочетает в себе строки и целые числа в качестве ключей массива. Лучшее решение? По предложению Undolog stackoverflow.com/a/15413637/1977778, лучшим решением является использование пробела ... К сожалению.
Sentenza
@sentenza: это возможно представить, тем более что PHP допускает сочетание строк и целых чисел в качестве ключей массива:[42 => 'answer', 'snafu' => 'fubar']
LS
@LS ага. Я знаю, что PHP позволяет вам это делать, но если вы рассмотрите ключи в рамках гипотетической системы общих типов, они не будут соответствовать никаким смешанному типу, который одновременно охватывает строки и числа. Рыхлое типирование применяется к ключам ассоциативного массива просто ошибки.
sentenza
1

У меня была эта проблема при попытке объединить массивы, которые имели как строковые, так и целочисленные ключи. Было важно, чтобы целые числа также обрабатывались как строка, поскольку это были имена для полей ввода (например, размеры обуви и т. Д.)

Когда я использовал $data = array_merge($data, $extra);PHP, ключи менялись. При попытке упорядочить целочисленные ключи (я пробовал с 6- '6'- "6"даже в (string)"6"качестве ключей) были переименованы с 0 наn ... Если подумать, в большинстве случаев это было бы желаемым поведением.

Вы можете обойти это, используя $data = $data + $extra;вместо этого. Довольно прямолинейно, но сначала я не подумал об этом ^^.

Brainfeeder
источник
Точно такая же проблема привела меня на эту страницу, но я должен сказать, что это не ответ на вопрос OP.
Flimm
@Flimm Верно. Но поиски ответа привели меня на эту страницу. Я подумал, что мое решение может помочь другим гуглерам :)
Brainfeeder
1

Строки, содержащие допустимые целые числа, будут преобразованы в целочисленный тип. Например, ключ «8» будет фактически сохранен под номером 8. С другой стороны, «08» не будет приведено, так как это недопустимое десятичное целое число.

НЕПРАВИЛЬНО

У меня есть функция литья, которая обрабатывает последовательное преобразование в ассоциативный массив,

$array_assoc = cast($arr,'array_assoc');

$array_sequential = cast($arr,'array_sequential');

$obj = cast($arr,'object');

$json = cast($arr,'json');



function cast($var, $type){

    $orig_type = gettype($var);

    if($orig_type == 'string'){

        if($type == 'object'){
            $temp = json_decode($var);
        } else if($type == 'array'){
            $temp = json_decode($var, true);
        }
        if(isset($temp) && json_last_error() == JSON_ERROR_NONE){
            return $temp;
        }
    }
    if(@settype($var, $type)){
        return $var;
    }
    switch( $orig_type ) {

        case 'array' :

            if($type == 'array_assoc'){

                $obj = new stdClass;
                foreach($var as $key => $value){
                    $obj->{$key} = $value;
                }
                return (array) $obj;

            } else if($type == 'array_sequential'){

                return array_values($var);

            } else if($type == 'json'){

                return json_encode($var);
            }
        break;
    }
    return null; // or trigger_error
}
TarranJones
источник
1

В качестве обходного пути вы можете закодировать массив PHP в объект json с опцией JSON_FORCE_OBJECT.

т.е. этот пример:

     $a = array('foo','bar','baz');
     echo "RESULT: ", json_encode($a, JSON_FORCE_OBJECT);

приведет к:

     RESULT: {"0" : "foo", "1": "bar", "2" : "baz"}
GigiManco
источник
0

Я столкнулся с этой проблемой в массиве с ключами '0' и ''. Это означало, что я не мог проверить свои ключи массива с помощью == или ===.

$array=array(''=>'empty', '0'=>'zero', '1'=>'one');
echo "Test 1\n";
foreach ($array as $key=>$value) {
    if ($key == '') { // Error - wrongly finds '0' as well
        echo "$value\n";
    }
}
echo "Test 2\n";
foreach ($array as $key=>$value) {
    if ($key === '0') { // Error - doesn't find '0'
        echo "$value\n";
    }
}

Обходной путь - преобразовать ключи массива обратно в строки перед использованием.

echo "Test 3\n";
foreach ($array as $key=>$value) {
    if ((string)$key == '') { // Cast back to string - fixes problem
        echo "$value\n";
    }
}
echo "Test 4\n";
foreach ($array as $key=>$value) {
    if ((string)$key === '0') { // Cast back to string - fixes problem
        echo "$value\n";
    }
}
fisharebest
источник
1
Это не совсем ответ на вопрос.
Flimm
0

Что касается решения @david, обратите внимание, что когда вы пытаетесь получить доступ к строковым значениям в ассоциативном массиве, числа не будут работать. Я предполагаю, что они приводятся к целым числам за кулисами (при доступе к массиву), и значение не найдено. Доступ к значениям в виде целых чисел тоже не сработает. Но вы можете использовать array_shift () для получения значений или итерации массива.

$data = new stdClass;
$data->{"0"} = "Zero";
$data->{"1"} = "One";
$data->{"A"} = "A";
$data->{"B"} = "B";

$data = (array)$data;

var_dump($data);
/*
Note the key "0" is correctly saved as a string:
array(3) {
  ["0"]=>
  string(4) "Zero"
  ["A"]=>
  string(1) "A"
  ["B"]=>
  string(1) "B"
}
*/

//Now let's access the associative array via the values 
//given from var_dump() above:
var_dump($data["0"]); // NULL -> Expected string(1) "0"
var_dump($data[0]); // NULL (as expected)
var_dump($data["1"]); // NULL -> Expected string(1) "1"
var_dump($data[1]); // NULL (as expected)
var_dump($data["A"]); // string(1) "A" (as expected)
var_dump($data["B"]); // string(1) "B" (as expected)
Нико Шефер
источник
Какая версия php? Я пробовал именно ваш пример и ключ "0"делается 0с php 7.2.10.
J.BizMai
-1

У меня возникла эта проблема при попытке отсортировать массив, где мне нужно, чтобы ключ сортировки был шестнадцатеричным sha1. Когда в результирующем значении sha1 нет букв, PHP превращает ключ в целое число. Но мне нужно было отсортировать массив по относительному порядку строк. Поэтому мне нужно было найти способ заставить ключ быть строкой без изменения порядка сортировки.

Глядя на диаграмму ASCII ( https://en.wikipedia.org/wiki/ASCII ), восклицательный знак сортируется примерно так же, как пробел, и определенно ниже, чем все числа и буквы.

Поэтому я добавил восклицательный знак в конце ключевой строки.

for(...) {

    $database[$sha.'!'] = array($sha,$name,$age);
}

ksort($database);
$row = reset($database);
$topsha = $row[0];
Drchuck
источник
Так вы говорите, что невозможно использовать числовую строку типа «123» в качестве ключа в массиве PHP без ее преобразования в целое число?
Flimm
Нет - он обрабатывает только ключ, состоящий из всех чисел, как целое число при сортировке массива с помощью ksort () - он никогда не преобразуется в целое число - просто сравнивается как один во время сортировки.
drchuck