PHP sort seems to malfunction - php

### Result of sort
sort($keys_arranged, SORT_NUMERIC);
var_dump($keys_arranged);
{ ...
[51]=> float(11.903327742296)
[52]=> int(5)
[53]=> float(13.165002546636)
[54]=> float(14.478273306964)
[55]=> float(4.6264742674547)
[56]=> float(13.290508819344)
[57]=> float(15.686809055276) }
### Result or rsort
rsort($keys_arranged, SORT_NUMERIC);
var_dump($keys_arranged);
{
[0]=> float(15.686809055276)
[1]=> float(14.478273306964)
[2]=> float(13.290508819344)
[3]=> float(13.165002546636)
[4]=> float(11.903327742296)
[5]=> int(5)
[6]=> float(4.6264742674547)
... }
echo var_export($keys_arranged);
array ( 0 => 3.142678516658294, 1 => 1.0, 2 => 1.0, 3 => 14.478273306963985, 4 => 13.165002546635966, 5 => 1.0, 6 => 1.0005037081114851, 7 => 1.0, 8 => 4.6264742674547001, 9 => 15.686809055275578, 10 => 1.0, 11 => 11.903327742295504, 12 => 13.29050881934397, 13 => 1.0, 14 => 1.0, 15 => 3.5421134937189365, 16 => 1.0, 17 => 0.010999999999999999, 18 => 3.2999566681750605, 19 => 5, 20 => 1.2282984802843129, 21 => 1.0, 22 => 2.9748253120971184, 23 => 0.44855992975075798, 24 => 0.99999999999999989, 25 => 3.8350475954623371, 26 => 1.0625975061426283, 27 => 1.0000072792091179, 28 => 0.99999987785487132, 29 => 1, 30 => 0.0, 31 => 1.0, 32 => 1.0, 33 => 1.0, 34 => 0.0, 35 => 1.0972568578553616, 36 => 1.0, 37 => 1.4077661823957415, 38 => 1.0, 39 => 0.0, 40 => 3.6038030347555705, 41 => 1.0, 42 => 1.0, 43 => 1.0636876768842174, 44 => 1.0, 45 => NAN, 46 => 1.0, 47 => NAN, 48 => NAN, 49 => NAN, 50 => NAN, 51 => 1.0, 52 => 1.0, 53 => NAN, 54 => 0.99958680716631509, 55 => 1.0, 56 => NAN, 57 => 1.0, )
I got some incorrect result from array_multisort. It has 8 different keys and all the keys but this key(say A) works fine.
To figure out why that happened, I played with A in the array. And I could see that not only in the function of array_multisort, but also in 'sort' function, an incorrect result prompted as well.
As you can see the result of rsort seems okay, the result of sort works strangely when it comes to
13.16 < 14.47 < 4.62 < 13.29 < 15.68
Does anyone know why this occurred?
// Data in readible format
array (
0 => 3.142678516658294,
1 => 1.0,
2 => 1.0,
3 => 14.478273306963985,
4 => 13.165002546635966,
5 => 1.0,
6 => 1.0005037081114851,
7 => 1.0,
8 => 4.6264742674547001,
9 => 15.686809055275578,
10 => 1.0,
11 => 11.903327742295504,
12 => 13.29050881934397,
13 => 1.0,
14 => 1.0,
15 => 3.5421134937189365,
16 => 1.0,
17 => 0.010999999999999999,
18 => 3.2999566681750605,
19 => 5,
20 => 1.2282984802843129,
21 => 1.0,
22 => 2.9748253120971184,
23 => 0.44855992975075798,
24 => 0.99999999999999989,
25 => 3.8350475954623371,
26 => 1.0625975061426283,
27 => 1.0000072792091179,
28 => 0.99999987785487132,
29 => 1,
30 => 0.0,
31 => 1.0,
32 => 1.0,
33 => 1.0,
34 => 0.0,
35 => 1.0972568578553616,
36 => 1.0,
37 => 1.4077661823957415,
38 => 1.0,
39 => 0.0,
40 => 3.6038030347555705,
41 => 1.0,
42 => 1.0,
43 => 1.0636876768842174,
44 => 1.0,
45 => NAN,
46 => 1.0,
47 => NAN,
48 => NAN,
49 => NAN,
50 => NAN,
51 => 1.0,
52 => 1.0,
53 => NAN,
54 => 0.99958680716631509,
55 => 1.0,
56 => NAN,
57 => 1.0, )

