I am a bit 'rusty with php as it happens that sometimes I use it for weeks and sometimes it happens that you do not use for months. Either way I'm trying to pass values of another array are "array", on another array in an orderly manner ... What I want to do is essentially create a key that allows me to organize incremental values per line, in particular;
array content
Array
(
[key] => value
[2] => 1
[3] => Inter
[4] => 4
[5] => 4
[6] => 0
[7] => 0
[8] => 5
[9] => 1
[10] => +4
[11] => 12
[12] => Chievo Verona - Inter 0 - 1
[13] => Inter - Milan 1 - 0
[14] => Carpi - Inter 1 - 2
[15] => Inter - Atalanta 1 - 0
[16] => ;
[17] => 2
[18] => Torino
[19] => 4
[20] => 3
[21] => 1
[22] => 0
[23] => 9
[24] => 4
[25] => +5
[26] => 10
[27] => Torino - Sampdoria 2 - 0
[28] => Hellas Verona - Torino 2 - 2
[29] => Torino - Fiorentina 3 - 1
[30] => Frosinone - Torino 1 - 2
[31] => ;
[32] => 3
[33] => Fiorentina
[34] => 4
[35] => 3
[36] => 0
[37] => 1
[38] => 5
[39] => 3
[40] => +2
[41] => 9
[42] => Carpi - Fiorentina 0 - 1
[43] => Fiorentina - Genoa 1 - 0
[44] => Torino - Fiorentina 3 - 1
[45] => Fiorentina - Milan 2 - 0
[46] => ;
[47] => 4
[48] => Roma
[49] => 4
[50] => 2
the ";" It'll need to be able to recognize where you break the line, I do not remember if there is any method that allows me to access the next key.
Currently my code is:
$classifica = array("key" => "value");
function buildArrayClassifica()
{
global $array;
global $classifica;
$i = 0;
foreach(array_slice($array,1) as $key => $value)
{
if($value != ";")
{
array_push($classifica[$i], $value); //there is a problem
echo $value . " ";
}
else if($value == "value ")
{
continue;
}
else
{
$i++;
echo "<br/>";
}
}
}
The code I will return this error:
Warning: array_push () Expects parameter 1 to be array, null given in...
in particular on array_push, it seems not to accept incremental keys or maybe I'm doing it the wrong way.
Can anyone tell me how to solve?
UPDATING
As you have seen the issue is not simple and it is quite difficult to explain the problem, but I will try to be even clearer to meet.
As you can see above you are the structure of the array "array", but is a disordered structure that needs to be ordered in an additional array. To recapitulate the structure of the array "array" is:
1 , Inter , 4 , 4 , 0 , 0 , 5 , 1 , +4 , 12 , Chievo Verona - Inter 0 - 1 , Inter - Milan 1 - 0 , Carpi - Inter 1 - 2 , Inter - Atalanta 1 - 0 , ;
the ";" means that the line is finished. So the next value near the ";" means that a new line coming. What I need is move all the value of "array" in array classifica, but I want organize them for:
ROW1 => 1 , Inter , 4 , 4 , 0 , 0 , 5 , 1 , +4 , 12 , Chievo Verona - Inter 0 - 1 , Inter - Milan 1 - 0 , Carpi - Inter 1 - 2 , Inter - Atalanta 1 - 0
ROW2 => Other values...
So the ROW1, 2 .. rapresents the key of the array classifica. I'm trying to push the value inside a row and after it increment $i index but the code doesn't add the value because the index replace in loop the actual key, for example:
actual foreach content:
$i = 0
value = "Inter"
content of array=> [0] => Inter
now the $i is ever 0 because the row isn't finished yet, the ";"
it has not yet been reached, so the next content of foreach is:
"1" but replace the "Inter" value, so this is a problem.
You cannot use array_push() in this way. Please try:
$classifica = array();
function buildArrayClassifica()
{
global $array;
global $classifica;
$i = 0;
foreach(array_slice($array,1) as $key => $value)
{
if($value != ";")
{
$classifica[$i] = $value;
echo $value . " ";
}
else if($value == "value ")
{
continue;
}
else
{
$i++;
echo "<br/>";
}
}
}
This will create indexes (the value of $i) when $value is added to your array. array_push() would place the $value at the next numerical index and may not be what you want by the looks of it. You could also use $key if you wanted the index to match.
EDIT
After more discussion, you have a specific format, where the first Item is the Key, the following indexes are values, and when you encounter the value ";", it starts the sequence over. So when we read:
[2] => 1
[3] => Inter
[4] => 4
[5] => 4
[6] => 0
[7] => 0
[8] => 5
[9] => 1
[10] => +4
[11] => 12
[12] => Chievo Verona - Inter 0 - 1
[13] => Inter - Milan 1 - 0
[14] => Carpi - Inter 1 - 2
[15] => Inter - Atalanta 1 - 0
[16] => ;
The first value, '1' is our Index, the following values become the Value for this Index, and we stop reading when we find ";". That would look something like:
<?php
function buildArrayClassifica($dataArray){
$resultArray = array();
$t = array_values($dataArray);
print_r($t);
$lsc = 0;
foreach($t as $k => $v){
if((string)$v == ';'){
echo "<p>Found ';' at [$k] => {$v}</p>";
// Found end of data
// Save position
$scp = $k;
echo "<p>Recorded [$scp] position for ';'.</p>";
// Reset to find the Index, first int in this series
$c=$lsc; // First pass this should be 0
// Set the index
if($lsc ==0){
// First pass
$index = intval($t[$c]);
echo "<p>Getting Index from position [" . ($c) ."] => $index for Result Array.</p>";
$c++;
} else {
$c++;
$index = intval($t[$c]);
echo "<p>Getting Index from position [" . ($c) ."] => $index for Result Array.</p>";
$c++;
}
echo "<p>Starting to read data from [$c] until [$scp].</p>";
// Init implode variable
$data = "";
for($c;$c<$scp;$c++){
//Populate variable with the series up to semicolon, skipping first element (index)
$data .= $t[$c] . ", ";
}
echo "<p>Data collected for this round: '" . htmlentities(substr($data,0,-2)) . "'</p>";
// populate result array
$resultArray[$index] = substr($data,0,-2);
echo "<p>resultArray[$index] => " . htmlentities($resultArray[$index]) . "</p><br />";
$lsc = $scp;
}
}
return $resultArray;
}
$oldArray = array(1, "Inter", 4 , 4 , 0 , 0 , 5 , 1 , "+4" , 12 , "Chievo Verona - Inter 0 - 1", "Inter - Milan 1 - 0", "Carpi - Inter 1 - 2", "Inter - Atalanta 1 - 0", ";", 2, "Torino", 4, 3, 1, 0, 9, 4, '+5', 10, "Torino - Sampdoria 2 - 0", "Hellas Verona - Torino 2 - 2", "Torino - Fiorentina 3 - 1", "Frosinone - Torino 1 - 2", ";", 3, "apple", 0, 4, 6, "apple", ";");
$classifica = buildArrayClassifica($oldArray);
print_r($classifica);
?>
My initial testing seems to work for what you described. The first element of the array becomes the Index, the next few values become imploded until we reach the semicolon (;) value.
What I see as a result:
Array ( [1] => Inter, 4, 4, 0, 0, 5, 1, +4, 12, Chievo Verona - Inter 0 - 1, Inter - Milan 1 - 0, Carpi - Inter 1 - 2, Inter - Atalanta 1 - 0 [2] => Torino, 4, 3, 1, 0, 9, 4, +5, 10, Torino - Sampdoria 2 - 0, Hellas Verona - Torino 2 - 2, Torino - Fiorentina 3 - 1, Frosinone - Torino 1 - 2 [3] => apple, 0, 4, 6, apple )
ASIDE
If it were me, I would push it all into an array like so:
$data = array();
for($c;$c<$scp;$c++){
$data[] = $t[$c];
}
$resultArray[$index] = $data;
Or if you really want a string:
$resultArray[$index] = implode(", ", $data);
Hope that helps.
Related
I have an array of 20 different numbers which contains ranking of students.
I want to split this array into two sub arrays of equal length i.e 10
I also want the sum of all numbers within each array to be close.
For example, in sub-array A there could be a total sum of 56 and in sub-array B there could be a total sum of 57.
I am using PHP.
I sort the main array here and would like to assign index[0] to sub-array A and index[1] to sub-array B, and keep repeating this until both arrays are filled.
My approach works but i think its not great and not dynamic.
I interate through the main original array for [i] and then add that to the first sub-array, then I set i = i+2 so that I get every second value and store them in the first array.
I then remove the value at index[i] from the main array.
What is left over is now sub-array B.
$kids = array (8,5,6,9,3,8,2,4,6,10,8,5,6,1,7,10,5,3,7,6);
sort($kids);
$arrlength = count($kids);
for($x = 0; $x < $arrlength; $x++) {
echo $kids[$x];
echo "<br>";
}
$teamA = array();
$teamB = array();
$i = 0;
while ($i < $arrlength)
{
#echo $kids[$i] ."<br />";
array_push($teamA, $kids[$i]);
unset($kids[$i]);
$i += 2;
}
$teamB = $kids;
print_r($teamA);
print_r($teamB);
My Output is :
Array ( [0] => 1 [1] => 3 [2] => 4 [3] => 5 [4] => 6 [5] => 6 [6] => 7 [7] =>
8 [8] => 8 [9] => 10 )
The sum here of all values is = 58
Array ( [1] => 2 [3] => 3 [5] => 5 [7] => 5 [9] => 6
[11] => 6 [13] => 7 [15] => 8 [17] => 9 [19] => 10 )
The sum here of all values is = 61
Any help is greatly appreciated. I have no real experience with PHP or its built in functions so sorry if this is a basic question. Thanks!
There is a built-in helper function of php, array_slice. You can read about it in the link I provided.
Here's how you can use in to achieve what you want:
$kids = array (5,7,6,8,3,8,2,4,6,10,8,5,6,10,7,6,5,3,7,6);
sort($kids);
$arrlength = count($kids);
$arrayA= array_slice($kids, 0, $arrlength / 2);
$arrayB= array_slice($kids, $arrlength / 2);
Output
// $arrayA
array:10 [▼
0 => 2
1 => 3
2 => 3
3 => 4
4 => 5
5 => 5
6 => 5
7 => 6
8 => 6
9 => 6
]
// $arrayB
array:10 [▼
0 => 6
1 => 6
2 => 7
3 => 7
4 => 7
5 => 8
6 => 8
7 => 8
8 => 10
9 => 10
]
Another approach for achieving what you asked
$kids = array(8,5,6,9,3,8,2,4,6,10,8,5,6,1,7,10,5,3,7,6);
sort($kids);
$teamA = array();
$teamB = array();
foreach($kids as $i => $kid){
if($i % 2){
array_push($teamA, $kid);
} else{
array_push($teamB, $kid);
}
}
It will generate the same output and sum as you want.
This question already has answers here:
How can I sort arrays and data in PHP?
(14 answers)
Closed 5 years ago.
I need to sort the multidimensional array with output by value. I can not group the nested arrays by date and output it with the title (date).
There is an array:
Array (
[0] => (
[ID] => 959
[title] => title
[post_date] => 2018-01-01 10:17:49
)
[1] => (
[ID] => 960
[title] => title
[post_date] => 2018-01-01 10:17:49
)
[2] => (
[ID] => 961
[title] => title
[post_date] => 2018-01-02 10:17:49
)
[3] => (
[ID] => 962
[title] => title
[post_date] => 2014-01-02 10:17:49
)
[4] => (
[ID] => 963
[title] => title
[post_date] => 2014-01-03 10:17:49
)
)
As a result, I need to get this.
There is a date as the header and all arrays in which this date.
Result
- 2018-01-01 -
id: 959 Title
id: 560 Title
- 2018-01-02 -
id: 961 Title
id: 562 Title
- 2018-01-02 -
id: 963 Title
.....
have you done with part about sorting? I assume you are. So the only thing left is "grouping".
But actually you don't need to group data. Just iterate sorted items one by one and remember date from previous element. Once dates for current element and for previous one differ - you output "header". Something like that:
$data = [[a=> 2, b=> 1], [a=> 2, b=> 4], [a=> 3, b=> 1], [a=> 4, b=> 0]];
$previous_a = null;
foreach($data as $item) {
if ($item['a'] != $previous_a) {
echo 'header --- '.$item['a'].'<br />';
}
$previous_a = $item['a'];
echo $item['b'].'<br />';
}
You could try something like this :
$aggreg = [] ;
foreach ($array as $item) {
$day = $item['post_date'];
// if you want to group by day, just use $day = substr($day,0,10) ;
$aggreg[$day][] = 'id: ' . $item['ID'] . " " . $item['title'] ;
}
krsort($aggreg) ; // or ksort() or just comment this line.
// then build the output :
$output = "" ;
foreach ($aggreg as $day => $data) {
$output .= "- " .substr($day,0,10) . " -\n\n" . implode("\n", $data) . "\n\n" ;
}
print $output ;
Will outputs :
- 2018-01-02 -
id: 961 title
- 2018-01-01 -
id: 959 title
id: 960 title
- 2014-01-03 -
id: 963 title
- 2014-01-02 -
id: 962 title
NB If you have to output this in HTML, please change \n to <br>
I am trying to calculate the winning order of golfers when they are tied in a competition.
These golf competitions are using the "stableford" points scoring system, where you score points per hole with the highest points winning. Compared to normal golf "stroke play" where the lowest score wins (though this also has the countback system, only calculating the lowest score in the event of a tie...)
The rules are to use a "countback". i.e., if scores are tied after 9 holes, the best placed of the ties is the best score from the last 8 holes. then 7 holes, etc.
The best I can come up with is 2 arrays.
An array with all the players who tied in a given round. ($ties)
One which has the full score data in (referencing the database playerid) for all 9 holes. ($tie_perhole)
I loop through array 1, pulling data from array 2 and using the following formula to create a temporary array with the highest score:
$max = array_keys($array,max($array));
If $max only has 1 item, this player is the highest scorer. the loop through the first array is "by reference", so on the next iteration of the loop, his playerid is now longer in the array, thus ignored. this continues until there is only 1 playerid left in the first array.
However, it only works if a single player wins in each iteration. The scenario that doesn't work is if a sub-set of players tie on any iterations / countbacks.
I think my problem is the current structure I have will need the original $ties array to become split, and then to continue to iterate through the split arrays in the same way...
As an example...
The $ties array is as follows:
Array
(
[18] => Array
(
[0] => 77
[1] => 79
[2] => 76
[3] => 78
)
)
The $tie_perhole (score data) array is as follows:
Array
(
[18] => Array
(
[77] => Array
(
[9] => 18
[8] => 16
[7] => 14
[6] => 12
[5] => 10
[4] => 8
[3] => 6
[2] => 4
[1] => 2
)
[79] => Array
(
[9] => 18
[8] => 17
[7] => 15
[6] => 14
[5] => 11
[4] => 9
[3] => 7
[2] => 5
[1] => 3
)
[76] => Array
(
[9] => 18
[8] => 16
[7] => 14
[6] => 12
[5] => 10
[4] => 8
[3] => 6
[2] => 4
[1] => 2
)
[78] => Array
(
[9] => 18
[8] => 17
[7] => 15
[6] => 13
[5] => 11
[4] => 9
[3] => 7
[2] => 5
[1] => 3
)
)
)
So in this competition, player's 78 and 79 score highest on the 8th hole countback (17pts), so 1st and 2nd should be between them. Player 79 should then be 1st on the 6th hole countback (14pts, compared to 13pts). The same should occur for 3rd and 4th place with the 2 remaining other players.
There are other scenarios that can occur here, in that within a competition, there will likely be many groups of players (of different amounts) on different tied points through the leaderboard.
Also note, there will be some players on the leaderboard who are NOT tied and stay in their current outright position.
The basics of the working code I have is:
foreach ($ties as $comparekey => &$compareval) {
$tie_loop = 0;
for ($m = 9; $m >= 1; $m--) {
$compare = array();
foreach ($compareval as $tie) {
$compare[$tie] = $tie_perhole[$comparekey][$tie][$m];
}
$row = array_keys($compare,max($compare));
if (count($row) == 1) {
$indexties = array_search($row[0], $ties[$comparekey]);
unset($ties[$comparekey][$indexties]);
// Now update this "winners" finishing position in a sorted array
// This is a multidimensional array too, with custom function...
$indexresults = searchForId($row[0], $comp_results_arr);
$comp_results_arr[$indexresults][position] = $tie_loop;
$tie_loop++;
}
// I think I need conditions here to filter if a subset of players tie
// Other than count($row) == 1
// And possibly splitting out into multiple $ties arrays for each thread...
if (empty($ties[$comparekey])) {
break;
}
}
}
usort($comp_results_arr, 'compare_posn_asc');
foreach($comp_results_arr as $row) {
//echo an HTML table...
}
Thanks in advance for any helpful insights, tips, thoughts, etc...
Robert Cathay asked for more scenarios. So here is another...
The leaderboard actually has more entrants (player 26 had a bad round...), but the code i need help with is only bothered about the ties within the leaderboard.
Summary leaderboard:
Points Player
21 48
21 75
20 73
20 1
13 26
This example produces a $tie_perhole array of:
Array
(
[21] => Array
(
[75] => Array
(
[9] => 21
[8] => 19
[7] => 16
[6] => 14
[5] => 12
[4] => 9
[3] => 7
[2] => 5
[1] => 3
)
[48] => Array
(
[9] => 21
[8] => 19
[7] => 16
[6] => 13
[5] => 11
[4] => 9
[3] => 8
[2] => 5
[1] => 3
)
)
[20] => Array
(
[73] => Array
(
[9] => 20
[8] => 18
[7] => 16
[6] => 13
[5] => 11
[4] => 8
[3] => 6
[2] => 5
[1] => 3
)
[1] => Array
(
[9] => 20
[8] => 17
[7] => 16
[6] => 14
[5] => 12
[4] => 9
[3] => 7
[2] => 4
[1] => 2
)
)
)
In this example, the array shows that players 75 and 48 scored 21 points that player 75 will eventually win on the 6th hole countback (14pts compared to 13pts) and player 48 is 2nd. In the next tied group, players 73 and 1 scored 20 points, and player 73 will win this group on the 8th hole countback and finishes 3rd (18 pts compared to 17 pts), with player 1 in 4th. player 26 is then 5th.
Note, the $tie_loop is added to another array to calculate the 1st to 5th place finishing positions, so that is working.
Hopefully that is enough to help.
Ok, so I don't understand golf at all... hahaha BUT! I think I got the gist of this problem, so heres my solution.
<?php
/**
* Author : Carlos Alaniz
* Email : Carlos.glvn1993#gmail.com
* Porpuse : Stackoverflow example
* Date : Aug/04/2015
**/
$golfers = [
"A" => [1,5,9,1,1,2,3,4,9],
"B" => [2,6,4,2,4,4,1,9,3],
"C" => [3,4,9,8,1,1,5,1,3],
"D" => [1,5,1,1,1,5,4,5,8]
];
//Iterate over scores.
function get_winners(&$golfers, $hole = 9){
$positions = array(); // The score numer is the key!
foreach ($golfers as $golfer=>$score ) { // Get key and value
$score_sub = array_slice($score,0,$hole); // Get the scores subset, first iteration is always all holes
$total_score = (string)array_sum($score_sub); // Get the key
if(!isset($positions[$total_score])){
$positions[$total_score] = array(); // Make array
}
$positions[$total_score][] = $golfer; // Add Golpher to score.
}
ksort($positions, SORT_NUMERIC); // Sort based on key, low -> high
return array(end($positions), key($positions)); // The last shall be first
}
//Recursion is Awsome
function getWinner(&$golfers, $hole = 9){
if ($hole == 0) return;
$winner = get_winners($golfers,$hole); // Get all ties, if any.
if(count($winner[0]) > 1){ // If theirs ties, filter again!
$sub_golfers =
array_intersect_key($golfers,
array_flip($winner[0])); // Only the Worthy Shall Pass.
$winner = getWinner($sub_golfers,$hole - 1); // And again...
}
return $winner; // We got a winner, unless they really tie...
}
echo "<pre>";
print_R(getWinner($golfers));
echo "</pre>";
Ok... Now ill explain my method...
Since we need to know the highest score and it might be ties, it makes no sense to me to maintain all that in separate arrays, instead I just reversed the
golfer => scores
to
Tota_score => golfers
That way when we can sort the array by key and obtain all the golfers with the highest score.
Now total_score is the total sum of a subset of the holes scores array. So... the first time this function runs, it will add all 9 holes, in this case theres 3 golfers that end up with the same score.
Array
(
[0] => Array
(
[0] => A
[1] => B
[2] => C
)
[1] => 35
)
Since the total count of golfers is not 1 and we are still in the 9th hole, we run this again, but this time only against those 3 golfers and the current hole - 1, so we are only adding up to the 8th hole this time.
Array
(
[0] => Array
(
[0] => B
[1] => C
)
[1] => 32
)
We had another tie.... this process will continue until we reach the final hole, or a winner.
Array
(
[0] => Array
(
[0] => C
)
[1] => 31
)
EDIT
<?php
/**
* Author : Carlos Alaniz
* Email : Carlos.glvn1993#gmail.com
* Porpuse : Stackoverflow example
**/
$golfers = [
"77" => [2,4,6,8,10,12,14,16,18],
"79" => [3,5,7,9,11,14,15,17,18],
"76" => [2,4,6,8,10,12,14,16,18],
"78" => [3,5,7,9,11,13,15,17,18]
];
//Iterate over scores.
function get_winners(&$golfers, $hole = 9){
$positions = array(); // The score numer is the key!
foreach ($golfers as $golfer => $score) { // Get key and value
//$score_sub = array_slice($score,0,$hole); // Get the scores subset, first iteration is always all holes
$total_score = (string)$score[$hole-1]; // Get the key
if(!isset($positions[$total_score])){
$positions[$total_score] = array(); // Make array
}
$positions[$total_score][] = $golfer; // Add Golpher to score.
}
ksort($positions, SORT_NUMERIC); // Sort based on key, low -> high
return [
"winner"=> end($positions),
"score" => key($positions),
"tiebreaker_hole" => [
"hole"=>$hole,
"score"=> key($positions)],
]; // The last shall be first
}
//Recursion is Awsome
function getWinner(&$golfers, $hole = 9){
if ($hole == 0) return;
$highest = get_winners($golfers,$hole); // Get all ties, if any.
$winner = $highest;
if(count($winner["winner"]) > 1){ // If theirs ties, filter again!
$sub_golfers =
array_intersect_key($golfers,
array_flip($winner["winner"])); // Only the Worthy Shall Pass.
$winner = getWinner($sub_golfers,$hole - 1); // And again...
}
$winner["score"] = $highest["score"];
return $winner; // We got a winner, unless they really tie...
}
echo "<pre>";
print_R(getWinner($golfers));
echo "</pre>";
Result:
Array
(
[winner] => Array
(
[0] => 79
)
[score] => 18
[tiebreaker_hole] => Array
(
[hole] => 6
[score] => 14
)
)
It's 2 days that i'm trying to understand why i get so illogical results. Let me explain with an example. I create invoices based on this simple array.
Array
(
[0] => Array
(
[userid] => 208
[number_of_services] => 3
[username1] => sara
[income1] => 10
[username2] => mark
[income2] => 18
[username3] => joe
[income3] => 12
)
[1] => Array
(
[userid] => 4
[number_of_services] => 1
[username1] => bruce
[income1] => 5
)
[2] => Array
(
[userid] => 303
[number_of_services] => 1
[username1] => michael
[income1] => 7
)
)
With this array i want to create 3 invoices for clients with the following ids: 208, 4, 303. I can make it with with a simple foreach.
foreach($myarray AS $key => $value)
{
// create my invoice
}
Now as you can see client with ID 208 has multiple usernames. I store them as [username1], [username2], [username3] and so on.
[0] => Array
(
[userid] => 208
[number_of_services] => 3
[username1] => sara
[income1] => 10
[username2] => mark
[income2] => 18
[username3] => joe
[income3] => 12
)
I want to display each username as new line (item) in the invoice so i do the following:
foreach($myarray AS $key => $value)
{
for($n=1; $n<=$value["number_of_services"]; $n++)
{
$invoice["itemdescription$n"] = "Username: ".$value["username$n"]."";
$invoice["itemamount$n"] = $value["income$n"];
$invoice["itemtaxed$n"] = 1;
}
}
Now with this cycle i should have:
1 invoice for client 4 with 1 item (bruce -> 5 euro)
1 invoice for client 303 with 1 item (michael -> 7 euro)
1 invoice for client 208 with 3 item (sara -> 10, mark -> 18, joe -> 12)
But i always get:
1 invoice for client 4 with 3 item (bruce -> 5 euro, mark -> 18, joe -> 12)
1 invoice for client 303 with 3 item (michael -> 7 euro, mark -> 18, joe -> 12)
1 invoice for client 208 with 3 item (sara -> 10, mark -> 18, joe -> 12)
I don't get the reason why the script keeps "merging" [username2], [income2] and [username3], [income3] of client 208 into client 4 and 303! I debugged the script in every single step and the problems seems to be in the for() cycle.
Any idea?
foreach($myarray AS $key => $value)
{
for($n=1; $n<=$value["number_of_services"]; $n++)
{
$invoice[] = array(
"itemdescription" => "Username: " . $value . "username" . $n,
"itemamount" . $n = $value . "income" . $n,
"itemtaxed" . $n = 1
);
}
}
Try this.
I'm trying to find each missing number in an array like the following.
Array (
[0] => 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 [39] => 40 [40] => 41 [41] => 42 [42] => 43
[43] => 44 [44] => 45 [45] => 46 [46] => 47 [47] => 48 [48] => 49 [49] => 50
[50] => 51 [51] => 52 [52] => 53 [53] => 54 [54] => 55 [55] => 56 [56] => 57
[57] => 58 [58] => 59 [59] => 60 [60] => 61 [61] => 62 [62] => 63 [63] => 64
[64] => 67 [65] => 68 [66] => 69
)
The numbers 65,66 are missing in this particular array.
My question how do I figure out which numbers are missing with the help of PHP. Specifically what I need to find out is the lowest missing number.
Why: Because then I can assign that number to a member as an id.
You can make use of array_diff and range functions as:
// given array. 3 and 6 are missing.
$arr1 = array(1,2,4,5,7);
// construct a new array:1,2....max(given array).
$arr2 = range(1,max($arr1));
// use array_diff to get the missing elements
$missing = array_diff($arr2,$arr1); // (3,6)
I'm assuming the number is the element, not the key, of the array. I'm also assuming that the numbers start from 1, not 0.
$Expected = 1;
foreach ($InputArray as $Key => $Number)
{
if ($Expected != $Number)
{
break;
}
$Expected++;
}
echo $Number;
For big sorted arrays of unique numbers, you can binary search the array for either the lowest or highest unused number. Cost=Log2N. Example: 65536 items can be searched in 16 loops since
if ( arr[hi] - arr[lo] > hi - lo )
... there are unused numbers in that range ...
So (I don't know PHP, but it can be translated...):
lo = first entry index
hi = last entry index
if ( arr[hi] - arr[lo] == hi - lo )
return arr[hi]+1; // no gaps so return highest + 1
do
{
mid = (lo + hi) / 2;
if ( arr[mid] - arr[lo] > mid - lo ) // there is a gap in the bottom half somewhere
hi = mid; // search the bottom half
else
lo = mid; // search the top half
} while ( hi > lo + 1 ); // search until 2 left
return arr[lo]+1;
If given input is not in sorted order and size of input is very large then we can use following logic in any programming language:
Algorithm
bring smaller chunk into memory from large input
initialize three variables say min = 0, max = 0 and missingIds = []
scan smaller chunked input from left to right
if scannedValue found in missingIds
then,
pop scannedValue from missingIds
go to next value;
If scanned value is near to min
then,
find all the missing numbers between scannedValue and min, push into missingIds
min = scannedValue;
Else if scanned value is near to max
then,
find all the missing numbers between scannedValue and max, push into missingIds
max = scannedValue;
repeat above steps until large input scanned from left to right
Example in PHP
<?php
$largeInput = [40,41,42,43,44,45,1,2,3,4,5,6,7,8,9,10,11,12,13,14,35,36,37,38,39,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,67,68,69,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34];
$missingIds = [];
$min = 0;
$max = 0;
$chunkSize = 10;
$chunkNo = 0;
$currentInput = array_slice($largeInput, $chunkNo, $chunkSize);
while(count($currentInput) > 0) {
foreach($currentInput as $id) {
if(in_array($id,$missingIds)) {
$missingIds = array_diff($missingIds,[$id]);
continue;
}
if($id <= $min) {
$distMin = $min - $id;
if($distMin > 2) {
$tempArr = range($id+1,$min-1);
$missingIds = array_merge($missingIds, $tempArr);
$tempArr = [];
} else if ($distMin > 1) {
$tempArr = [$id+1];
$missingIds = array_merge($missingIds, $tempArr);
$tempArr = [];
}
$min = $id;
} else if ($id >= $max){
$distMax = $id - $max;
if($distMax > 2) {
$tempArr = range($max+1,$id-1);
$missingIds = array_merge($missingIds, $tempArr);
$tempArr = [];
} else if ($distMax > 1) {
$tempArr = [$max+1];
$missingIds = array_merge($missingIds, $tempArr);
$tempArr = [];
}
$max = $id;
}
}
$chunkNo++;
$currentInput = array_slice($largeInput, $chunkNo, $chunkSize);
}
print_r($missingIds);
//$idArrayMissing = array([0] => 1, [1] => 2, [2] => 4, [3] => 5, [4] => 6, [5] => 7);
$idArrayMissing = array(1, 2, 4, 5, 6, 7);
//$idArrayFull = array([0] => 1, [1] => 2, [2] => 3, [3] => 4, [4] => 5, [5] => 6);
$idArrayFull = array(1, 2, 3, 4, 5, 6);
function gap($arr)
{
while (list($k, $v) = each($arr))
if ($k != ($v-1))
return $k;
return -1;
}
print "ok:" . gap($idArrayMissing) . "<br/>\n";
print "full:" . gap($idArrayFull) . "<br/>\n";
The return of the gap function can be 2 values:
-1 could indicate that the array has been traversed and there are no free slots or
$k+1 which could indicate that the first free slot is on the end of the array.
It can also be done easily by using in_array() function like this:
// lets say $InputArray has all the data
// lets declare a variable which we will search inside the $InputArray array and lets initialize it with either 0 or 1 or with the minimum value found inside $InputArray
$start_counting = 1;
$max_value = count($InputArray);
if (!(in_array($start_counting, $InputArray)))
{
echo "Value: ".$start_counting." is missing!"."<br>" ;
}
else{
if($start_counting <= $max_value -1)
{$start_counting++;}
}
else if($start_counting > $max_value -1)
{
echo "All missing numbers printed!"
}
}