PHP Arrays difference between [0] and ['0'] [duplicate] - php

Is it possible to use a numeric string like "123" as a key in a PHP array, without it being converted to an integer?
$blah = array('123' => 1);
var_dump($blah);
prints
array(1) {
[123]=>
int(1)
}
I want
array(1) {
["123"]=>
int(1)
}

No; no it's not:
From the manual:
A key may be either an integer or a string. If a key is the standard representation of an integer, it will be interpreted as such (i.e. "8" will be interpreted as 8, while "08" will be interpreted as "08").
Addendum
Because of the comments below, I thought it would be fun to point out that the behaviour is similar but not identical to JavaScript object keys.
foo = { '10' : 'bar' };
foo['10']; // "bar"
foo[10]; // "bar"
foo[012]; // "bar"
foo['012']; // undefined!

Yes, it is possible by array-casting an stdClass object:
$data = new stdClass;
$data->{"12"} = 37;
$data = (array) $data;
var_dump( $data );
That gives you (up to PHP version 7.1):
array(1) {
["12"]=>
int(37)
}
(Update: My original answer showed a more complicated way by using json_decode() and json_encode() which is not necessary.)
Note the comment: It's unfortunately not possible to reference the value directly: $data['12'] will result in a notice.
Update:
From PHP 7.2 on it is also possible to use a numeric string as key to reference the value:
var_dump( $data['12'] ); // int 32

My workaround is:
$id = 55;
$array = array(
" $id" => $value
);
The space char (prepend) is a good solution because keep the int conversion:
foreach( $array as $key => $value ) {
echo $key;
}
You'll see 55 as int.

If you need to use a numeric key in a php data structure, an object will work. And objects preserve order, so you can iterate.
$obj = new stdClass();
$key = '3';
$obj->$key = 'abc';

You can typecast the key to a string but it will eventually be converted to an integer due to PHP's loose-typing. See for yourself:
$x=array((string)123=>'abc');
var_dump($x);
$x[123]='def';
var_dump($x);
From the PHP manual:
A key may be either an integer or a string . If a key is the standard
representation of an integer , it will be interpreted as such (i.e.
"8" will be interpreted as 8, while "08" will be interpreted as "08").
Floats in key are truncated to integer . The indexed and associative
array types are the same type in PHP, which can both contain integer
and string indices.

I had this problem trying to merge arrays which had both string and integer keys. It was important that the integers would also be handled as string since these were names for input fields (as in shoe sizes etc,..)
When I used $data = array_merge($data, $extra); PHP would 're-order' the keys. In an attempt doing the ordering, the integer keys (I tried with 6 - '6'- "6" even (string)"6" as keys) got renamed from 0 to n ... If you think about it, in most cases this would be the desired behaviour.
You can work around this by using $data = $data + $extra; instead.
Pretty straight forward, but I didn't think of it at first ^^.

As workaround, you can encode PHP array into json object, with JSON_FORCE_OBJECT option.
i.e., This example:
$a = array('foo','bar','baz');
echo "RESULT: ", json_encode($a, JSON_FORCE_OBJECT);
will result in:
RESULT: {"0" : "foo", "1": "bar", "2" : "baz"}

I ran into this problem on an array with both '0' and '' as keys. It meant that I couldn't check my array keys with either == or ===.
$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";
}
}
The workaround is to cast the array keys back to strings before use.
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";
}
}

Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.
WRONG
I have a casting function which handles sequential to associative array casting,
$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
}

Regarding #david solution, please note that when you try to access the string values in the associative array, the numbers will not work. My guess is that they are casted to integers behind the scenes (when accessing the array) and no value is found. Accessing the values as integers won't work either. But you can use array_shift() to get the values or iterate the array.
$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)