It seems as you actually do have discovered a bug. On large arrays containing NaN values, represented by the NAN constant in PHP, the built-in function sort fails.
The comparision with NaN should always result as non-ordered as mentioned by kuh-chan in the question comments. However, as soon as there is at least one operand NaN, in recent (March 2019) versions of PHP the spaceship operator <=> returns 1 (first operand greater than second one) and -1 (first operand less than second one) in some other versions.
echo var_dump( NAN <=> 1.23 );
echo var_dump( 1.23 <=> NAN );
echo var_dump( NAN <=> -1.23 );
echo var_dump( -1.23 <=> NAN );
echo var_dump( NAN <=> 0 );
echo var_dump( NAN <=> NAN );
I'm not very famimilar with the internals of the Zend engine. However, I guess the sort algorithm terminates too soon on large arrays with several NaN values, likely in order to the order comparision algorithm alway returns "greater (or less) than" which probably leads to multiple exchanges of the same elements.
I experienced that calling sort multiple times seems to continue the sorting. However, that would not be a proper workaround.
You can use a custom comparision algorithm with usort instead. If you want to order NaN at the start of the array, return -1 when the first operand is NaN, 1 when the second operand is NaN and 0 (equal) if both are. Otherwise return the comparision result of the spaceship operator.
usort($keys_arranged,
function(&$v1, &$v2)
{
switch( (is_nan($v1) ? 1 : 0) | (is_nan($v2) ? 2 : 0) )
{
case 0: return $v1 <=> $v2;
case 1: return -1;
case 2: return 1;
case 3: return 0;
}
}
);
Similar bitwise logic as above, but in a more compressive and direct form:
usort( $keys_arranged,
function(&$v1, &$v2){ return -2 === ($b = (is_nan($v1) ? -1 : -2) ^ (is_nan($v2) ? -1 : 0)) ? $v1 <=> $v2 : $b; }
);

Related

Properly getting the sorting order of this array in PHP

