I just saw this on php.net description of how key to value mapping works:
$switching = array( 10, // key = 0
5 => 6,
3 => 7,
'a' => 4,
11, // key = 6 (maximum of integer-indices was 5)
'8' => 2, // key = 8 (integer!)
'02' => 77, // key = '02'
0 => 12 // the value 10 will be overwritten by 12
);
I just cant quite understand how 11 could be assigned key 6. I know 5 is not possible since it is already used on the second element as a key so it makes sense to jump it over.
But should not be 11 intuitively assigned key 4 in the first place since the first element of the array 10 is assigned key 0 and therefore the key value is incremented 0..1..2..3..4 from that point according to the first index unless specified otherwise (e.g 5=>6 could have had key 1, 3=>7 with key 2, and 'a'=> 4 could have had key 3 if not specified)? And also, why does it say that 11 should be assigned a key that represents the maximum integer of indices (in this case 6 since 5 was used already)?
Would appreciate any help/clarification. Please let me know if the question needs to be clarified. Thanks much.
It is implemented in this way just because it is the most performant solution - to just use maximum_specified_key + 1, rather than to find a hole in enumration
I believe it has to do with the way that PHP arrays work. Each has an internal cursor for the each, current, next, pos and similar methods. My guess: as a new key is specified, if it is higher than the current cursor, the cursor is advanced to that position so that anything added after that point will still be at current position + 1.
And also, why does it say that 11
should be assigned a key that
represents the maximum integer of
indices (in this case 6 since 5 was
used already)?
Why not? As zerkms says, it's performant, so there's that.
It's also more or less what you might expect. Given an array with mixed keys like that, what would you expect array_push() to do?
Of course, if you're running into this kind of thing in real life, it's probably time to stop and consider some amount of refactoring. Arrays in PHP are very flexible, and these somewhat arbitrary decisions had to be made. They're documented. The only alternative is to make array usage much more rigid.
The PHP manual gives you the answer in the link you provided:
As mentioned above, if no key is specified, the maximum of the
existing integer indices is taken, and the new key will be that
maximum value plus 1 (but at least 0). If no integer indices exist
yet, the key will be 0 (zero).
The code:
$switching = array( 10, // key = 0
5 => 6,
3 => 7,
'a' => 4,
11, // key = 6 (maximum of integer-indices was 5)
'8' => 2, // key = 8 (integer!)
'02' => 77, // key = '02'
0 => 12 // the value 10 will be overwritten by 12
);
So far the maximum key when you get to 'a' is 5 so the next available key according to the php manual would be 5 + 1 i.e. 6.
Note that the maximum integer key used
for this need not currently exist in the array. It need only have
existed in the array at some time since the last time the array was
re-indexed.
// empty the array;
foreach( $switching as $key => $value ) {
unset( $switching[$key] );
}
// refill it with new elements
for( $i = 0; $i < 10; $i++ ) {
$switching[] = $i + 1;
}
output array:
Array
(
[9] => 1
[10] => 2
[11] => 3
[12] => 4
[13] => 5
[14] => 6
[15] => 7
[16] => 8
[17] => 9
[18] => 10
)
Related
So I'm learning Php, so as I was messing around with arrays to see how they work, I stumbled into this when I made two arrays.
$TestArray1 = array( 1 => 1, "string" => "string", 24, "other", 2 => 6, 8);
$TestArray2 = array( 6 => 1, "string" => "string", 24, "other", 1 => 6, 8);
But when I print them out with print_r() this is what I get (this also happens with var_dump by the way)
Array ( [1] => 1 [string] => string [2] => 6 [3] => other [4] => 8 )
Array ( [6] => 1 [string] => string [7] => 24 [8] => other [1] => 6 [9] => 8 )
As far as I can tell, by putting the two in the second array it overwrites the next possible spot with no key and then keeps going, shortening the array. So I thought that meant that if I use a 1 it would put it at the start but that does not happen either.
Is this normal or is there something wrong with my php installation?
Im using Ampps in windows 10 with php 7.3.
Thanks in advance
Good question.
What's happening is that when determining automatic numeric indexes, PHP will look to the largest numeric index added and increment it (or use 0 if there are none).
The key is optional. If it is not specified, PHP will use the increment of the largest previously used integer key.
What's happening with your first array is that as it is evaluated left-to-right, 24 is inserted at index 2 because the last numeric index was 1 => 1.
Then when it gets to 2 => 6, it overwrites the previous value at index 2. This is why 24 is missing from your first array.
If multiple elements in the array declaration use the same key, only the last one will be used as all others are overwritten.
Here's a breakdown
$TestArray1 = [1 => 6]; // Array( [1] => 6 )
// no index, so use last numeric + 1
$TestArray1[] = 24; // Array( [1] => 6, [2] => 24 )
$TestArray1[2] = 6; // Array( [1] => 6, [2] => 6 )
When you manually add numeric indexes that are lower than previous ones (ie $TestArray2), they will be added as provided but their position will be later.
This is because PHP arrays are really maps that just pretend to be indexed arrays sometimes, depending on what's in them.
References are from the PHP manual page for Arrays
This question already has an answer here:
PHP, sort, sort_flags
(1 answer)
Closed 2 years ago.
I have an array to sort as
$numbers = array(24, 19, 3, 16, 56, 8, 171);
sort($numbers, SORT_STRING);
print_r($numbers);
And thus when i sort the array i get the result as
(
[0] => 16
[1] => 171
[2] => 19
[3] => 24
[4] => 3
[5] => 56
[6] => 8
)
How does the SORT_STRING works can anybody explain it to me?
SORT_STRING will compare character by character as in a word. So letter A is "higher" (or lower, your choice) than B, B > C.
1 > 2, 2 > 3, 11 > 12 and so on.
So in your case, when comparing 24 with 19, it will compare 1 with 2 and then order.
As a second example, when comparing 19 with 16, first will compare 1 with 1, which is equal, then compare 9 with 6, making 6 (and 16) a higher order.
More info here: PHP, sort, sort_flags
If you want to dig deeper, this is the link for the PHP source code.
This is the line where it choices the sort algorithm depending on the sort flag:
https://github.com/php/php-src/blob/50765075db6b6d5a9597589601c59f743f6ee9c8/ext/standard/array.c#L502
And here where it execute the comparison/sort:
https://github.com/php/php-src/blob/50765075db6b6d5a9597589601c59f743f6ee9c8/ext/standard/array.c#L207
And then it will call a zend operator:
https://github.com/php/php-src/blob/5430a466ff31422b436df076581d8345531db975/Zend/zend_operators.c
The SORT_STRING compare items as strings. You can find more detail here: https://www.php.net/manual/en/function.sort.php
A simple example is when you sort your files or directories on you computer: the sorting is alphabetical order.
This works as an ascending numeric values for the first digits(as 1 in 16) of all the numbers and after finishing them all,the pointer goes towards the digit next to it (as 6 in 16) and this is how it works so basically it arranges the digits from the left most digit and then goes on arranging them by the preceeding ones(right).
For example: comparing between 16 and 171
As both the numbers have their left-most digits as 1 it sorts it numbers based on their next digits(the right ones).
And thus by following the rule you get an result array as
(
[0] => 16
[1] => 171
[2] => 19
[3] => 24
[4] => 3
[5] => 56
[6] => 8
)
I'm writing this code that changes an indexed array to an associative array. It works for all the states I've checked, except california. For california, its an associative array for a little while, and then it changes to an indexed array out of nowhere and back to an associative one at the end. When I echo the individual indexes, I get the values I'm looking to use in the associative array so I'm not sure why it doesn't stick. Can anyone help?
$ids_file = file_get_contents("http://waterdata.usgs.gov/ca/nwis/current?index_pmcode_STATION_NM=1&index_pmcode_DATETIME=2&index_pmcode_72019=72&index_pmcode_00065=261&index_pmcode_00060=191&group_key=NONE&format=sitefile_output&sitefile_output_format=rdb&column_name=agency_cd&column_name=site_no&column_name=station_nm&column_name=dec_lat_va&column_name=dec_long_va&column_name=coord_acy_cd&column_name=dec_coord_datum_cd&column_name=alt_va&column_name=alt_acy_va&column_name=alt_datum_cd&sort_key_2=site_no&html_table_group_key=NONE&rdb_compression=file&list_of_search_criteria=realtime_parameter_selection");
$gages = explode("\t",$ids_file);
//this provides the randomized list of gages to pick from
for($i=19;$i<count($gages);$i=$i+9){
$gagenew = array($gages[$i]=>$gages[$i+1]);
$gagenum = array_merge($gagenum,$gagenew);
}
this is a small sample of what I get when I echo $gagenum
[09526200] => YPSILANTI CANAL NEAR WINTERHAVEN, CA
[09527590] => COACHELLA CANAL ABV ALL-AMERICAN CANAL DIV
[09527594] => COACHELLA CANAL NEAR NILAND, CA
[09527597] => COACHELLA CANAL NEAR DESERT BEACH, CA
[09527700] => ALL-AMERICAN CANAL BELOW DROP 2 RESERVOIR OUTLET
[09530000] => RESERVATION MAIN DRAIN NO. 4 NEAR YUMA, AZ
[09530500] => DRAIN 8-B NEAR WINTERHAVEN, CA
[0] => BOREHOLE SPG CHANNEL NR TECOPA HOT SPGS, CA
[1] => AMARGOSA RV AT TECOPA, CA
[2] => AMARGOSA RV ABV CHINA RANCH WASH NR TECOPA, CA
[3] => WILLOW CK AT CHINA RANCH, CA
[4] => SALT C NR MECCA
[5] => ALAMO R NR NILAND CA
[6] => NEW R AT INTERNATIONAL BOUNDARY AT CALEXICO CA
[7] => NEW R NR WESTMORLAND CA
[8] => SNOW C NR WHITE WATER CA
[9] => FALLS C NR WHITE WATER CA
[10] => WHITEWATER R A WINDY POINT MAIN CHANNEL CA
[11] => WHITEWATER R A WINDY POINT OVERFLOW CHANNEL CA
see where is it changes to an indexed array out of nowhere?
Your problem comes from the behavior that naturally occurs in array_merge():
Merges the elements of one or more arrays together so that the values of one are appended to the end of the previous one. It returns the resulting array.
If the input arrays have the same string keys, then the later value for that key will overwrite the previous one. If, however, the arrays contain numeric keys, the later value will not overwrite the original value, but will be appended.
Values in the input array with numeric keys will be renumbered with incrementing keys starting from zero in the result array.
PHP array_merge() Reference
You could try declaring all your keys as strings.
Edit
Oddly enough, my testing can't get it to behave. Even stranger than that, I found a simpler method of joining arrays that I never even considered.
//Replace array_merge() with this
$gagenum += $gagenew;
When you print_r($gagenum) it should be the two arrays joined with the keys they had. You could either leave it at that or use the result for a var_dump($gagenum) and debug it.
Experimentation
Further tests with the differences between array_merge() and the concatenation method $array1 + $array2 should help give a better understanding of the difference.
<?php
$int1 = array( 2 => 'george', 3 => 'sam', 4 => 'bob' );
$int2 = array( 0 => 'sue', 1 => 'debbie', 2 => 'sally' );
$str1 = array( 'one' => 'ken', 'two' => 'tom', 'three' => 'phil' );
$str2 = array( 'three' => 'jenny', 'four' => 'alice', 'five' => 'jan' );
$mergeii = array_merge( $int1, $int2 );
$mergeis = array_merge( $int1, $str1 );
$mergesi = array_merge( $str1, $int1 );
$mergess = array_merge( $str1, $str2 );
$concatii = $int1 + $int2;
$concatis = $int1 + $str1;
$concatsi = $str1 + $int1;
$concatss = $str1 + $str2;
// Merge Int/Int - Numeric Keys Reassigned Starting From [0], with $int1 first. No Overwrites.
echo "Merge Int/Int:\r\n"; print_r($mergeii);
// Merge Int/Str - Numeric Keys Reassigned Starting From [0], $int1 first. No Overwrites.
echo "\r\nMerge Int/Str:\r\n"; print_r($mergeis);
// Merge Str/Int - Numeric Keys Reassigned Again, $str1 first. No Overwrites.
echo "\r\nMerge Str/Int:\r\n"; print_r($mergesi);
// Merge Str/Str - No Numeric Keys. Value of Key 'Three' overwritten with $str2 value. Bye bye, 'phil'.
echo "\r\nMerge Str/Str:\r\n"; print_r($mergess);
// Concatenate Int/Int - Numeric Keys Preserved. Value of Key (int)2 from $int1 Preserved, 'sally' lost.
echo "\r\nConcatenate Int/Int:\r\n"; print_r($concatii);
// Concatenate Int/Str - Expected results, All Key/Value Pairs Preserved
echo "\r\nConcatenate Int/Str:\r\n"; print_r($concatis);
// Concatenate Str/Int - Expected results, All Key/value Pairs Preserved
echo "\r\nConcatenate Str/Int:\r\n"; print_r($concatsi);
// Concatenate Str/Str - Expected results, Value of Key (str)'three' from $int1 Preserved, 'jenny' lost.
echo "\r\nConcatenate Str/Str:\r\n"; print_r($concatss);
?>
So array_merge() prioritizes the second argument over the first, and the concatenation method prioritizes the first over the second. array_merge() for some reason still treats numeral-like strings as int, even if explicitly declared. This was by changing the line to:
$int1 = array( (string)'2' => 'george', (string)'3' => 'sam', (string)'4' => 'bob' );
var_dump($int1) correctly shows the keys as strings before merging, yet still reassigns the keys numerically. Bug perhaps?
Which one is Faster
On to the performance test! Tests were done without altering the original array, was tested using 8 for loops with this format:
$start1 = microtime(true);
for( $x = 0; $x < 100000; ++$x)
{
$merged = array_merge($int1, $int2);
}
$end1 = microtime(true);
Array Merge Function Average time was anywhere between 0.15 - 0.19 seconds, with $int1/$int2 usually being the lowest by -0.01, where $str1/$str2 was usually +0.02 seconds. $str1/$int1 and $int1/$str1 flip-flopped between the middle average 0.16-0.17.
Concatenation Method Average time was anywhere between 0.05 - 0.07. Once again, $int1/$int2 was the leader by -0.01 second. $str1/$str2 was also +0.02 seconds longer typically.
So the concatenation method is definitely a better method, especially if you want to preserve array keys. Use array_merge() when you want to keep all values, but want to restructure two different int arrays into a sequential array for scripts that could use a good for loop.
i'm working on a project that will need to have everything shown with barcodes, so I've generated 7 numbers for EAN8 algorithm and now have to get these 7 numbers seperately, right now i'm using for the generation
$codeint = mt_rand(1000000, 9999999);
and I need to get this 7 numbers each seperately so I can calculate the checksum for EAN8, how can i split this integer to 7 parts, for example
12345678 to
arr[0]=1
arr[1]=2
arr[2]=3
arr[3]=4
arr[4]=5
arr[5]=6
arr[6]=7
any help would be appreciated..
also I think that I'm becoming crazy :D because I already tried most of the solutions you gave me here before and something is not working like it should work, for example:
$codeint = mt_rand(1000000, 9999999);
echo $codeint."c</br>";
echo $codeint[1];
echo $codeint[2];
echo $codeint[3];
gives me :
9082573c
empty row
empty row
empty row
solved! $codeint = (string)(mt_rand(1000000, 9999999));
Try to use str_split() function:
$var = 1234567;
print_r(str_split($var));
Result:
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 6
[6] => 7
)
There are two ways to do this, one of which is reasonably unique to PHP:
1) In PHP, you can treat an integer value as a string and then index into the individual digits:
$digits = "$codeint";
// access a digit using intval($digits[3])
2) However, the much more elegant way is to use actual integer division and a little knowledge about mathematical identities of digits, namely in a number 123, each place value is composed of ascending powers of 10, i.e.: 1 * 10^2 + 2 * 10^1 + 3 * 10^0.
Consequently, dividing by powers of 10 will permit you to access each digit in turn.
it's basic math you can divide them in loop by 10
12345678 is 8*10^1 + 7*10^2 + 6*10^3...
the other option is cast it to char array and then just get it as char
Edit
After #HamZa DzCyberDeV suggestion
$string = '12345678';
echo "<pre>"; print_r (str_split($string));
But in mind it comes like below but your suggestion is better one.
If you're getting string from your function then you can use below one
$string = '12345678';
$arr = explode(",", chunk_split($string, 1, ','));
$len = count($arr);
unset($arr[$len-1]);
echo "<pre>";
print_r($arr);
and output is
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 6
[6] => 7
[7] => 8
)
okay what you can do is
Type cast to string with prefill 0
this is how it works
$sinteger = (string)$integer;
$arrsize = 0 ;
for (i=strlen($sinteger), i == 0 ; i--)
{
arr[$arrsize]=$sinteger[i];
$arrsize++;
}
And then what is left you can prefill with zip.
I am sure you can manage the order reverse or previous. but this is simple approach.
I have an example array:
$a = array(
5 => 35,
16 => 22,
7 => 22,
3 => 22,
11 => 22,
9 => 27,
);
and I want to sort it by values and remember its keys.
Result that I expected was:
$a = array(
16 => 22,
7 => 22,
3 => 22,
11 => 22,
9 => 27,
5 => 35,
);
So my first thought was: asort !
Ok, I did
asort($a);
But no - it didn't just move 5 => 35 to the end of the array.
It changed my array to:
$a = array(
11 => 22,
3 => 22,
7 => 22,
16 => 22,
9 => 27,
5 => 35
);
You see ? Keys with the same value are reverse sorted. Why ?
You can't expect a certain sorting order for equal values. From the PHP manual on Sorting Arrays:
If any of these sort functions evaluates two members as equal then the order is undefined (the sorting is not stable).
"Why" is another question.
But it actually did what you were asking for, didn't it?
Key order weren't determined.
If you want certain order of keys, you should state it in the exercise conditions
http://en.wikipedia.org/wiki/Sorting_algorithm#Stability
in short, making sure that the order of the already sorted keys remains the same would cost computation time (and whoever designed the function at PHP decided it wasn't worth it)
Depending on the sort algorithm, it probably started sorting in another manner than just detecting that it should only move that single pair. But it ended up with a validly sorted array maintaining keys/values. they only look swapped cuz you have 4 keys with values of 22.