I had this problem while trying to sort an array where I needed the sort key to be a hex sha1. When a resulting sha1 value has no letters, PHP turns the key into an integer. But I needed to sort the array on the relative order of the strings. So I needed to find a way to force the key to be a string without changing the sorting order.
Looking at the ASCII chart (https://en.wikipedia.org/wiki/ASCII) the exclamation point sorts just about the same as space and certainly lower than all numbers and letters.
So I appended an exclamation point at the end of the key string.
for(...) {
$database[$sha.'!'] = array($sha,$name,$age);
}
ksort($database);
$row = reset($database);
$topsha = $row[0];

Related

Convert PHP array key from integer to string value [duplicate]

Is it possible to use a numeric string like "123" as a key in a PHP array, without it being converted to an integer?
$blah = array('123' => 1);
var_dump($blah);
prints
array(1) {
[123]=>
int(1)
}
I want
array(1) {
["123"]=>
int(1)
}
No; no it's not:
From the manual:
A key may be either an integer or a string. If a key is the standard representation of an integer, it will be interpreted as such (i.e. "8" will be interpreted as 8, while "08" will be interpreted as "08").
Addendum
Because of the comments below, I thought it would be fun to point out that the behaviour is similar but not identical to JavaScript object keys.
foo = { '10' : 'bar' };
foo['10']; // "bar"
foo[10]; // "bar"
foo[012]; // "bar"
foo['012']; // undefined!
Yes, it is possible by array-casting an stdClass object:
$data = new stdClass;
$data->{"12"} = 37;
$data = (array) $data;
var_dump( $data );
That gives you (up to PHP version 7.1):
array(1) {
["12"]=>
int(37)
}
(Update: My original answer showed a more complicated way by using json_decode() and json_encode() which is not necessary.)
Note the comment: It's unfortunately not possible to reference the value directly: $data['12'] will result in a notice.
Update:
From PHP 7.2 on it is also possible to use a numeric string as key to reference the value:
var_dump( $data['12'] ); // int 32
My workaround is:
$id = 55;
$array = array(
" $id" => $value
);
The space char (prepend) is a good solution because keep the int conversion:
foreach( $array as $key => $value ) {
echo $key;
}
You'll see 55 as int.
If you need to use a numeric key in a php data structure, an object will work. And objects preserve order, so you can iterate.
$obj = new stdClass();
$key = '3';
$obj->$key = 'abc';
You can typecast the key to a string but it will eventually be converted to an integer due to PHP's loose-typing. See for yourself:
$x=array((string)123=>'abc');
var_dump($x);
$x[123]='def';
var_dump($x);
From the PHP manual:
A key may be either an integer or a string . If a key is the standard
representation of an integer , it will be interpreted as such (i.e.
"8" will be interpreted as 8, while "08" will be interpreted as "08").
Floats in key are truncated to integer . The indexed and associative
array types are the same type in PHP, which can both contain integer
and string indices.
I had this problem trying to merge arrays which had both string and integer keys. It was important that the integers would also be handled as string since these were names for input fields (as in shoe sizes etc,..)
When I used $data = array_merge($data, $extra); PHP would 're-order' the keys. In an attempt doing the ordering, the integer keys (I tried with 6 - '6'- "6" even (string)"6" as keys) got renamed from 0 to n ... If you think about it, in most cases this would be the desired behaviour.
You can work around this by using $data = $data + $extra; instead.
Pretty straight forward, but I didn't think of it at first ^^.
As workaround, you can encode PHP array into json object, with JSON_FORCE_OBJECT option.
i.e., This example:
$a = array('foo','bar','baz');
echo "RESULT: ", json_encode($a, JSON_FORCE_OBJECT);
will result in:
RESULT: {"0" : "foo", "1": "bar", "2" : "baz"}
I ran into this problem on an array with both '0' and '' as keys. It meant that I couldn't check my array keys with either == or ===.
$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";
}
}
The workaround is to cast the array keys back to strings before use.
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";
}
}
Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.
WRONG
I have a casting function which handles sequential to associative array casting,
$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
}
Regarding #david solution, please note that when you try to access the string values in the associative array, the numbers will not work. My guess is that they are casted to integers behind the scenes (when accessing the array) and no value is found. Accessing the values as integers won't work either. But you can use array_shift() to get the values or iterate the array.
$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)
I had this problem while trying to sort an array where I needed the sort key to be a hex sha1. When a resulting sha1 value has no letters, PHP turns the key into an integer. But I needed to sort the array on the relative order of the strings. So I needed to find a way to force the key to be a string without changing the sorting order.
Looking at the ASCII chart (https://en.wikipedia.org/wiki/ASCII) the exclamation point sorts just about the same as space and certainly lower than all numbers and letters.
So I appended an exclamation point at the end of the key string.
for(...) {
$database[$sha.'!'] = array($sha,$name,$age);
}
ksort($database);
$row = reset($database);
$topsha = $row[0];

Can not understand why count is not returning the length of my array ? [duplicate]

Is it possible to use a numeric string like "123" as a key in a PHP array, without it being converted to an integer?
$blah = array('123' => 1);
var_dump($blah);
prints
array(1) {
[123]=>
int(1)
}
I want
array(1) {
["123"]=>
int(1)
}
No; no it's not:
From the manual:
A key may be either an integer or a string. If a key is the standard representation of an integer, it will be interpreted as such (i.e. "8" will be interpreted as 8, while "08" will be interpreted as "08").
Addendum
Because of the comments below, I thought it would be fun to point out that the behaviour is similar but not identical to JavaScript object keys.
foo = { '10' : 'bar' };
foo['10']; // "bar"
foo[10]; // "bar"
foo[012]; // "bar"
foo['012']; // undefined!
Yes, it is possible by array-casting an stdClass object:
$data = new stdClass;
$data->{"12"} = 37;
$data = (array) $data;
var_dump( $data );
That gives you (up to PHP version 7.1):
array(1) {
["12"]=>
int(37)
}
(Update: My original answer showed a more complicated way by using json_decode() and json_encode() which is not necessary.)
Note the comment: It's unfortunately not possible to reference the value directly: $data['12'] will result in a notice.
Update:
From PHP 7.2 on it is also possible to use a numeric string as key to reference the value:
var_dump( $data['12'] ); // int 32
My workaround is:
$id = 55;
$array = array(
" $id" => $value
);
The space char (prepend) is a good solution because keep the int conversion:
foreach( $array as $key => $value ) {
echo $key;
}
You'll see 55 as int.
If you need to use a numeric key in a php data structure, an object will work. And objects preserve order, so you can iterate.
$obj = new stdClass();
$key = '3';
$obj->$key = 'abc';
You can typecast the key to a string but it will eventually be converted to an integer due to PHP's loose-typing. See for yourself:
$x=array((string)123=>'abc');
var_dump($x);
$x[123]='def';
var_dump($x);
From the PHP manual:
A key may be either an integer or a string . If a key is the standard
representation of an integer , it will be interpreted as such (i.e.
"8" will be interpreted as 8, while "08" will be interpreted as "08").
Floats in key are truncated to integer . The indexed and associative
array types are the same type in PHP, which can both contain integer
and string indices.
I had this problem trying to merge arrays which had both string and integer keys. It was important that the integers would also be handled as string since these were names for input fields (as in shoe sizes etc,..)
When I used $data = array_merge($data, $extra); PHP would 're-order' the keys. In an attempt doing the ordering, the integer keys (I tried with 6 - '6'- "6" even (string)"6" as keys) got renamed from 0 to n ... If you think about it, in most cases this would be the desired behaviour.
You can work around this by using $data = $data + $extra; instead.
Pretty straight forward, but I didn't think of it at first ^^.
As workaround, you can encode PHP array into json object, with JSON_FORCE_OBJECT option.
i.e., This example:
$a = array('foo','bar','baz');
echo "RESULT: ", json_encode($a, JSON_FORCE_OBJECT);
will result in:
RESULT: {"0" : "foo", "1": "bar", "2" : "baz"}
I ran into this problem on an array with both '0' and '' as keys. It meant that I couldn't check my array keys with either == or ===.
$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";
}
}
The workaround is to cast the array keys back to strings before use.
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";
}
}
Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.
WRONG
I have a casting function which handles sequential to associative array casting,
$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
}
Regarding #david solution, please note that when you try to access the string values in the associative array, the numbers will not work. My guess is that they are casted to integers behind the scenes (when accessing the array) and no value is found. Accessing the values as integers won't work either. But you can use array_shift() to get the values or iterate the array.
$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)
I had this problem while trying to sort an array where I needed the sort key to be a hex sha1. When a resulting sha1 value has no letters, PHP turns the key into an integer. But I needed to sort the array on the relative order of the strings. So I needed to find a way to force the key to be a string without changing the sorting order.
Looking at the ASCII chart (https://en.wikipedia.org/wiki/ASCII) the exclamation point sorts just about the same as space and certainly lower than all numbers and letters.
So I appended an exclamation point at the end of the key string.
for(...) {
$database[$sha.'!'] = array($sha,$name,$age);
}
ksort($database);
$row = reset($database);
$topsha = $row[0];

Why does array_count_values silently turns strings to int? [duplicate]

Is it possible to use a numeric string like "123" as a key in a PHP array, without it being converted to an integer?
$blah = array('123' => 1);
var_dump($blah);
prints
array(1) {
[123]=>
int(1)
}
I want
array(1) {
["123"]=>
int(1)
}
No; no it's not:
From the manual:
A key may be either an integer or a string. If a key is the standard representation of an integer, it will be interpreted as such (i.e. "8" will be interpreted as 8, while "08" will be interpreted as "08").
Addendum
Because of the comments below, I thought it would be fun to point out that the behaviour is similar but not identical to JavaScript object keys.
foo = { '10' : 'bar' };
foo['10']; // "bar"
foo[10]; // "bar"
foo[012]; // "bar"
foo['012']; // undefined!
Yes, it is possible by array-casting an stdClass object:
$data = new stdClass;
$data->{"12"} = 37;
$data = (array) $data;
var_dump( $data );
That gives you (up to PHP version 7.1):
array(1) {
["12"]=>
int(37)
}
(Update: My original answer showed a more complicated way by using json_decode() and json_encode() which is not necessary.)
Note the comment: It's unfortunately not possible to reference the value directly: $data['12'] will result in a notice.
Update:
From PHP 7.2 on it is also possible to use a numeric string as key to reference the value:
var_dump( $data['12'] ); // int 32
My workaround is:
$id = 55;
$array = array(
" $id" => $value
);
The space char (prepend) is a good solution because keep the int conversion:
foreach( $array as $key => $value ) {
echo $key;
}
You'll see 55 as int.
If you need to use a numeric key in a php data structure, an object will work. And objects preserve order, so you can iterate.
$obj = new stdClass();
$key = '3';
$obj->$key = 'abc';
You can typecast the key to a string but it will eventually be converted to an integer due to PHP's loose-typing. See for yourself:
$x=array((string)123=>'abc');
var_dump($x);
$x[123]='def';
var_dump($x);
From the PHP manual:
A key may be either an integer or a string . If a key is the standard
representation of an integer , it will be interpreted as such (i.e.
"8" will be interpreted as 8, while "08" will be interpreted as "08").
Floats in key are truncated to integer . The indexed and associative
array types are the same type in PHP, which can both contain integer
and string indices.
I had this problem trying to merge arrays which had both string and integer keys. It was important that the integers would also be handled as string since these were names for input fields (as in shoe sizes etc,..)
When I used $data = array_merge($data, $extra); PHP would 're-order' the keys. In an attempt doing the ordering, the integer keys (I tried with 6 - '6'- "6" even (string)"6" as keys) got renamed from 0 to n ... If you think about it, in most cases this would be the desired behaviour.
You can work around this by using $data = $data + $extra; instead.
Pretty straight forward, but I didn't think of it at first ^^.
As workaround, you can encode PHP array into json object, with JSON_FORCE_OBJECT option.
i.e., This example:
$a = array('foo','bar','baz');
echo "RESULT: ", json_encode($a, JSON_FORCE_OBJECT);
will result in:
RESULT: {"0" : "foo", "1": "bar", "2" : "baz"}
I ran into this problem on an array with both '0' and '' as keys. It meant that I couldn't check my array keys with either == or ===.
$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";
}
}
The workaround is to cast the array keys back to strings before use.
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";
}
}
Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.
WRONG
I have a casting function which handles sequential to associative array casting,
$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
}
Regarding #david solution, please note that when you try to access the string values in the associative array, the numbers will not work. My guess is that they are casted to integers behind the scenes (when accessing the array) and no value is found. Accessing the values as integers won't work either. But you can use array_shift() to get the values or iterate the array.
$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)
I had this problem while trying to sort an array where I needed the sort key to be a hex sha1. When a resulting sha1 value has no letters, PHP turns the key into an integer. But I needed to sort the array on the relative order of the strings. So I needed to find a way to force the key to be a string without changing the sorting order.
Looking at the ASCII chart (https://en.wikipedia.org/wiki/ASCII) the exclamation point sorts just about the same as space and certainly lower than all numbers and letters.
So I appended an exclamation point at the end of the key string.
for(...) {
$database[$sha.'!'] = array($sha,$name,$age);
}
ksort($database);
$row = reset($database);
$topsha = $row[0];

A numeric string as array key in PHP

Is it possible to use a numeric string like "123" as a key in a PHP array, without it being converted to an integer?
$blah = array('123' => 1);
var_dump($blah);
prints
array(1) {
[123]=>
int(1)
}
I want
array(1) {
["123"]=>
int(1)
}
No; no it's not:
From the manual:
A key may be either an integer or a string. If a key is the standard representation of an integer, it will be interpreted as such (i.e. "8" will be interpreted as 8, while "08" will be interpreted as "08").
Addendum
Because of the comments below, I thought it would be fun to point out that the behaviour is similar but not identical to JavaScript object keys.
foo = { '10' : 'bar' };
foo['10']; // "bar"
foo[10]; // "bar"
foo[012]; // "bar"
foo['012']; // undefined!
Yes, it is possible by array-casting an stdClass object:
$data = new stdClass;
$data->{"12"} = 37;
$data = (array) $data;
var_dump( $data );
That gives you (up to PHP version 7.1):
array(1) {
["12"]=>
int(37)
}
(Update: My original answer showed a more complicated way by using json_decode() and json_encode() which is not necessary.)
Note the comment: It's unfortunately not possible to reference the value directly: $data['12'] will result in a notice.
Update:
From PHP 7.2 on it is also possible to use a numeric string as key to reference the value:
var_dump( $data['12'] ); // int 32
My workaround is:
$id = 55;
$array = array(
" $id" => $value
);
The space char (prepend) is a good solution because keep the int conversion:
foreach( $array as $key => $value ) {
echo $key;
}
You'll see 55 as int.
If you need to use a numeric key in a php data structure, an object will work. And objects preserve order, so you can iterate.
$obj = new stdClass();
$key = '3';
$obj->$key = 'abc';
You can typecast the key to a string but it will eventually be converted to an integer due to PHP's loose-typing. See for yourself:
$x=array((string)123=>'abc');
var_dump($x);
$x[123]='def';
var_dump($x);
From the PHP manual:
A key may be either an integer or a string . If a key is the standard
representation of an integer , it will be interpreted as such (i.e.
"8" will be interpreted as 8, while "08" will be interpreted as "08").
Floats in key are truncated to integer . The indexed and associative
array types are the same type in PHP, which can both contain integer
and string indices.
I had this problem trying to merge arrays which had both string and integer keys. It was important that the integers would also be handled as string since these were names for input fields (as in shoe sizes etc,..)
When I used $data = array_merge($data, $extra); PHP would 're-order' the keys. In an attempt doing the ordering, the integer keys (I tried with 6 - '6'- "6" even (string)"6" as keys) got renamed from 0 to n ... If you think about it, in most cases this would be the desired behaviour.
You can work around this by using $data = $data + $extra; instead.
Pretty straight forward, but I didn't think of it at first ^^.
As workaround, you can encode PHP array into json object, with JSON_FORCE_OBJECT option.
i.e., This example:
$a = array('foo','bar','baz');
echo "RESULT: ", json_encode($a, JSON_FORCE_OBJECT);
will result in:
RESULT: {"0" : "foo", "1": "bar", "2" : "baz"}
I ran into this problem on an array with both '0' and '' as keys. It meant that I couldn't check my array keys with either == or ===.
$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";
}
}
The workaround is to cast the array keys back to strings before use.
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";
}
}
Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.
WRONG
I have a casting function which handles sequential to associative array casting,
$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
}
Regarding #david solution, please note that when you try to access the string values in the associative array, the numbers will not work. My guess is that they are casted to integers behind the scenes (when accessing the array) and no value is found. Accessing the values as integers won't work either. But you can use array_shift() to get the values or iterate the array.
$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)
I had this problem while trying to sort an array where I needed the sort key to be a hex sha1. When a resulting sha1 value has no letters, PHP turns the key into an integer. But I needed to sort the array on the relative order of the strings. So I needed to find a way to force the key to be a string without changing the sorting order.
Looking at the ASCII chart (https://en.wikipedia.org/wiki/ASCII) the exclamation point sorts just about the same as space and certainly lower than all numbers and letters.
So I appended an exclamation point at the end of the key string.
for(...) {
$database[$sha.'!'] = array($sha,$name,$age);
}
ksort($database);
$row = reset($database);
$topsha = $row[0];

How can I force PHP to use strings for array keys? [duplicate]

This question already has answers here:
A numeric string as array key in PHP
(11 answers)
Closed 2 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
I've come across an old app that uses an id to name type array, for example...
array(1) {
[280]=>
string(3) "abc"
}
Now I need to reorder these, and a var_dump() would make it appear that that isn't going to happen while the keys are integers.
If I add an a to every index, var_dump() will show double quotes around the key, my guess to show it is now a string...
array(1) {
["280a"]=>
string(3) "abc"
}
This would let me easily reorder them, without having to touch more code.
This does not work.
$newArray = array();
foreach($array as $key => $value) {
$newArray[(string) $key] = $value;
}
A var_dump() still shows them as integer array indexes.
Is there a way to force the keys to be strings, so I can reorder them without ruining the array?
YOU CAN'T!!
Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.
Edit:
ACTUALLY YOU CAN!!
Cast sequential array to associative array
$obj = new stdClass;
foreach($array as $key => $value){
$obj->{$key} = $value;
}
$array = (array) $obj;
In most cases, the following quote is true:
Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.
This examples from the PHP Docs
<?php
$array = array(
1 => "a",
"1" => "b",
1.5 => "c",
true => "d",
);
var_dump($array);
?>
The above example will output:
array(1) {
[1]=> string(1) "d"
}
So even if you were to create an array with numbered keys they would just get casted back to integers.
Unfortunately for me I was not aware of this until recently but I thought I would share my failed attempts.
Failed attempts
$arr = array_​change_​key_​case($arr); // worth a try.
Returns an array with all keys from array lowercased or uppercased. Numbered indices are left as is.
My next attempts was to create a new array by array_combineing the old values the new (string)keys.
I tried several ways of making the $keys array contain numeric values of type string.
range("A", "Z" ) works for the alphabet so I though I would try it with a numeric string.
$keys = range("0", (string) count($arr) ); // integers
This resulted in an array full of keys but were all of int type.
Here's a couple of successful attempts of creating an array with the values of type string.
$keys = explode(',', implode(",", array_keys($arr))); // values strings
$keys = array_map('strval', array_keys($arr)); // values strings
Now just to combine the two.
$arr = array_combine( $keys, $arr);
This is when I discovered numeric strings are casted to integers.
$arr = array_combine( $keys, $arr); // int strings
//assert($arr === array_values($arr)) // true.
The only way to change the keys to strings and maintain their literal values would be to prefix the key with a suffix it with a decimal point "00","01","02" or "0.","1.","2.".
You can achieve this like so.
$keys = explode(',', implode(".,", array_keys($arr)) . '.'); // added decimal point
$arr = array_combine($keys, $arr);
Of course this is less than ideal as you will need to target array elements like this.
$arr["280."]
I've created a little function which will target the correct array element even if you only enter the integer and not the new string.
function array_value($array, $key){
if(array_key_exists($key, $array)){
return $array[ $key ];
}
if(is_numeric($key) && array_key_exists('.' . $key, $array)){
return $array[ '.' . $key ];
}
return null;
}
Usage
echo array_value($array, "208"); // "abc"
Edit:
ACTUALLY YOU CAN!!
Cast sequential array to associative array
All that for nothing
You can append the null character "\0" to the end of the array key. This makes it so PHP can't interpret the string as an integer. All of the array functions (like array_merge()) work on it. Also not even var_dump() will show anything extra after the string of integers.
Example:
$numbers1 = array();
$numbers2 = array();
$numbers = array();
$pool1 = array(111, 222, 333, 444);
$pool2 = array(555, 666, 777, 888);
foreach($pool1 as $p1)
{
$numbers1[$p1 . "\0"] = $p1;
}
foreach($pool2 as $p2)
{
$numbers2[$p2 . "\0"] = $p2;
}
$numbers = array_merge($numbers1, $numbers2);
var_dump($numbers);
The resulting output will be:
array(8) {
["111"] => string(3) "111"
["222"] => string(3) "222"
["333"] => string(3) "333"
["444"] => string(3) "444"
["555"] => string(3) "555"
["666"] => string(3) "666"
["777"] => string(3) "777"
["888"] => string(3) "888"
}
Without the . "\0" part the resulting array would be:
array(8) {
[0] => string(3) "111"
[1] => string(3) "222"
[2] => string(3) "333"
[3] => string(3) "444"
[4] => string(3) "555"
[5] => string(3) "666"
[6] => string(3) "777"
[7] => string(3) "888"
}
Also ksort() will also ignore the null character meaning $numbers[111] and $numbers["111\0"] will both have the same weight in the sorting algorithm.
The only downside to this method is that to access, for example $numbers["444"], you would actually have to access it via $numbers["444\0"] and since not even var_dump() will show you there's a null character at the end, there's no clue as to why you get "Undefined offset". So only use this hack if iterating via a foreach() or whoever ends up maintaining your code will hate you.
Use an object instead of an array $object = (object)$array;
EDIT:
I assumed that if they are integers, I
can't reorder them without changing
the key (which is significant in this
example). However, if they were
strings, I can reorder them how they
like as the index shouldn't be
interpreted to have any special
meaning. Anyway, see my question
update for how I did it (I went down a
different route).
Actually they dont have to be in numeric order...
array(208=>'a', 0=> 'b', 99=>'c');
Is perfectly valid if youre assigning them manually. Though i agree the integer keys might be misinterpreted as having a sequential meaning by someone although you would think if they were in a non-numeric order it would be evident they werent. That said i think since you had the leeway to change the code as you updated that is the better approach.
Probably not the most efficient way but easy as pie:
$keys = array_keys($data);
$values = array_values($data);
$stringKeys = array_map('strval', $keys);
$data = array_combine($stringKeys, $values);
//sort your data
I was able to get this to work by adding '.0' onto the end of each key, as such:
$options = [];
for ($i = 1; $i <= 4; $i++) {
$options[$i.'.0'] = $i;
}
Will return:
array("1.0" => 1, "2.0" => 2, "3.0" => 3, "4.0" => 4)
It may not be completely optimal but it does allow you to sort the array and extract (an equivalent of) the original key without having to truncate anything.
Edit:
This should work
foreach($array as $key => $value) {
$newkey = sprintf('%s',$key);
$newArray["'$newkey'"] = $value;
}
Hi we can make the index of the array a string using the following way. If we convert an array to xml then indexes like [0] may create issue so convert to string like [sample_0]
$newArray = array();
foreach($array as $key => $value) {
$newArray["sample_".$key] = $value;
}
All other answers thus far are hacks that either use fragile workarounds that could break between major PHP versions, create unnecessary gotchas by deliberately corrupting keys, or just slow down your code for no benefit. The various functions to sort arrays yet maintain the crucial key associations have existed since PHP 4.
It is pointless stop PHP from using integer keys, it only does so when the integer representation is exactly the same as the string, thus casting an integer key back to string when reading from the array is guaranteed to return the original data. PHP's internal representation of your data is completely irrelevant as long as you avoid the functions that rewrite integer keys. The docs clearly state which array functions will do that.
An example of sorting, without any hacks, that demonstrates how data remains uncorrupted:
<?php
# use string keys to define as populating from a db, etc. would,
# even though PHP will convert the keys to integers
$in = array(
'347' => 'ghi',
'176' => 'def',
'280' => 'abc',
);
# sort by key
ksort($in);
echo "K:\n";
$i = 1;
foreach ($in as $k => $v) {
echo $i++, "\n";
$k = (string) $k; # convert back to original
var_dump($k, $v);
}
# sort by value
asort($in, SORT_STRING);
echo "\nV:\n";
$i = 1;
foreach ($in as $k => $v) {
echo $i++, "\n";
$k = (string) $k;
var_dump($k, $v);
}
# unnecessary to cast as object unless keys could be sequential, gapless, and start with 0
if (function_exists('json_encode')) {
echo "\nJSON:\n", json_encode($in);
}
The output it produces hasn't changed since v5.2 (with only the JSON missing prior to that):
K:
1
string(3) "176"
string(3) "def"
2
string(3) "280"
string(3) "abc"
3
string(3) "347"
string(3) "ghi"
V:
1
string(3) "280"
string(3) "abc"
2
string(3) "176"
string(3) "def"
3
string(3) "347"
string(3) "ghi"
JSON:
{"280":"abc","176":"def","347":"ghi"}

Categories