I have a 2-dimensional array which the values...
9999999999999999 3201 4584 4821 1628 1218 1786 4738 4897
3122 9999999999999999 1400 1638 1797 2756 3323 5310 5472
4523 1400 9999999999999999 237 3198 4156 4723 6711 6872
4760 1638 237 9999999999999999 3435 4394 4961 6948 7110
1324 1846 3247 3485 9999999999999999 958 1525 3931 4093
932 2854 4273 4510 1002 9999999999999999 567 4873 5034
1499 3422 4840 5078 1569 567 9999999999999999 5440 5602
5061 5359 6760 6998 4019 4959 5526 9999999999999999 161
5233 5531 6931 7169 4190 5130 5697 171 9999999999999999
Here's the same array in code:
array:9 [
0 => array:9 [
0 => 9999999999999999
1 => 3122
2 => 4523
3 => 4760
4 => 1324
5 => 932
6 => 1499
7 => 5061
8 => 5233
]
1 => array:9 [
0 => 3201
1 => 9999999999999999
2 => 1400
3 => 1638
4 => 1846
5 => 2854
6 => 3422
7 => 5359
8 => 5531
]
2 => array:9 [
0 => 4584
1 => 1400
2 => 9999999999999999
3 => 237
4 => 3247
5 => 4273
6 => 4840
7 => 6760
8 => 6931
]
3 => array:9 [
0 => 4821
1 => 1638
2 => 237
3 => 9999999999999999
4 => 3485
5 => 4510
6 => 5078
7 => 6998
8 => 7169
]
4 => array:9 [
0 => 1628
1 => 1797
2 => 3198
3 => 3435
4 => 9999999999999999
5 => 1002
6 => 1569
7 => 4019
8 => 4190
]
5 => array:9 [
0 => 1218
1 => 2756
2 => 4156
3 => 4394
4 => 958
5 => 9999999999999999
6 => 567
7 => 4959
8 => 5130
]
6 => array:9 [
0 => 1786
1 => 3323
2 => 4723
3 => 4961
4 => 1525
5 => 567
6 => 9999999999999999
7 => 5526
8 => 5697
]
7 => array:9 [
0 => 4738
1 => 5310
2 => 6711
3 => 6948
4 => 3931
5 => 4873
6 => 5440
7 => 9999999999999999
8 => 171
]
8 => array:9 [
0 => 4897
1 => 5472
2 => 6872
3 => 7110
4 => 4093
5 => 5034
6 => 5602
7 => 161
8 => 9999999999999999
]
]
What I'm trying to achieve is find the sorting order of this array in terms of closest. Each row contains the distance of a place to another place.
In other words, the array looks something like:
China India USA Japan
China 0 50 4000 2000
India 50 0 4100 2100
USA 4050 4120 2 3000
Japan 2010 1950 2997 0
As you can see, the value to self is 0, sometimes it's a small number like 2-10 (not really sure thou) because Google distance matrix API sometimes returns big values for same origin and destination too which is why I replaced it to 9999999999999999 throughout the diagonal in my original array above.
The goal is to get the sorting in terms of distance. The first entry is the starting point so in the case of the hypothetical array above it would be:
[0 1 3 2] i.e. From China to India, then India to Japan, and Japan to the USA. My goal is to use array_multisort, in the end, to order just the keys with the place names so that it's ordered properly.
The code I came up with isn't working as I hoped for:
$order = [0];
$i = 0;
$nextItemToProcessIndex = 0;
foreach($tempMatrix as $key => $entry) {
$tempMatrix[$key][$key] = PHP_INT_MAX;
}
while(!empty($tempMatrix)) {
$closestItemIndex = array_search(min($tempMatrix[$nextItemToProcessIndex]), $tempMatrix[$nextItemToProcessIndex]);
array_push($order, $closestItemIndex);
$this->pull_item($tempMatrix, $nextItemToProcessIndex);
$nextItemToProcessIndex = $closestItemIndex - 1;
$i++;
}
dd($order);
...
...
public function pull_item(&$array, $offset) {
array_walk($array, function (&$v) use ($offset) {
array_splice($v, $offset, 1);
});
$row = array_splice($array, $offset, 1);
return $row;
}
If altering matrix column labels is ok for you - you can do sorting like that:
$dist = [
"China" => ['China' => 0, 'India' => 50, 'USA' => 4000, 'Japan' => 2000],
"India" => ['China' => 50, 'India' => 0, 'USA' => 4100, 'Japan' => 2100],
"USA " => ['China' => 4050, 'India' => 4120, 'USA' => 2, 'Japan' => 3000],
"Japan" => ['China' => 2010, 'India' => 1950, 'USA' => 2997, 'Japan' => 0]
];
// sort columns by distance
foreach ($dist as $key => $value) {
asort($dist[$key]);
}
// display distances matrix
foreach ($dist as $row_c => $data) {
$row1 = "\t";
$row2 = $row_c . " : ";
foreach ($data as $col_c => $col_dist) {
$row1 .= $col_c . "\t";
$row2 .= $col_dist . "\t";
}
echo "{$row1}\n{$row2}\n\n";
}
Outputs:
China India Japan USA
China : 0 50 2000 4000
India China Japan USA
India : 0 50 2100 4100
USA Japan China India
USA : 2 3000 4050 4120
Japan India China USA
Japan : 0 1950 2010 2997
I was able to figure out a solution to this on my own. Posting here if anyone else comes across the same issue:
$nextItemToProcess = 0;
$order = [0];
foreach(range(0, count($tempMatrix) - 2) as $i) {
if(count(array_diff_key($tempMatrix[$nextItemToProcess], $order)) == 1) {
$order = array_merge($order, array_diff(range(0, count($tempMatrix)-1), $order));
break;
}
$diffArray = array_diff_key($tempMatrix[$nextItemToProcess], array_flip($order));
$closestItemIndex = array_search(min($diffArray), $tempMatrix[$nextItemToProcess]);
array_push($order, $closestItemIndex);
$nextItemToProcess = $closestItemIndex;
}
$ascOrigins = [];
foreach($order as $i) {
array_push($ascOrigins, $arrOrigins[$i]);
}
ascOrigins is the array in ascending order of place distances. I thought I could use php's array_multisort but it wasn't useful since here I'm sorting the keys of arr based on the values of order. (I know an array_flip could have done the trick but this works fine).

