splitting a large array() on a specific index - php

I have a form on my site for the users to post a results report into,
the report looks like this:
Nojoks's Tourney Bracket Tool Version 1.2.1.84
Tournament: 3/5 Backgammon 1:00pm
Date: 01/22/2017
Day: Sunday
Scheduled Start: 1.00pm PST
Actual Start: 20:00:30
Closed: 20:11:00
Host: Waiter ()
Number of Players: 15
1st place: poppop
1st place email: bobmitch1170#gmail.com
2nd place: Sarge
2nd place email: rgarvey5#hotmail.com
3rd place: Litigolfer
3rd place email: dostrow2008#gmail.com
3rd place: PhantomMask
3rd place email:
START POINTS
burnieboy 5
EU_BNL_Chris1 5
EU_IT_VIANG 5
GennaLee 5
happybear 5
MC_Vicky 5
merceaviles 5
MRC_cadet 5
poeticfool 5
UBG_Angel_D_8 5
UBG_sara1smoon 5
Litigolfer 60
PhantomMask 60
Sarge 90
poppop 120
STOP POINTS
this report is going to be identical everytime with some minor changes
I have already split this into an array with explode
$records = explode( PHP_EOL, $_POST['points'] );
$records = array_map('htmlspecialchars', $records );
$records = array_map ('trim', $records);
Then i have work on collecting the information from the top of the report like so:
// Get Date
$date = substr($records[2], 7, 10);
echo "<b>Tournament Date: </b>" . $date . "<br />";
// Get star time
$start_time = substr($records[4], 18, 7);
echo "<b>Tournament Start Time: </b>" . $start_time . "<br />";
now i need to work on everything from $records[20] down
what i need to do is simple enough i just do not know how to get to the correct part of my array first
I used to ask my users to post only the information from the START POINTS line down to STOP POINTS so to get my information out and split was simple i used:
foreach( $records as $record ) {
$lastSpace = strrpos( $record, ' ' );
$player = trim( substr( $record, 0, $lastSpace ) );
$points = trim( substr( $record, $lastSpace+1 ) );
this code will still work in this case if i can drop the index's 0 - 19 or just split the array into a new array $records1
P.S its this section in the report that is ever changing so to speak this report is from a online tournament hosting tool and each tournament has no set amount of players it can range from 8 upwards

I'm not sure I understand what you need. Perhaps something like this?
The idea is that you search for the indexes in the array which contain the start and end of the points and do a for loop to iterate only over those points.
$start = array_search("START POINTS", $records);
$end = array_search("END POINTS", $records);
$playerArray = [];
for ($i = $start+1;$i < $end;$i++) {
$parts = explode(" ",$records[$i]);
$player = $parts[0];
$points = $parts[1];
$playerArray = [ "player" => $player, "points" => $points ];
}

I managed to work this problem out for myself with thanks to #apokryfos your code still did not do quiet what i wanted so i used your codes and added them into what i have now,
Here is what i came up with:
// New try to split a full report in one go.
$points = $date = $day = $start_time = $host = $number_of_players = $fp_name = $fp_email = $sp_name = $sp_email = "";
// Explode the string at the end of each line,
// array map and remove any special chars then trim white space.
$records = explode( PHP_EOL, $_POST['points'] );
$records = array_map('htmlspecialchars', $records );
$records = array_map ('trim', $records);
// now each line of the NJ's report is in an array, call the array $records[index number]
// use substr( , , ) to find the needed part of the array indexs,
// I.E in the array $records on line 2 is the Date the actual needed information is the date dd/mm/yyyy,
// so we use $date = substr($records[2], 7, 10);
// from this string : Date: 01/18/2017 which is line 2 in records we get the 01/18/2017
// Get Date
$date = substr($records[2], 7, 10);
echo "<b>Tournament Date: </b>" . $date . "<br />";
// Get star time
$start_time = substr($records[4], 18, 7);
echo "<b>Tournament Start Time: </b>" . $start_time . "<br />";
// get Host name
$host = substr($records[7], 7);
echo "<b>Tournament Host: </b>" . $host . "<br />";
// get number of players
$number_of_players = substr($records[8], 20);
echo "<b> Number Of Players: </b>" . $number_of_players . "<br />";
echo "<br />";
// get the first place name and email address
$fplaceName = substr($records[10], 12);
echo "<b>1ST place: </b>" . $fplaceName . "<br />";
$fplaceEmail = substr($records[11], 18);
echo "<b>1ST place Email: </b>" . $fplaceEmail . "<br />";
// Get second place name and email
$splaceName = substr($records[12], 12);
echo "<b>2ND place Email: </b>" . $splaceName . "<br />";
$splaceEmail = substr($records[13], 18);
echo "<b>2ND place Email: </b>" . $splaceEmail . "<br />";
// get third place name and email
$tplaceName = substr($records[14], 12);
echo "<b>3RD place Email: </b>" . $tplaceName . "<br />";
$tplaceEmail = substr($records[15], 18);
$t1placeEmail = "fake#fake.com";
if($tplaceEmail == "") { // if third place email is empty add a generic fake email else continue as normal
$tplaceEmail = $t1placeEmail;
} ;
echo "<b>3RD place Email: </b>" . $tplaceEmail . "<br />";
echo "<hr /><br /><br />";
// Getting the players and points.
$parts1 = array_slice($records, 20, -1);
$end = array_pop($parts1);
$records = array_map('htmlspecialchars', $records );
$records = array_map ('trim', $records);
foreach( $parts1 as $record ) {
$lastSpace = strrpos( $record, ' ' );
$player = trim( substr( $record, 0, $lastSpace ) );
$points = trim( substr( $record, $lastSpace+1 ) );
echo $player . " " . " " . " " . $points . "<hr /><br />";
}
so what happens is, some user pastes the report as above in my original post,
and hits submit,
the form posts to my processing.php page,
I explode the whole post "string" into an array $records,
array_map the htmlspecialchars and trim the records,
then there is all the codes to extract the Date, time, host, number of players,
Then we get the 1st, 2nd, 3rd names and email address's,
then we get to sorting the playernames and points,
array_slice from the line after STOP POINTS index[20] also -1 for the STOP POINTS index,
pop the last line from the array "STOP POINTS" gone,
re do the array_map's on the new array,
run a foreach loop on the new array using a lastSpace strpos " " and the new array
then split player name from points....
if i run the finished code on the report given in my original post you get the following output:
Music Cafe Tournament Date: 01/22/2017 Tournament Start Time: 1.00pm
Tournament Host: Waiter () Number Of Players: 15
1ST place: poppop 1ST place Email: bobmitch1170#gmail.com 2ND place
Email: Sarge 2ND place Email: rgarvey5#hotmail.com 3RD place Email:
Litigolfer 3RD place Email: dostrow2008#gmail.com
burnieboy 5
EU_BNL_Chris1 5
EU_IT_VIANG 5
GennaLee 5
happybear 5
MC_Vicky 5
merceaviles 5
MRC_cadet 5
poeticfool 5
UBG_Angel_D_8 5
UBG_sara1smoon 5
Litigolfer 60
PhantomMask 60
Sarge 90
poppop 120
of course with the rule separating each name and points,
which is what i needed and now i can add all my sql statements and bingo.
please if anyone can see an easier or better way to achieve this goal i would love to see your edits and test them out

Related

Win Rate calculation - Php and api from leaguepedia

i'm using the api from leaguepedia to receive the informations.
So I have this code to show how many times a champion was picked in a tournament, its working fine, but
right now im trying to show the winrate and i'm having problems,
for example :
Syndra picked 4 Times - Won 3 times - winrate = 75%, thats the result i expect;
Gragas picked 3 Times - Won 3 times - winrate = 100%, thats the result i expect;
what im recieving is :
Syndra picked 4 Times - Won 1 time - winrate = 25%
Gragas picked 3 Times - Won 1 time - winrate = 33,33% , victory is showing 1 for every champion (image)
i know that my problem might be on the "switch / case", but i don't know how to fix
so how can i fix my code to show the properly win rate.
thanks
thats my code
<?php
// $Result is a CURL coming from leaguepedia api
$result = json_decode($file_contents);
$heroPicks = [];
$heroVictory = [];
// Double foreach to access the values from leaguepedia api
foreach ($result as $d) {
foreach ($d as $data) {
//$data->title->Team1Picks and
// $data->title->Team2Picks is coming from leaguepedia api
//as a string separated by "," Ex:("Gragas,Shen,Maokai,etc")
// So i need to explode and merge to an array to count.
$picks1 = explode(",", $data->title->Team1Picks);
$picks2 = explode(",", $data->title->Team2Picks);
$picks = array_merge($picks1, $picks2);
// this switch is to check if
// $data->title->Winner == 1 , it means that
// $picks1 won the game else == 2 $picks2
// won the game ($data->title->Winner is coming from api aswell)
switch ($data->title->Winner) {
case 1:
$w = $picks1;
break;
case 2:
$w = $picks2;
break;
}
//foreach to count the times a champion was picked
foreach ($picks as $pick) {
$pick = trim($pick);
if (!array_key_exists($pick, $heroPicks)) {
$heroPicks[$pick] = 0;
}
$heroPicks[$pick] += 1;
}
//foreach to count how many times a champion won a game
foreach ($w as $victory) {
$victory = trim($victory);
if (!array_key_exists($victory, $heroVictory)) {
$heroVictory[$victory] = 0;
}
$heroVictory[$victory] += 1;
}
}
}
//sorting the arrays
uasort(
$heroPicks,
function ($a, $b) {
return $a - $b;
}
);
uasort(
$heroVictory,
function ($c, $d) {
return $c - $d;
}
);
$heroPicks = array_reverse($heroPicks);
$heroVictory = array_reverse($heroVictory);
//foreach to show the results
echo "Best picks:" . PHP_EOL . "<br />";
foreach ($heroPicks as $heroName => $pickCount) {
foreach ($heroVictory as $heroVictoryName => $totalVictory) {
$total = ($totalVictory * 100) / $pickCount;
}
echo $heroName . " - " . $pickCount . PHP_EOL . " - Victorys = " . $totalVictory . " -- winrate :" . $total . "%" . "<br />";
}
?>
the $result variable for #bassxzero
The outer foreach will loop through all of your heroes that were picked. This is what you want.
The inner foreach is looping through all of your heroes' victories, regardless of the hero the outer loop is currently processing. All the victories are set to 1 because that is how many victories the last hero in your victory array has. This is not what you want.
You don't want a second inner foreach. You just want to lookup the victory stats for the hero currently being processed by the outer foreach.
foreach ($heroPicks as $heroName => $pickCount) {
$totalVictory = $heroVictory[$heroName] ?? 0;
$total = ($totalVictory * 100) / $pickCount;
echo $heroName . " - " . $pickCount . PHP_EOL . " - Victorys = " . $totalVictory . " -- winrate :" . $total . "%" . "<br />";
}

Auto switch between sentences

I would like my joomla site to automatically change which sentence it will echo to the user, so I wrote 3 different sentences:
$sentence1 = "Everything okay?";
$sentence2 = "Have a good day";
$sentence3 = "What are you doing today?";
I would like it to switch between the sentences, so I'm aware I can't just put $sentence1 in the echo but I don't know how to write it then. I have the echo line like this:
echo "Hey {user->name}." . "<br />" . $sentence1
By the way, the {user->name} is from Joomla's own "codes" so that worked fine :)
random greetings:
$sentence[1] = "Everything okay?";
$sentence[2] = "Have a good day";
$sentence[3] = "What are you doing today?";
echo "Hey {user->name}." . "<br />" . $sentence[rand(1,3)]
You could use a random function, such as mt_rand():
$sentence1 = "Everything okay?";
$sentence2 = "Have a good day";
$sentence3 = "What are you doing today?";
$nb = mt_rand(1, 3); // Gets a random number from 1 to 3
$sentence_shown = ${'sentence' . $nb}; // Equals $sentence1, $sentence2 or $sentence3
echo "Hey {user->name}." . "<br />" . $sentence_shown;
Or even better, put your three strings in an array :
$sentences = array();
$sentences[] = "Everything okay?";
$sentences[] = "Have a good day";
$sentences[] = "What are you doing today?";
$nb = mt_rand(0, 2); // Gets a random number from 0 to 2
$sentence_shown = $sentences[$nb];
echo "Hey {user->name}." . "<br />" . $sentence_shown;
In my opinion, you can put your 3 sentences or 3 strings in an array, and then you can print every sentence when you loop in it. Example:
$array = array(sentence1, sentence2, sentence3 , ... , sentence n);
for ($index = 0; $index < sizeof($array); $index++) {
echo 'This is sentence ' + index ':' + $array[index];
}

Division with php

Im trying to get out an average value from a vote function.
<?php
$file = file("textfile.txt");
$textfil = file_get_contents("textfile.txt");
$textfill = str_split($textfil);
echo "Number of votes: " . count($textfill) . "<br>";
$sum = 0;
foreach ($textfill as $vote) {
$sum = $sum + intval($vote);
}
echo "Average: " . $sum;
?>
Simple by substitute (+) with a (/), and even tried a (%). But still getting error message.
Would appreciate alot if anyone could help me out and tell me what im doing wrong.
/thanks
Edit
Sidenote: Please read an explanation under "First answer given" further down below.
This version will take into account any blank lines in a file, if the content looks like:
1
2
3
// <- blank line
Sidenote: Please provide a sample of your text file. A comment has already been given to that effect.
PHP
<?php
// first line not required
// $file = file("textfile.txt");
$textfil = file_get_contents("textfile.txt");
$textfill = array_filter(array_map("trim", file("textfile.txt")), "strlen");
echo "Number of votes: " . count($textfill) . "<br>";
$sum = 0;
foreach ($textfill as $vote) {
$sum += intval($vote);
}
$avg = $sum / count($textfill);
echo "Average: " . $avg;
?>
First answer given
Using the following in a text file: (since no example of file content was given)
5
5
5
IMPORTANT NOTE: There should not be a carriage return after the last entry.
produced
Number of votes: 5
Average: 3
which is false, since there are 3 entries in the text file.
explode() should be used, and not str_split()
The following using the same text file produced:
Number of votes: 3
Average: 5
which is correct. In simple mathematics, averages are done by adding all numbers then dividing them by how many numbers there are.
In this case it's 3 numbers (all 5's) added equals 15, divided by 3 is 5.
Sidenote: The first line is not required $file = file("textfile2.txt");
<?php
// first line not required
// $file = file("textfile.txt");
$textfil = file_get_contents("textfile.txt");
$textfill = explode("\n", $textfil);
echo "Number of votes: " . count($textfill) . "<br>";
$sum = 0;
foreach ($textfill as $vote) {
$sum += intval($vote);
}
$avg = $sum / count($textfill);
echo "Average: " . $avg;
?>
Footnotes:
If the average comes out to 8.33333 and you would like it to be rounded off to 8, use:
echo "Average: " . floor($avg);
If the average comes out to 8.33333 and would like it to be as 9 you would use:
echo "Average: " . ceil($avg);
ceil() function
floor() function
You may be mixing in stuff that can't be divided, like text, etc. I don't know what your text file looks like. intval may be having a problem with arrays. You may try:
foreach ($textfill as $vote) {
if(is_int($vote) {
$sum += $vote;
}
}
echo "Average: " . $sum;
Lower school math says:
foreach ($textfill as $vote) {
$sum += intval($vote);
}
$avg = $sum / count($textfill);
The average value is calculated by divide the sum with the number of votes. This line will print the average value:
echo "Average: " . $sum/count($textfill);

PHP regex prevent repeated string

Im trying to create a regular expression in PHP which is checking that a string conforms to the following rules:
Has 1 - 7 occurrences of one of the following strings (Mon,Tues,Wed,Thurs,Fri,Sat,Sun)
Strings separated by a ,
not case sensitive
No strings are repeated.
I believe I have fulfilled the first three aspects of this with the following:
/^(Mon|Tues|Wed|Thurs|Fri|Sat|Sun)(,(Mon|Tues|Wed|Thurs|Fri|Sat|Sun)){0,6}$/i
I am struggling to get to grips with preventing any repeats. Can anyone advise?
^(Mon|Tues|Wed|Thurs|Fri|Sat|Sun)(?:,(?!\1|\2)(Mon|Tues|Wed|Thurs|Fri|Sat|Sun)){0,6}$
You could use this if regex is an absolute requirement but I'd rather recommend Martijn's answer. It is much more flexible and easier to read.
Here is how i tested this in PHP:
<?php
$subject1 = "Mon,Mon";
$subject2 = "Sun,Mon,Fri,Sun";
$subject3 = "Sat";
$subject4 = "Mon,Wed,Tues,Fri,Wed";
$subject5 = "Mon,Tues";
$pattern = '/^(Mon|Tues|Wed|Thurs|Fri|Sat|Sun)(?:,(?!\1|\2)(Mon|Tues|Wed|Thurs|Fri|Sat|Sun)){0,6}$/i';
print_r(preg_match($pattern, $subject1, $matches) . " " . $subject1 . "\n");
print_r(preg_match($pattern, $subject2, $matches) . " " . $subject2 . "\n");
print_r(preg_match($pattern, $subject3, $matches) . " " . $subject3 . "\n");
print_r(preg_match($pattern, $subject4, $matches) . " " . $subject4 . "\n");
print_r(preg_match($pattern, $subject5, $matches) . " " . $subject5 . "\n");
?>
This outputs:
0 Mon,Mon
0 Sun,Mon,Fri,Sun
1 Sat
1 Mon,Wed,Tues,Fri,Wed
1 Mon,Tues
Does it have to be a regex? If not:
$daysStart = 'Mon,Tues,Wed,mon';
$days = strtolower($daysStart);
$days = explode(",", $days); // split on comma
$days = array_unique($days); // remove uniques
$days = implode(",", $days); // join on comma
// Compare new string to original:
if(strtolower($days)===strtolower($daysStart )){ /*match*/ }
This results in a lowercase string of days, seperated by commas. Not sure what you wanted as output, you might want to save the original input in another far, or ucfirst() the values via an array_map() or something, this is just to show you another method
Or my code shorter:
$daysStart = 'Mon,Tues,Wed,mon';
$days = explode(",", strtolower($daysStart ) );
$days = implode(",", array_unique($days) );
if(strtolower($days)===strtolower($daysStart )){ /*match*/ }
or function (as short code, can be the longer version ofcourse):
function checkDays($string){
$days = explode(",", strtolower($string) );
$days = implode(",", array_unique($days) );
return (strtolower($days)===strtolower($daysStart)) ? true : false;// *
}
*I could've done just the return and the str-checks, but I prefer to add true/false in a way im sure my returnvalue always is true of false as boolean, not truthy or falsy.

Reopened: PHP array_shift(); VS reset(); unset(); array_splice();

Reopened:
Since PHP is a server-side language I had the assumption that it didn't matter what browser I used, but apparently not so. I had only been using Google Chrome.
I installed WAMP on my local machine so I can test it locally to see if it did the same as my shared hosting account. Both codes worked the way they should (in Chrome mind you). Then I looked at the exact same code on my shared hosting -- one worked, the other didn't.
I called my shared hosting support and they tried to duplicate the issue and said they weren't finding it. So I tried it in Firefox with them on the line and in IE, lo and behold... it worked perfectly in both just like on WAMP.
This still isn't making much sense to me. I'm using PHP 5.3 on my shared hosting account with a well known hosting company but not getting anywhere because they can't really troubleshoot code. They can replicate the issue but can't answer why. I'm going to try to find out more in the next weeks and post an update as I know more.
What I'm trying to do is this:
Generate a range of numbers
Shuffle them into a random order
Copy that array of randomly ordered numbers into a session array
Get the first value of the array, delete that value, and shift all
the values down by one
Here's my issue:
I tried using array_shift(); and it worked fine the first run but each time I ran the code after it would remove the first two elements.
To test what was going on I tried to print the array first, do array_shift();, and then print the array again to see what happened.
Expected Results:
Run #1:
[0]=>5 [1]=>2 [2]=>1 [3]=>4 [4]=>3 //print_r($array);
//execute array_shift($array);
[0]=>2 [1]=>1 [2]=>4 [3]=>3 //print_r($array);
Run#2:
[0]=>2 [1]=>1 [2]=>4 [3]=>3 //print_r($array);
//execute array_shift($array);
[0]=>1 [1]=>4 [2]=>3 //print_r($array);
Actual Results:
Run #1:
[0]=>5 [1]=>2 [2]=>1 [3]=>4 [4]=>3 //print_r($array);
//execute array_shift($array);
[0]=>2 [1]=>1 [2]=>4 [3]=>3 //print_r($array);
Run#2:
[0]=>1 [1]=>4 [2]=>3 //print_r($array);
//execute array_shift($array);
[0]=>4 [1]=>3 //print_r($array);
My issue (cont.)
So then I tried using reset($array);, unset($array[0]);, and array_splice($array,1,0); as an alternative to array_shift($array); and it worked! Then I tried to compare them side by side
and cleaned up the code and now they are doing the opposite of each other. Sometimes reset, unset, and array_shift; will even jump through up to 7 cells in the array when called once. array_shift(); is working the way I want it to now but I want to know why. It's driving me nuts! If someone could please help me I'd be so appreciative.
Code Dump:
unset, reset, splice
<?php
session_start();
$min = A;
$max = S;
if((!isset($_SESSION['image'])) || ($_SESSION['image'] == null))
{
$numbers = range($min, $max); //set a range for all images
shuffle($numbers); //shuffle the order for randomness
$_SESSION['image'] = $numbers;
echo "<br />Current value: " . $_SESSION['image'][0] . "<br />";
print_r($_SESSION['image']);
reset($_SESSION['image']);
unset($_SESSION['image'][0]);
array_splice($_SESSION['image'],1,0);
echo "<br />New value: " . $_SESSION['image'][0] . "<br />";
echo "<br />1st exec<br />";
}
else
{
echo "<br />Current value: " . $_SESSION['image'][0] . "<br />";
print_r($_SESSION['image']);
reset($_SESSION['image']);
unset($_SESSION['image'][0]);
array_splice($_SESSION['image'],1,0);
echo "<br />New value: " . $_SESSION['image'][0] . "<br />";
echo "<br />2nd exec<br />";
}
?>
shift
<?php
session_start();
$min = A;
$max = S;
if((!isset($_SESSION['id2'])) || ($_SESSION['id2'] == null))
{
$numbers = range($min, $max); //set a range for all images
shuffle($numbers); //shuffle the order for randomness
$_SESSION['id2'] = $numbers;
echo "<br />Current value: " . $_SESSION['id2'][0] . "<br />";
print_r($_SESSION['id2']);
array_shift($_SESSION['id2']);
echo "<br />New value: " . $_SESSION['id2'][0] . "<br />";
echo "<br />1st execution<br />";
}
else
{
echo "<br />Current value: " . $_SESSION['id2'][0] . "<br />";
print_r($_SESSION['id2']);
array_shift($_SESSION['id2']);
echo "<br />New value: " . $_SESSION['id2'][0] . "<br />";
echo "<br />2nd execution<br />";
}
?>
To ultimately learn what's going on, I propose to register a tick-function. The tick-function can execute at each PHP statement execution.
Here's a simple one, which traces line/file of each statement (feel free to add further details):
// Execute at each single statement
declare(ticks=1);
// Function to get called at each executing statement
function logEachStatement() {
$traceInfo = debug_backtrace();
$lastActivation = $traceInfo[ count( $traceInfo ) - 1 ];
$info = "\n"
. $lastActivation[ 'line' ] . '#' . $lastActivation[ 'file' ]
;
$targetFile = dirname( __FILE__ ) . '/stmnt.log' );
file_put_contents( $targetFile, $info, FILE_APPEND );
}
// using a function as the callback
register_tick_function( 'logEachStatement', true );
While there are other options to track down the problem, this one does not require external infrastructure.
Assuming that you pasted the actual code, the only way for this to happen is if you made an unintentional intermediate request. It would be impossible for us to determine how or why this is the case with only the given information, though.
My suggestion is to use your browser dev tool of choice (e.g. Firebug) and monitor the network tab to make sure that only one request is being sent to the script. If this doesn't turn up your problem, add some simple debug logging, such as this:
$log = date('Y-m-d H:i:s')
. ' :: Request received from '
. $_SERVER['REMOTE_ADDR']
. "\n";
file_put_contents('/path/to/your/log/file', $log, FILE_APPEND);
Then check your log after your test to make sure only one entry was added.

Categories