Sorting with arsort not stable [duplicate] - php

This question already has answers here:
How to have a stable sort in PHP with arsort()?
(7 answers)
Closed 9 years ago.
There is strange issue in php asort, arsort.
I am taking example of arsort
Case1
$a = array(
1 => 2,
2 => 1,
3 => 2,
4 => 1
);
arsort($a);
var_dump($a);
Output:
array(4) {
[3] =>
int(2)
[1] =>
int(2)
[4] =>
int(1)
[2] =>
int(1)
}
Here at index (3,1) and (4,2) are sorted in descending order because at index 3 and 1 values are same. Same for index 4 and 2.
Case2
$a = array(
1 => 2,
2 => 1,
3 => 2
);
arsort($a);
var_dump($a);
Output:
array(3) {
[1] =>
int(2)
[3] =>
int(2)
[2] =>
int(1)
}
Here at index (3,1) are sorted in ascending order and still at index 3 and 1 values are same.
Is there any solution for this issue? As I want that ordering should be certain. Like sort in either descending or ascending order if value at some indices are same.

According to the PHP documentation:
If any of these sort functions evaluates two members as equal then the order is undefined (the sorting is not stable).

You can't know exactly which behaviour is correct, with testing just with 2 elements. Here's an array with multiple elements (odd and even).
even number:
<?php
$a = array(
1 => 2,
2 => 1,
3 => 2,
4 => 1,
5 => 2,
6 => 1,
7 => 2,
8 => 1,
9 => 2,
10 => 1,
11 => 2,
12 => 1
);
arsort($a);
var_dump($a);
Result:
array (size=12)
1 => int 2
7 => int 2
5 => int 2
11 => int 2
9 => int 2
3 => int 2
10 => int 1
12 => int 1
6 => int 1
2 => int 1
4 => int 1
8 => int 1
odd number
<?php
$a = array(
1 => 2,
2 => 1,
3 => 2,
4 => 1,
5 => 2,
6 => 1,
7 => 2,
8 => 1,
9 => 2,
10 => 1,
11 => 2,
12 => 1,
13 => 2
);
arsort($a);
var_dump($a);
Result
array (size=13)
9 => int 2
11 => int 2
13 => int 2
1 => int 2
7 => int 2
3 => int 2
5 => int 2
12 => int 1
2 => int 1
4 => int 1
8 => int 1
6 => int 1
10 => int 1
The question is now, where did it add the 13th element (=2)? it's added just after the 11th element and before the first element...this means that there's no rule here. (At least according to what we see).
We can't say that it follows any rule with testing with just 2 variables like what you did, because you saw (1,3) and you presumed that it's sorted by key. Which is apparently not the case with multiple variables.

Related

laravel sorting arrays last 5 unique and reverse

I have an array of id's and I want to filter those id's to last 5 and unique ids.
$recently_viewed_ids
array:16 [▼
0 => 1
1 => 2
2 => 1
3 => 2
4 => 8
5 => 7
6 => 6
7 => 6
8 => 6
9 => 5
10 => 8
11 => 4
12 => 1
13 => 1
14 => 1
15 => 1
]
Here is my code and it's messing up because I'm getting 85672
$items = array_slice(array_unique(array_reverse($recently_viewed_ids)), -5);
Output I am expecting
14856
You need to use next combination of array functions:
array_slice( // get first 5 values
array_unique( // get only unique values
array_reverse($arr) //reverse array for get last values
)
,0,5);
Code example here: PHPize.online
In Laravel, I believe you can rewrite the correct answer to:
return collect($arr)
->reverse()
->unique()
->slice(0, 5)
->all();
try this :
$items = array_slice(array_unique(array_reverse($recently_viewed_ids)), 5);

Why an array key-value sets in the end of an array instead of a specific place?

to be precise I have:
$foo = [1, 4, 1, 5, 8, 1, 3, 5, 1, 4, 1, 3, 7, 2];
sort($foo);
$bar = array_count_values($foo);
for ($i = 1; $i < count($bar); $i++) {
if (!isset($bar[$i])) {
$bar[$i] = 0;
}
}
Actual result:
array (size=8)
1 => int 5
2 => int 1
3 => int 2
4 => int 2
5 => int 2
7 => int 1
8 => int 1
6 => int 0
Expected result:
array (size=8)
1 => int 5
2 => int 1
3 => int 2
4 => int 2
5 => int 2
6 => int 0
7 => int 1
8 => int 1
Why my key value pair 6 => 0 appears in the bottom of an array instead of a specific place?
It's because you are appending a new value to the array.
To "fix" that, use ksort($bar);.
As #Rob Ruchte wrote in comments, the array returned by array_count_values() is associative, which means there is no order with the keys.
http://php.net/manual/en/function.array-count-values.php
Returns an associative array of values from array as keys and their count as value.

Count and sum multidimensional array php