PHP Trader macd returns false

I want to use trader_macd but it returns always false.
I am using the default parameters:
$data = [
0 => "0.06945900",
1 => "0.06945200",
2 => "0.06948100",
3 => "0.06944100",
4 => "0.06939800",
5 => "0.06941800",
6 => "0.06942300",
7 => "0.06940000",
8 => "0.06937700",
9 => "0.06937200",
10 => "0.06940000",
11 => "0.06939800",
12 => "0.06941100",
13 => "0.06944500",
14 => "0.06940100",
15 => "0.06942600",
16 => "0.06941500",
17 => "0.06941400",
18 => "0.06939900",
19 => "0.06941400",
20 => "0.06940700",
21 => "0.06938100",
22 => "0.06940400",
23 => "0.06937400",
24 => "0.06937000",
25 => "0.06939700"]
$result = trader_macd($data, 12, 26, 9)
When I set the last parameter ($signalPeriod) then get an array with 0 values:
0 => array:1 [▼
24 => -0.0
]
1 => array:1 [▼
24 => -0.0
]
2 => array:1 [▼
24 => -0.0
]
When I am using other methods like trader_ema with same $data it works fine.
I also set trader.real_precision to 8.
ini_set('trader.real_precision', '8');
What I am doing wrong?
My Systems uses php 7.2.7 with trader 0.5.0.
You don't have enough data to calculate the signal line you chose (9 day EMA of MACD line). Add eight more entries to your data array, and you'll get a result. Or lower the signal line period

How to Create complex Array Structure in PHP

I have to make this kind of structure in array;
We have three ( 3 ) variables which creates this structure:
$numberOfParticipants = 38; // 38 is example
$numberOfParticipantsPerHeat = 8 // 8 is example
$numberOfHeats = 5; // 5 is example
Based on this variables I have this table:
The problem is that, I can't place the ' - ' or null after 31 OR 38. The task is that i have to make the arrays of array "almost equal" like the photo and must depend on the variables above. By the way, after I create the correct list I will slice the array to 5 or 6 or whatever parts I need this is not the problem, the problem is that I have to parse the list like this first. This is what I tried so far:
$calc1 = (int)round($numberOfParticipants * $numberOfParticipantsPerHeat, -1); //First round the numberOfParticipants to closest integer by 10
$readyArr = [];
for ($i = 1; $i <= $calc1; $i++) {
if ($i <= $numberOfParticipants) {
$readyArr[$i] = $i;
} else {
$readyArr[$i] = null;
}
}
The problem with this snippet is that it places the null at the end of the list not after 31, or based on the var.
This is the result I have:
array:40 [▼
1 => 1
2 => 2
3 => 3
4 => 4
5 => 5
6 => 6
7 => 7
8 => 8
9 => 9
10 => 10
11 => 11
12 => 12
13 => 13
14 => 14
15 => 15
16 => 16
17 => 17
18 => 18
19 => 19
20 => 20
21 => 21
22 => 22
23 => 23
24 => 24
25 => 25
26 => 26
27 => 27
28 => 28
29 => 29
30 => 30
31 => 31
32 => 32
33 => 33
34 => 34
35 => 35
36 => 36
37 => 37
38 => 38
39 => null
40 => null
]
The Array after partition I want should be:
array(
0 => array(0 => 1, 1 => 2, 2 => 3, 3 => 4, 4 => 5, 5 => 6, 6 => 7, 7 => 8,),
1 => array(0 => 9, 1 => 10, 2 => 11, 3 => 12, 4 => 13, 5 => 14, 6 => 15, 7 => 16,),
2 => array(0 => 17, 1 => 18, 2 => 19, 3 => 20, 4 => 21, 5 => 22, 6 => 23, 7 => 24,),
3 => array(0 => 25, 1 => 26, 2 => 27, 3 => 28, 4 => 29, 5 => 30, 6 => 31, 7 => null,),
4 => array(0 => 32, 1 => 33, 2 => 34, 3 => 35, 4 => 36, 5 => 37, 6 => 38, 7 => null,),
);
Every help, every clue will be highly appreciated.
There are two things you need to know about the target structure:
How many players are in the first (which will always be the largest, if only by one) set.
$playersPerHeat = ceil($numberOfParticipants / $numberOfHeats);
// note this replaces your hard-coded $numberOfParticipantsPerHeat
You also need to know how many heats actually have that many, that is how many heats are actually full.
$fullHeats = $numberOfParticipants % $numberOfHeats ?: $numberOfHeats;
// The ?: bit means that if we get zero (ie. all equal heats), then we
// count all the heats instead, since they're all full.
Now it's easy!
$players = range(1,$numberOfParticipants);
$heats = array_merge(
array_chunk(
array_slice($players, 0, $fullHeats * $playersPerHeat),
$playersPerHeat
),
array_chunk(
array_slice($players, $fullHeats * $playersPerHeat),
$playersPerHeat - 1
)
);
That's it! Demo

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

