PHP.
$a['0']=1;
$a[0]=2;
Which is proper form?
In the first example you use a string to index the array which will be a hashtable "under the hood" which is slower. To access the value a "number" is computed from the string to locate the value you stored. This calculation takes time.
The second example is an array based on numbers which is faster. Arrays that use numbers will index the array according to that number. 0 is index 0; 1 is index 1. That is a very efficient way of accessing an array. No complex calculations are needed. The index is just an offset from the start of the array to access the value.
If you only use numbers, then you should use numbers, not strings. It's not a question of form, it's a question of how PHP will optimize your code. Numbers are faster.
However the speed differences are negligible when dealing with small sizes (arrays storing less than <10,000 elements; Thanks Paolo ;)
In the first you would have an array item:
Key: 0
Index: 0
In the second example, you have only an index set.
Index: 0
$arr = array();
$arr['Hello'] = 'World';
$arr['YoYo'] = 'Whazzap';
$arr[2] = 'No key'; // Index 2
The "funny" thing is, you will get exactly the same result.
PHP (for whatever reason) tests whether a string used as array index contains only digits. If it does the string is converted to int or double.
<?php
$x=array(); $x['0'] = 'foo';
var_dump($x);
$x=array(); $x[0] = 'foo';
var_dump($x);
For both arrays you get [0] => foo, not ["0"] => foo.
Or another test:<?php
$x = array();
$x[0] = 'a';
$x['1'] = 'b';
$x['01'] = 'c';
$x['foo'] = 'd';
foreach( $x as $k=>$v ) {
echo $k, ' ', gettype($k), "\n";
}0 integer
1 integer
01 string
foo string
If you still don't believe it take a look at #define HANDLE_NUMERIC(key, length, func) in zend_hash.h and when and where it is used.
You think that's weird? Pick a number and get in line...
If you plan to increment your keys use the second option. The first one is an associative array which contains the string "0" as the key.
They are both "proper" but have the different side effects as noted by others.
One other thing I'd point out, if you are just pushing items on to an array, you might prefer this syntax:
$a = array();
$a[] = 1;
$a[] = 2;
// now $a[0] is 1 and $a[1] is 2.
they are both good, they will both work.
the difference is that on the first, you set the value 1 to a key called '0'
on the second example, you set the value 2 on the first element in the array.
do not mix them up accidentally ;)
Related
I'm trying to create a random number generator in PHP. It's supposed to generate three (3) numbers at a time, without repeat. That's to say, the 3 numbers cannot be the same.
Here's what I've tried so far:
$array = [];
$A = mt_rand(1,36);
$array[0] = $A;
$B = mt_rand(1,36);
$array[1] = $B;
if(in_array($B,$array)){
$B = mt_rand(1,36);
$array[1] = $B;
}
$C = mt_rand(1,36);
$array[2] = $C;
if(in_array($C,$array)){
$C = mt_rand(1,36);
$array[2] = $C;
}
$length = count($array);
//display the array values;
for($i = 0; $i < $length; $i++){
echo ($array[$i]."<br>");
}
Can anyone tell me where I'm going wrong?
Like this ( as per my initial comment ),
$array = [];
while( count($array) < 3 ){
$rand = mt_rand(1,36);
$array[$rand] = $rand;
}
print_r( $array );
By setting the "key" to be the random number, we can abuse the fact that associative array keys are unique. Then it's a simple matter of waiting until the array contains the desired amount of unique items.
You can test it here
Outputs: ( your results may vary, it's random )
Array
(
[16] => 16
[20] => 20
[27] => 27
)
UPDATE I was trying to think of a valid way to do it without using a loop ( on my way home from work ), and this way may be even better in some cases.
$a = range(1,36);
shuffle($a);
$array = array_slice($a, 0, 3);
print_r($array);
This will have better performance when the number of items you must find is higher. This is because there is no repetition, no collisions. So if you have a small range but need to find many items for the return, this will preform better. If you have many items and need to return only few, then the first one may be better, if not from speed then from memory use.
You can see it here
For reference this uses
range() - Create an array containing a range of elements.
http://php.net/manual/en/function.range.php
shuffle() - Shuffles (randomizes the order of the elements in) an array. It uses a pseudo random number generator that is not suitable for cryptographic purposes.
http://php.net/manual/en/function.shuffle.php
array_slice() - Returns the sequence of elements from the array as specified by the offset and length parameters.
http://php.net/manual/en/function.array-slice.php
So to explain this last one
First we create an array that contains each of our possible numbers as an element. So for example like this [1,2,3,4,5,6, ...].
Next we shuffle it which randomizes the order of the whole array. Shuffle modifies the array by "reference" so it doesn't return our array and therefor there is no assignment ( I think it returns Boolean, however I'm at a loss as to how it could fail and return false, pretty much it just returns true which we don't want to overwrite our array with ). So our example then becomes this [16,20,27,14,5,1, ...]
Last we cut out the number of items we need to return. Finally we end the example with this [16,20,27];
You can crunch the first one down into one ( really 2) line by assigning the value of the $rand variable in the condition of the loop. Like this:
$array = [];
while( count($array) < 3 && false !== ($rand = mt_rand(1,36))) $array[$rand] = $rand;
Because mt_rand(1,36) will never return boolan false. Also if I remember mt_rand is the same as rand now, or at least in current PHP versions.
Note: As of PHP 7.1.0, rand() uses the same random number generator as mt_rand(). To preserve backwards compatibility rand() allows max to be smaller than min as opposed to returning FALSE as mt_rand(). http://php.net/manual/en/function.rand.php
Hope it helps you, remember to think outside of the box.
Can someone explain the output of this code?
Why is it "fb" instead of "100100"?
$items = array();
$items[] = "foo";
$items[] = "bar";
foreach($items as $item) {
$item['points'] = 100;
}
foreach($items as $item) {
echo $item['points']; //output: "fb"
}
You loop though the $items array, which has two elements.
First: foo and second: bar. E.g.
Array (
[0] => foo
[1] => bar
)
Now you access them like this:
echo $item['points'];
PHP will convert points which is a string into an integer, as you can see from the manual warning:
Warning: [...] Non-integer types are converted to integer. [...]
Which in your case will be 0.
And so you access the two values (strings) as array:
string: f o o
index: 0 1 2 //$index["points"] -> $index[0]
string: b a r
index: 0 1 2 //$index["points"] -> $index[0]
So you print the first character of both strings (e.g. foo and bar), which are:
fb
EDIT:
Also worth to note here is, that PHP only silently converts it with PHP <5.4 from newer version you will get a warning, as from the manual:
As of PHP 5.4 string offsets have to either be integers or integer-like strings, otherwise a warning will be thrown. Previously an offset like "foo" was silently cast to 0.
Which in your case with PHP >=5.4 you would get:
Warning: Illegal string offset 'points' ...
I found this question intriguing.
I had my own walk-through and here is the result.
$items is defined as follows.
$items = [
0 => "foo",
1 => "bar"
];
Then, goes into the foreach loop.
foreach($items as $item) {
$item['points'] = 100;
}
At the beginning, $item contains a string "foo". The [] syntax is dominantly used for associative arrays, so it tricks us that $item might be an array, which is not the case. A less well-known usage of the [] is to get/set a single character in a string via [int] or {int} expression, as #Rizier123 has noted in his answer. For example, a "string"[0] gives "s". So, the following code
$item['points'] = 100;
is virtually similar to
"foo"['points'] = 100;
Now, a non-integer value given as a character position of a string, raises a PHP warning, and the position (here 'points') will be force-converted to an integer.
// Converting a string to integer:
echo intval('points'); // gives 0
As a result, the "foo"['points']" statement becomes "foo"[0], so
"foo"[0] = 100;
Now, the assignment part. The [] syntax operates on a single character. The numeric 100 is first converted to a string "100" and then only the first character is taken out for the assignment operation(=). The expression is now similar to
"foo"[0] = "1"; // result: "1oo"
To make things a bit twisted, the modified value of $item( which is "1oo") is not preserved. It's because the $item is not a reference. See https://stackoverflow.com/a/9920684/760211 for more information.
So, all the previous operations are negligible in terms of the end result. The $items are intact in the original state.
Now, in the last loop, we can see that the $item['point'] statement tries to read a character out of a string, in an erroneous way.
foreach($items as $item) {
echo $item['points']; //output: "fb"
}
echo "foo"[0]; // "f"
echo "boo"[0]; // "b"
You're not actually modifying the array by doing $items as $item. $item is its own variable, so it would make sense that you get the correct output when printing within that loop.
How do iterate through a deserialized json string? I can't get the right number of values right now it doesn't count right.
my $list = request("http://localhost/getjson.php");
my $deserialize = from_json( $list );
print Dumper($deserialize);
$VAR1 = [
'ab',
'cc',
'de',
'aer',
'ffe',
'cer',
'dad',
'efef',
'afaf',
'ege',
'grsc',
'cegg',
'cegg',
'cegg/aaa.html',
'eggt',
'ttt'
];
print length($deserialize);
13 ?? it should say 16
You are getting back an array reference not an array. You need to dereference the value.
my #array = #$deserialize; # or #{ $deserialize }
print scalar #array;
Moreover, if you want to iterate over the array you can just use for
for (#$deserialize) {
# do stuff
You are actually working with reference to the results. Since JSON can contain all sorts of different results, decode_json won't return a list specifically.
So you need to dereference the variable that you have: $deserialize
Additionally, you don't really want to be using the length function. If you print the integer value (or scalar value) of an array, it will return it's size.
So here's what you want:
my $list = request("http://localhost/getjson.php");
my $deserialize = from_json( $list );
print scalar (#{$deserialize});
That will print the size of the array.
If you want to just start by working with an array you can do:
my $list = request("http://localhost/getjson.php");
my $deserialize = from_json( $list );
my #json_array = #{$deserialize});
print scalar (#json_array);
From perldoc -f length:
This function cannot be used on an entire array or hash to find
out how many elements these have. For that, use "scalar
#array" and "scalar keys %hash", respectively.
You cannot use length to find out the size of an array. To do that, use the advice above.
Your bug gives you a false value because you are taking the length of the array reference, which in string context will be something like ARRAY(0x22d0a88), which in your case seemed to be 13 characters long. E.g. the equivalent of:
print length "ARRAY(0x22d0a88)";
As a curious side note, if you would do length(#array), it will actually return the length of the length of the array. E.g. an array of size 16 would return 2, because the string "16" is two characters long.
I've been given a datafile where the original creator used alphabetical rather than numeric values to show order.
For example, if there's ten items, they'd be named:
12342313A
12342313B
12342313C
12342313D
12342313E
...
I need to import these values into a mySQL table that has order as a required int column, and I need to convert the letter to a number.
Is there a function in PHP to get a numeric value for a letter? Or will I need to do a substr to grab the trailing letter, and create an indexed array of letters and just do a lookup against that array?
I'm hesitant to do the simple way above, since I don't know how many objects could potentially exist, and I could need to write an array from A-AAAA or something.
Try converting it from base 36 to base 10 using base_convert(), I.e. base_convert($str, 36, 10). You might need to strtolower it first, and it'll only work if its not case sensitive.
PHP has a simple way to create that array, so you could write a function to figure all that out for you and do something like:
function str_to_num($letters, $max = 'ZZZZZZ') {
$count = 0;
for ($i = 'A'; $i < $max; $i++) {
$count++;
if ($letters == $i)
return $count;
}
}
Then you could do the substr, find the letters at the end, and then pass it into the function:
str_to_num('A'); // returns 1
str_to_num('AB'); // returns 28
str_to_num('AC'); // returns 29
str_to_num('ABC'); // returns 731
Something like that, anyway.
Good luck.
Assuming this is a one-time problem that you've got to correct and won't encounter moving forward, I suggest you use sort to... erm, sort out the problem. Let's say you have all those alpha-numeric order fields in an array, like so:
$vals = array (
'12342313A',
'12342313D',
'12342313E',
'12342313B',
'12342313C'
);
Those are all mixed up, not in order. But, you can call the function sort (docs) on that array and PHP does a decent job of making sense out of it:
print '<pre>Unsorted: ';
print_r($vals);
print '</pre>';
sort($vals);
print '<pre>Sorted: ';
print_r($vals);
print '</pre>';
/*
Unsorted: Array
(
[0] => 12342313A
[1] => 12342313D
[2] => 12342313E
[3] => 12342313B
[4] => 12342313C
)
Sorted: Array
(
[0] => 12342313A
[1] => 12342313B
[2] => 12342313C
[3] => 12342313D
[4] => 12342313E
)
*/
So far, so good. Now, you've got them ordered, and as a bonus you can use the index of the array as your new field in the database. Alter the table and add a field to hold the new value; we'll call this field numeric_order, and in my sample I've called the field that currently holds the alpha-numeric sort data string_order. Loop your sorted array and update the database (for example):
foreach ($vals as $x=>$v) {
$sql = 'UPDATE myTable SET numeric_order = '.($x+1).' WHERE string_order = "'.$v.'"';
}
I add 1 to x in the loop based on the assumption that you don't want anything to have 0 for the order - if that isn't a concern, then you can just use x. This is also predicated on the assumption that no two rows have the same alpha-numeric sort value.
If they do, then all is not lost! Start with your array looking like this:
$vals = array (
3=>'12342313A',
15=>'12342313D',
66=>'12342313E',
101=>'12342313B',
200=>'12342313C'
);
... the numeric keys would represent the unique/primary key of the corresponding row. Instead of sort, which does not preserve keys, use asort (which does preserve keys - docs), and then your loop looks like this:
$ord = 1
foreach ($vals as $x=>$v) {
$sql = 'UPDATE myTable SET numeric_order = '.$ord.' WHERE id = "'.$x.'"';
$ord++;
}
If my base assumption is wrong, and you'll continue to deal with this method of ordering rows, then in my humble view you ought to re-consider your data design.
use ord() with substr and subtract 64. This will set A to 1, B to 2, etc...
From what you have above, it seems like your values (last digit, at least) can be thought as being hex numbers. You can then transform them into decimal numbers through the hexdec function.
http://php.net/manual/en/function.hexdec.php
Update: My original intention for this question was to determine if PHP actually has this feature. This has been lost in the answers' focus on the scalar issue. Please see this new question instead: "Does PHP have autovivification?" This question is left here for reference.
According to Wikipedia, PHP doesn't have autovivification, yet this code works:
$test['a']['b'] = 1;
$test['a']['c'] = 1;
$test['b']['b'] = 1;
$test['b']['c'] = 1;
var_dump($test);
Output:
array
'a' =>
array
'b' => int 1
'c' => int 1
'b' =>
array
'b' => int 1
'c' => int 1
I found that this code works too:
$test['a'][4] = 1;
$test['b'][4]['f'] = 3;
But adding this line causes an warning ("Warning: Cannot use a scalar value as an array")
$test['a'][4]['f'] = 3;
What's going on here? Why does it fail when I add the associative element after the index? Is this 'true' Perl-like autovivification, or some variation of it, or something else?
Edit: oh, I see the error with the scalar now, oops! These work as expected:
$test['a'][4]['a'] = 1;
$test['a'][4]['b'] = 2;
$test['a'][5]['c'] = 3;
$test['a'][8]['d'] = 4;
So, php does have autovivification? Searching Google for "php autovivification" doesn't bring up a canonical answer or example of it.
From the PHP manual on the square bracket syntax:
$arr[] = value;
If $arr doesn't exist yet, it will be created, so this is also an alternative way to create an array
With your example:
$test['a'][4] = 1;
Since $test and $test['a'] don't currently exist; they are both created as arrays.
$test['b'][4]['f'] = 3;
$test['b'] and $test['b'][4] don't currently exist; they are both created as arrays.
$test['a'][4]['f'] = 3;
$test['a'][4] does exist, but it is an integer (1). This is the "scalar value" that cannot be used as an array. You can't use the square bracket [] syntax on number values; it doesn't convert an existing value to an array.
According to the results, PHP has autovivification. The error comes from the way it works.
When you say: $a[1][2] = 55, PHP wants to insert the 55 into $a[1] as [2]=>55. Since the $a[1] is non-existent, PHP is creating automatically an empty node, cca. $a[1] = Array(). But when the node already exists, PHP does not create $a[1], just performs the [2]=>55, which is an error, if $a[1] is not array-like (array, object).
The last language I've seen, where nodes may have value and children too, is MUMPS. There were also a function, called $DATA(), which told wheter a node has any child (10), value (1) or both (11), or it's non-existent (0). I think, that's the correct handling of associative arrays.
(Anyway, I like this behavior of PHP.)
With:
$test['b'][4]['f'] = 3;
You're not adding an element to
$test['a'][4]
bacause it's not initialized as an array.
If you'd write:
$test['a'][4] = array(1);
Then it would work.
With:
$test['a']['b'] = 1;
$test['a']['c'] = 1;
$test['b']['b'] = 1;
$test['b']['c'] = 1;
You're implicitly initializing $test['a'] and $test['b'] as an array. But $test['a']['b'] (and so on) as an int