Числовий рядок як ключ масиву в 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

8
Ще один приклад, коли числові рядки як клавіші масиву є проблематичними:asort
сунедо


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
Флімм

1
Як?! .. foo [012] повернення "бар"
Хіманшу

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

(Update: Мій початковий відповідь показав більш складний шлях, використовуючи 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 ($ дані), він вийде з
ладу

2
У PHP 7.2.0RC2 поведінка така ж, як і раніше.
dev0

1
Мабуть, array_key_existsйого не знайдете і в старих версіях.
Не панікуйте

1
не працює в php 7.4
жасмини

12

Якщо вам потрібно використовувати цифровий ключ у структурі даних php, об’єкт буде працювати. А об’єкти зберігають порядок, тому ви можете повторити.

$obj = new stdClass();
$key = '3';
$obj->$key = 'abc';

Це дуже гарна пропозиція. Я пишу рамковий код і стикаюся з тим, що хтось передає масив, який може мати або "випадкову" індексацію: array ("це", "те"), або "асоціативне" індексування: array (123 => масив ("це", "що ')). Тепер, завдяки вам, я можу просто набрати текст;) +1
Just Plain High

Але чи можна використовувати числовий рядок типу "123" як ключ у масиві PHP, не перетворюючи його на ціле число?
Flimm

@Flimm ні, що це неможливо, саме тому я пропоную своє рішення.
парі

@Flimm ця відповідь, здається, суперечить керівництву PHP : Рядки, що містять дійсні цілі числа, будуть передані цілому типу. Наприклад, ключ "8" буде фактично зберігатися під 8. З іншого боку, "08" не буде передано, оскільки це не є дійсним десятковим цілим числом. , хоча я не перевіряв його відповіді.
парі

Це речення знаходиться в розділі "Вказівка ​​за допомогою array(), тому я припускаю, що в цьому контексті рядки із зазначенням дійсних цілих чисел будуть передані цілому типу. Але виявляється, є й інші способи створення масивів, де цього не відбувається, наприклад, у відповіді Девіда, яку я перевірив.
Flimm

10

Моє вирішення:

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

Простір char (препендія) є хорошим рішенням, оскільки зберігайте перетворення int:

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

Ви побачите 55 як int.


4
Або "0$id" => $value. Припереджуючись до 0робіт теж.
nawfal

Отже, ви говорите, що не можна використовувати числовий рядок типу "123" як ключ у масиві PHP, не перетворюючи його на ціле число?
Флім

5

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

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

З посібника PHP:

Ключем може бути ціле число або рядок. Якщо ключовим є стандартне представлення цілого числа, воно буде інтерпретуватися як таке (тобто "8" буде інтерпретовано як 8, тоді як "08" буде інтерпретовано як "08"). Поплавці в ключі усічені до цілого числа. Індексований та асоціативний типи масивів є одним і тим же типом у PHP, який може містити як цілі, так і рядкові індекси.


1
Перетворення не відбувається через слабке введення тексту; php визначає, чи виглядає рядок числовим, а потім перетворює його.
Як

1
Так ви кажете, що це неможливо? Ця відповідь показує, що існує спосіб використовувати рядок, який виглядає як ціле число як ключ у масиві.
Flimm

IMHO проблема полягає в інтерпретації PHP. Неможливо навіть уявити, що є мова, яка змішує рядки та цілі числа у вигляді ключів масиву. Найкраще рішення? У відповідності з пропозицією Undolog stackoverflow.com/a/15413637/1977778 кращим рішенням є використання трейлинг простору ... До жаль.
сентенца

@sentenza: Це є можна уявити, тим більше , що PHP дозволяє суміш рядків і цілих чисел в якості ключів масиву:[42 => 'answer', 'snafu' => 'fubar']
LS

@LS так. Я знаю, що PHP дозволяє вам це зробити, але якщо врахувати ключі в гіпотетичній системі загального типу, вона не відповідає жодному змішаному типу, який одночасно містить рядки та числа. Пухке типування застосовується до ключів асоціативного масиву просто помилки.
сентенція

1

У мене була ця проблема, намагаючись об'єднати масиви, у яких були і рядкові, і цілі ключі. Важливо, щоб цілі числа також оброблялися як рядки, оскільки це назви полів введення (як у розмірах взуття тощо).

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

Ви можете обійти це, використовуючи $data = $data + $extra;натомість. Досить прямо, але я не думав про це спочатку ^^.


Точно така ж проблема привела мене до цієї сторінки, але я мушу сказати, що це не відповідь на питання ОП.
Flimm

@Flimm True. Але пошук відповіді привів мене до цієї сторінки. Я вважав, що моє рішення може допомогти іншим
Googlers

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
}

не працює в php 7.4
жасмини

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

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

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.
Дж.Бізмай

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

Отже, ти кажеш, що не можна використовувати числовий рядок типу "123" як ключ у масиві PHP, не перетворюючи його на ціле число?
Flimm

Ні - він розглядає лише ключ, який є всіма числами, як ціле число при сортуванні масиву за допомогою ksort () - він ніколи не перетворюється на ціле число - просто порівнюється як один під час сортування.
drchuck
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.