unexpected results with sort(array)

Odd.....
// array sort test
$_ar = array(
0 => "2015-02-23",
1 => "2015-02-21",
2 => "2015-02-28",
3 => "2015-03-20",
4 => "2015-03-14",
5 => "2015-03-21",
6 => "2015-02-21",
7 => "2015-02-28",
8 => "2015-03-07",
9 => "2015-03-14",
);
$_ar = sort($_ar);
var_dump($_ar);
// returns bool(true)
$__ar = array(
0 => "2015 02 23",
1 => "2015 02 21",
2 => "2015 02 28",
3 => "2015 03 20",
4 => "2015 03 14",
5 => "2015 03 21",
6 => "2015 02 21",
7 => "2015 02 28",
8 => "2015 03 07",
9 => "2015 03 14",
);
$__ar = sort($__ar);
var_dump($__ar);
// returns bool(true)
$ar = array(
0 => "20150223",
1 => "20150221",
2 => "20150228",
3 => "20150320",
4 => "20150314",
5 => "20150321",
6 => "20150221",
7 => "20150228",
8 => "20150307",
9 => "20150314",
);
$ar = sort($ar);
var_dump($ar);
// returns bool(true)
I am expecting this to return the array sorted by the date value. I thought maybe it was the - (hyphen) or spaces, but in all my examples my PHP var_dump simply returns bool(true) for each instance. Can someone confirm they get the same, or point out what I must be missing....
I have tried asort() - still the same.
You don't have to assign the return value of sort(). For more information about sort() see the manual: http://php.net/manual/en/function.sort.php
And a quote from there:
Returns TRUE on success or FALSE on failure.
So just to this:
sort($_ar);
Side Note:
I wouldn't recommend you to define variables with underscores at the start of the name, since this already get's used by defined php variables e.g. super globals or magic constants
The sort and asort function returns bool value. Just call this function and it will sort the array, don't store it , it returns true or false.Use the code below
// array sort test
$_ar = array(
0 => "2015-02-23",
1 => "2015-02-21",
2 => "2015-02-28",
3 => "2015-03-20",
4 => "2015-03-14",
5 => "2015-03-21",
6 => "2015-02-21",
7 => "2015-02-28",
8 => "2015-03-07",
9 => "2015-03-14",
);
sort($_ar);
var_dump($_ar);
// returns bool(true)
$__ar = array(
0 => "2015 02 23",
1 => "2015 02 21",
2 => "2015 02 28",
3 => "2015 03 20",
4 => "2015 03 14",
5 => "2015 03 21",
6 => "2015 02 21",
7 => "2015 02 28",
8 => "2015 03 07",
9 => "2015 03 14",
);
$__ar = sort($__ar);
var_dump($__ar);
// returns bool(true)
$ar = array(
0 => "20150223",
1 => "20150221",
2 => "20150228",
3 => "20150320",
4 => "20150314",
5 => "20150321",
6 => "20150221",
7 => "20150228",
8 => "20150307",
9 => "20150314",
);
sort($ar);
var_dump($ar);
// returns bool(true)
Hope this helps you

Categories