I have an array like this one:
array (size=1)
0 =>
array (size=33)
0 => int 126
1 => int 43
2 => int 4
3 => int 0
4 => int 3
5 => int 3
6 => int 30
7 => int 15
8 => int 22
9 => int 27
10 => int 22
11 => int 46
12 => int 0
13 => int 8
14 => int 14
15 => int 8
array (size=1)
1 =>
array (size=33)
0 => int 273
1 => int 3
2 => int 4
3 => int 28
4 => int 36
5 => int 19
6 => int 142
7 => int 81
8 => int 59
9 => int 71
10 => int 88
11 => int 47
12 => int 42
13 => int 0
14 => int 12
15 => int 97
(of course it is way longer)
and I need both to sum all the value with the same key and count how many values with the same key are >0 (cause I have to find the avarage of all the numbers >0
My expected result is
0=>
'sum' => 399
'count'=>2
1=>
'sum' =>46
'count'=>2
how can I create this array?
There's an inbuilt function in PHP to count the sum of all the elements of an array. Here, this will give you your expected output :
<?php
$arr = [[10, 20, 30, 40], [10, 20, 30], [10, 20, 30, 4]];
// Let the magic happen...
$yourArray = array_map(function ($el){ return ["sum" => array_sum($el), "count" => count($el)]; }, $arr);
print_r($yourArray);
?>
I have thought about this, and came up with a solution (I think...), it comes in the form of a function and it goes like this:
function getSumAndCount(array $arr)
{
$sum = 0;
$count = 0;
foreach ($arr as $v)
{
$count++;
if (is_array($v))
{
$next = getSumAndCount($v);
$count += $next['count'];
$sum += $next['sum'];
}
else
{
!is_numeric($v) ?: $sum += $v;
}
}
return [ 'sum' => $sum, 'count' => $count ];
}
It has a recursive check to if the array is multidimensional, checks if the value is numeric and sets the count and sum in a return array.
I haven't tested this properly as yet, but please test and let me know if you get the desired output.
EDIT
I will extend this to count dupe keys soon

How to reverse level index of array using foreach in php?

I'm using PHP on foreach function
$arr = array(array(1,2,3,4,5,6,7,8,9,10));
foreach($arr as $item){
var_dump($item);
}
And here is my result
array (size=10)
0 => int 1
1 => int 2
2 => int 3
3 => int 4
4 => int 5
5 => int 6
6 => int 7
7 => int 8
8 => int 9
9 => int 10
But I want to revers from high to small back
array (size=10)
0 => int 10
1 => int 9
2 => int 8
3 => int 7
4 => int 8
5 => int 9
6 => int 4
7 => int 5
8 => int 2
9 => int 1
How can I do
Use array_reverse() :
$arr = array(array(1,2,3,4,5,6,7,8,9,10));
foreach($arr as $item){
var_dump(array_reverse ($item));
}
See result
You could use PHPs reverse array sort (rsort). See the Documentation
$arr = array(array(1,2,3,4,5,6,7,8,9,10));
rsort($arr);
foreach($arr as $item){
var_dump($item);
}

PHP subtract one list from other

In python I have two list with non-unique values:
a = [1,2,3,4,5,5,4,3,2,1,2,3,4,5]
b = [1,2,2,2,5,5]
To substract b from a
I found solution:
from collections import Counter as mset
subtract = mset(a) - mset(b)
list(subtract.elements())
#result is [1, 3, 3, 3, 4, 4, 4, 5]!!!!!!!!
How to do the same in PHP? PHP does not support lists.
array_diff is not useful, because it deletes non-unique values
A "functional" solution:
$a = [1,2,3,4,5,5,4,3,2,1,2,3,4,5];
$b = [1,2,2,2,5,5];
$bCopy = $b;
$c = array_filter($a, function($item) use(&$bCopy) {
$idx = array_search($item, $bCopy);
// remove it from $b if found
if($idx !== false) unset($bCopy[$idx]);
// keep the item if not found
return $idx === false;
});
sort($c);
print_r($c);
You will need to make a copy of $b as the array_filter callback is destructive in regards to the array $b. Also you will need to sort the result if you want to have exact the same output as in python.
Related answers:
#1
#2
For the example you provided, you can try the following:
$a = [1,2,3,4,5,5,4,3,2,1,2,3,4,5];
var_dump($a);
$b = [1,2,2,2,5,5];
var_dump($b);
$c = array_diff($a, $b);
var_dump($c);
It should give you the following result:
array (size=14)
0 => int 1
1 => int 2
2 => int 3
3 => int 4
4 => int 5
5 => int 5
6 => int 4
7 => int 3
8 => int 2
9 => int 1
10 => int 2
11 => int 3
12 => int 4
13 => int 5
array (size=6)
0 => int 1
1 => int 2
2 => int 2
3 => int 2
4 => int 5
5 => int 5
array (size=6)
2 => int 3
3 => int 4
6 => int 4
7 => int 3
11 => int 3
12 => int 4
Update
Found the answer here.
I wrapped the solution in a useful function:
function array_diff_duplicates($array1, $array2) {
$counts = array_count_values($array2);
$result = array_filter($array1, function($o) use (&$counts) {
return empty($counts[$o]) || !$counts[$o]--;
});
sort($result, SORT_NUMERIC);
return $result;
}
Trying the following:
$a = [1,2,3,4,5,5,4,3,2,1,2,3,4,5];
$b = [1,2,2,2,5,5];
$c = array_diff_duplicates($a, $b);
var_dump($c);
Gives the expected result:
array (size=8)
0 => int 1
1 => int 3
2 => int 3
3 => int 3
4 => int 4
5 => int 4
6 => int 4
7 => int 5

Categories