PHP's asort does not work properly? - php

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.

Related

Is this normal behaviour in php arrays? Array size get shortened when using numbered indexes out of order

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

How does the sort_flag works for sorting the array? [duplicate]

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
)

Best way to iterate to a part of an array based on value?

I have an php-array like this:
$students = array(
array(1, 'James', 'Brown', 10, 50),
array(2, 'John', 'Doe', 10, 40),
array(3, 'Adam', 'Works', 10, 40));
Where the columns represent id, first name, last name, category, and level. As example I use three rows... in practice I use many more.
Now I need to group print the results based on column (category or level) and a specific value. For example, I want to print (echo) all students in category 10. I can loop through each record and check the category; if 10 then print. The same applies for level 40. Loop through the whole set again and check the level; if 40 then print.
With an eye on performance, is there a smarter way to achive this? For example, first sort or filter?
I could choose to query the database many times but I doubt if this is the best way. I guess manipulating the array is better.
Thanks for your support!
The most straightforward way to do this is with the array_filter() function. But if these values come from a database, you're probably better off writing better queries.
$ cat array-filter.php
<?php
$students = array(
array(1, 'James', 'Brown', 10, 50),
array(2, 'John', 'Doe', 10, 40),
array(3, 'Adam', 'Works', 10, 40));
$level_40 = array_filter($students, function($a) {return $a[4]==40 ? true : false;});
print_r($level_40);
?>
$ php array-filter.php
Array
(
[1] => Array
(
[0] => 2
[1] => John
[2] => Doe
[3] => 10
[4] => 40
)
[2] => Array
(
[0] => 3
[1] => Adam
[2] => Works
[3] => 10
[4] => 40
)
)
The callback function just compares the fifth column to the integer literal 40. The fifth column has index 4, not 5, hence the expression $a[4]==40 ?.
If you're getting this data from a database, you're probably better off writing better SQL. To get all the students whose level is 40, you'd write something like
select *
from students
where level = 40
order by student_id;
The result set will contain exactly the right rows, so you don't need to filter them. And they'll be in exactly the right order, so you don't need to sort them.

PHP array key to value mappings question

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
)

PHP How to sort associative array first by keys then by values?

$arr =array(
28 => 23,
26 => 23,
15 => 12,
29 => 12,
1 => 12,
16 => 15,
30 => 15,
11 => 12,
8 => 23,
33 => 23
);
how to sort like this :
8 => 23
26 => 23
28 => 23
33 => 23
16 => 15
30 => 15
1 => 12
11 => 12
15 => 12
29 => 12
Use uksort, but make the array available to the comparison function for the secondary comparison by value. Making it a global variable would be the quickest + dirtiest way.
You could use uksort() which enables the custom callback to take a look at both the keys and, indirectly, their associated values. Then it's a simple matter of deciding which comparisons to make and returning the appropriate greater-than-less-then-or-zero value to influence the sort order.
Here's an example using a closure around a temporary variable (see Jacob's comment) which should hopefully make sense.
$temp = $arr;
uksort($arr, function ($a,$b) use ($temp) {
// Same values, sort by key ascending
if ($temp[$a] === $temp[$b]) {
return $a - $b;
}
// Different values, sort by value descending
return $temp[$b] - $temp[$a];
});
unset($temp);
print_r($arr);
Its quite easy. First use ksort and then use asort for the new sorted array. You will find your result.

Categories