PHP - Convert a messy string to a useable one - php

I have a bunch of data from a football team that needs tidying up. Currently, it looks like this (for demonstration purposes, I've only included 3):
1
Team One
9 7 1 1 31 13 18 22
2
Team Two
9 6 2 1 25 21 4 19
3
Team Three
9 4 3 2 26 18 8 14
For clarity, I'll deconstruct the first 3 lines:
1\t\n
Team One\n
9\t7\t1\t1\t31\t13\t18\t22
Notice how there is a tab and then a linebreak after the position of each team. Then, the team name on the next line, with just a linebreak. And then finally, all of the details about that team. Then the next team's stats start.
I need it to be converted to:
1,Team One,9,7,1,1,31,13,18,22
2,Team Two,9,6,2,1,25,21,4,19
3,Team Three,9,4,3,2,26,18,8,14
Each line starts with the team's position, then team name, then each stat -- all separated by commas.
I've attempted doing this with very little luck. I imagine some kind of fancy regex can do the trick, but I wouldn't know how... hopefully someone can help!

You can use
$in = file("log.txt");
$out = fopen("php://output", "w");
foreach(array_chunk($in, 3) as $group) {
$group = array_map("trim", $group);
$group[2] = implode(",", str_getcsv($group[2], "\t"));
fputcsv($out, $group);
}
Output
1,"Team One","9,7,1,1,31,13,18,22"
2,"Team Two","9,6,2,1,25,21,4,19"
3,"Team Three","9,4,3,2,26,18,8,14"
If you want empty enclosure then use
fputcsv($out, $group, ",", " ");
Output
1, Team One , 9,7,1,1,31,13,18,22
2, Team Two , 9,6,2,1,25,21,4,19
3, Team Three , 9,4,3,2,26,18,8,14

<?php
$resultstr = array();
foreach($Teams as $items){
$resultstr[] = $items['Team One'];
}
$items = implode(", ",$resultstr);
echo $items;
?>
something like that. You can edit it with your own data because you did not mention that in your question.

Related

How Do I Implement Incomplete Answer to Old Question

This comment looks like it would work if the author included the value for $numbers. They say it is some type of array, but don't provide enough information to replicate it. I picture some hard coded array ranging from 0 to 9, but I can't help think that such an array would miss numbers greater than 9. What does the numbers array in this example look like?
$text = "1 out of 23";
if(preg_match_all('/\d+/', $text, $numbers))
$lastnum = end($numbers[0]);
I would just post a comment asking whoever wrote that to paste the value for $numbers, but it says I need reputation points to do that.
See How do I grab last number in a string in PHP?
To answer your initial question print_r() can be used to output all contents of an array. e.g. print_r($numbers)
https://3v4l.org/2jA1b
To explain the code:
\d is a single number
+ is a quantifier meaning one or more of the previous character or group
so this would find all numbers in a string. The $numbers[0] would be all numbers, 1 per index, and the end() pulls to the last number/index. Each index would be a number, the 0 is all matches, each indice at the root level is a capture group.
This code wouldn't work as intended for decimals or comma delimited integers. In those cases the numbers would be split up at the delimiter. 1.0 would become 1 and 0 (2 different numbers).
You could rewrite this as:
$text = "1 out of 23";
if(preg_match('/.*\K\D\d+/', $text, $numbers))
echo $numbers[0];
so the end function is not needed. This pulls everything until the last number then forgets everything before the last number.
What you are trying to do is likely easier using preg_split instead of preg_match_all. We can split the input text by the matched regex (digits) and then rebuild the string while incrementing the numbers as we go.
<?php
function incrementNumbers($text) {
// NOTES:
// parenthesis are important in the regex in order to return the captured values
// the -? will capture negative numbers too if necessary
// PREG_SPLIT_DELIM_CAPTURE allows the captured values to be returned too
$split = preg_split('/(-?\d+)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
$return = '';
foreach($split as $i => $s) {
// because we didn't use PREG_SPLIT_NO_EMPTY, $split[0] will either be an empty string if
// $text began with a number, or the text before the first number. Either way, $split alternates
// between non-number [0], number [1], non-number [2], number [3], etc which is why we can detect
// even or odd indexes to determine if this is a number that needs to be incremented or not
if ($i % 2 === 0) {
$return .= $s;
} else {
$return .= (intval($s) + 1);
}
}
return $return;
}
Examples:
echo incrementNumbers("1 out of 23 with 1 and 1 and 24 and 23");
echo incrementNumbers("1 1 2 2 3 3 2 2 1 1");
echo incrementNumbers("0 1 2 3 4 5 6 7");
echo incrementNumbers("-3 -2 -1 0 1 2 3 4 5 6 7");
echo incrementNumbers("there are no numbers in this text");
echo incrementNumbers("does not start 999 with a number 123 nor end 17 with a number");
Outputs:
2 out of 24 with 2 and 2 and 25 and 24
2 2 3 3 4 4 3 3 2 2
1 2 3 4 5 6 7 8
-2 -1 0 1 2 3 4 5 6 7 8
there are no numbers in this text
does not start 1000 with a number 124 nor end 18 with a number
Working example at https://3v4l.org/iKskO

Undefined index error on key that has been declared

I am building a leaderboard (scorebored) for a poker tournament. My aim is to echo data to a table to display scores for the season and scores all time. I want the table to run in order with the highest season points at the top.
I am getting an error message that reads:
Notice: Undefined index: season in C:\wamp\www\UPT Site\leaders.php on line 11
When I print_r an array from the $allplayers array, it shows that all the players arrays are going in correctly, including the [season] key and value declared on line 6 in the bit below...
Can anyone please tell me how to fix my code? (note, the real code doesn't have line numbers in it, I just added them here to make discussion easier).
1 foreach($allplayers as $player){
2 $i = $player[1];
3 if (${"seasonplayerid" . $i}){
4 $sum = array_sum(${"seasonplayerid" . $i});}
5 //$sum = points this season.
6 ${"playerid" . $i}['season'] = $sum;
7 }
8 function val_sort($array,$key) {
9 //Loop through and get the values of our specified key
10 foreach($array as $k=>$v) {
11 $b[] = strtolower($v[$key]);
12 }
13 asort($b);
14 /* foreach($b as $k=>$v) {
15 $c[] = $array[$k];
16 }return $c;
17 */
18 }
19 $sorted = val_sort($allplayers, '[season]');
20 foreach($allplayers as $player){
21 $i = $player[1];
22 echo ("<tr><td>" . $player[0] . $t . ${"playerid" . $i}[3] . $t . ${"playerid" . $i}[4] . $t. ${"playerid" . $i}['season'] . $t. count(${"seasonplayerid" . $i}). "</td><tr>");
23 }
Here is the print_r output for array $playerid1:
Array ( [0] => Jonathan Thompson [1] => 1 [2] => 2015-S 3 [3] => 944 [4] => 7 [season] => 470 )
Here is a key of the information in the array:
/*
$allplayers is a multidimentional array, containing many arrays of players called $playerid1, $playerid2, $playerid3 etc
playerid1[0] = Player name
playerid1[1] = Player ID
playerid1[2] = Current season
playerid1[3] = total points earned
playerid1[4] = total games played games
playerid1[season] = points earned in current season
*/
Looks like at line 19 you are trying to pass key in second parameter but defined it incorrectly.So, to call function val_sort($array,$key) you have to do something like that.
Therefore at line 19 change
$sorted = val_sort($allplayers, '[season]');
to
$sorted = val_sort($allplayers, 'season');
Also i suggest you to use data table it is good and fast
Instead of below line
$sorted = val_sort($allplayers, '[season]');
you should pass the key as below
$sorted = val_sort($allplayers, 'season');
Hope this helps.
I did a dodgy quick fix in this instance.
I wrote a loop to array_pop each array inside the main array, and created a new variable for each piece of data as the array_pop loop was going.
I also array_pushed the data I wanted to sort by and an ID number to a new array.
I used rsort to sort the new array, then looped through it using the ID number to reconstruct a 3rd array in the order I wanted to output data.
Next time I will take the advice of #rocky and do a data table. I may even go back to redo this page as a data table. To be honest, I'd never heard of a data table before yesterday. Once I've given this project over to the boss I'll be investing my time in learning about data tables and AJAX.
Thank you all for your tips, but the quick fix will have to do this time as I am time poor (This site needs to be live tomorrow morning).

Splitting a list of strings into new lists of similar length

Say I have a list of 65 strings.
I need to split this single list into multiple "pools" that have a similar amount of strings.
The amount cannot be over 32.
In this list of 65, they're all ranked from 1st to 65th.
For a list of 65 strings, it'd split into one pool of 21, and two pools of 22.
For a list of 34 strings, it'd split into one pool of 18, and one pool of 18.
For a list of 115 strings, it'd split into one pool of 28, and three pools of 29.
And so on.
However, the new lists need to be fairly ranked.
In example it should be like so:
rank 1 in pool 1
rank 2 in pool 2
rank 3 in pool 3
rank 4 in pool 1
rank 5 in pool 2
rank 6 in pool 3
This way, rank 1 and rank 4, become rank 1 and 2 in their new list.
Same goes for the rest.
I'm thinking I'd need to use array_chunk in combination with a modulo operation, but I can't seem to wrap my head around it.
This is not as difficult as it might seem:
// settings
$cellCount = 115;
$maxPoolSize = 32;
// create test array with numbered strings
$testArray = array_fill(1,$cellCount,'Cell ');
foreach ($testArray as $key => $value) $testArray[$key] .= $key;
// determine the number of pools needed
$arraySize = count($testArray);
$poolCount = ceil($arraySize/$maxPoolSize);
// fill the pools
$poolNo = 0;
foreach ($testArray as $cell)
{
$poolArray[$poolNo][] = $cell;
$poolNo++;
if ($poolNo == $poolCount) $poolNo = 0;
}
// show result
echo '<pre>';
print_r($poolArray);
echo '</pre>';
I'm sure there are other solutions, but this seems to do the job.
I think you're on the right way, by using array_chunk and a modulo operation, that's the way i would choose.
It would be like:
$countarray = count($myarray);
$modulo = 2;
while ($countarray>32)
{
$result = $countarray/$modulo;
if($result>32)
$modulo++;
}
$newpool = array_chunk($myarray, $modulo);
I'm not a god in php, so i hope it will help ! Sorry for my poor english.

php remove redundant words from string range

I have a range of information. For example:
Volume 1 Chapter 3 Page 5 TO Volume 1 Chapter 5 Page 10
what is the fastest way to remove redundant information and convert this to:
Volume 1 Chapter 3 Page 5 TO Chapter 5 Page 10
OR if the input is
Volume 1 Chapter 3 Page 5 TO Volume 1 Chapter 3 Page 10
then output
Volume 1 Chapter 3 Page 5 TO Page 10
The hardest part here is to split the input into tokens, as it's not structured well enough. I used a recursive function to sequentially clean the string of the first element duplicates. It works correctly for this input, but I am not sure, that it's 100% correct, as input structure is unclear:
<?php
$str = 'Volume 1 Chapter 3 Page 5 TO Volume 1 Chapter 3 Page 10';
$str = clear_first_element_duplicates($str);
var_dump($str);
function clear_first_element_duplicates($str)
{
if (preg_match('/(.*?\d)\s(.*)/', $str, $tokens))
{
$regexp = preg_quote($tokens[1]);
$str = preg_replace("/$regexp\s?/", '', $tokens[2]);
return $tokens[1]." ".clear_first_element_duplicates($str);
}
return $str;
}
Prints:
"Volume 1 Chapter 3 Page 5 TO Page 10"
My script seems complex but worth it:
I Added variable Levels, So it's not restricted to just Volume, chapter and pages, you can add for example paragraph line and character if desired, and you can even change the Wording. See examples at the end.
** BE careful with the $separator parameter, it must be Exact(case sensitive) and might appear only once on the script, this is easy to fix but I focused on the important part of the function **
function redundancy($string, $separator){
list($a, $b) = explode($separator, $string);
//getting the numeric values of both sides
$pattern = '/[0-9]+/';
preg_match_all($pattern, $a, $a_values);
preg_match_all($pattern, $b, $b_values);
$a_values = $a_values[0];
$b_values = $b_values[0];
//getting the wording and cleaning out the numbers, I guess this can be improved through a better REGEX
preg_match_all('/\b\w+\b/', $a, $matches);
foreach($matches[0] as $match){
if(!is_numeric($match)) $words[] = $match;
}
//algorithm
$length = count($a_values) - 1; // excluding the last element, to be checked separately
$output = $a.$separator." ";
$same_full_path = true; // check if the levels has been altered to check the last element
$same_parent = true; // check the previous level
for($i = 0; $i < $length; $i++){
if($a_values[$i] !== $b_values[$i] || $same_parent === false){
$same_parent = false;
$same_full_path = false;
$output .= $words[$i]." ".$b_values[$i]." ";
}
}
//adding the word to the last element or not, The last element check must be outside the loop because it's special;
if($same_full_path === false || end($a_values) === end($b_values)) $output .= end($words)." ";
$output .= end($b_values);
echo "$string <Br/> $output; <br/><br/> ";
}
redundancy('Volume 1 Chapter 3 Page 5 TO Volume 1 Chapter 5 Page 10', 'TO');
redundancy('Serie 1 Season 2 Chapter 2 Minute 5 Second 6 Until Serie 1 Season 3 Chapter 4 Minute 3 Second 1', 'Until');
redundancy('District 4 Building 2 Floor 4 Door 5 To District 4 Building 2 Floor 4 Door 8', 'To');
Outputs :
Volume 1 Chapter 3 Page 5 TO Volume 1 Chapter 5 Page 10
Volume 1 Chapter 3 Page 5 TO Chapter 5 Page 10;
-
Serie 1 Season 2 Chapter 2 Minute 5 Second 6 Until Serie 1 Season 3 Chapter 4 Minute 3 Second 1
Serie 1 Season 2 Chapter 2 Minute 5 Second 6 Until Season 3 Chapter 4 Minute 3 Second 1;
-
District 4 Building 2 Floor 4 Door 5 To District 4 Building 2 Floor 4 Door 8
District 4 Building 2 Floor 4 Door 5 To 8;

How can I generate a round robin tournament in PHP and MySQL?

I need to generate sequences of games using the round robin algorithm. I have the php page where the user can input the tournament name which will be inserted into the database and it has got a drop down menu up to 32 teams (select number of teams).
So if I select 4 teams in the page, so it will be from team 1 to team 4 which would be 6 matches because every team plays the other team once. I know how the algorithm works but I am not quite sure how to write the query for that.
I created the table team:
Team_id 01 02 03 etc
Team_name Team1 Team2 Team3 etc.
What should I do from here?
I created a roundrobin function from scratch as i thought it might be easier to get the same results and also allowing me to use arrays filled with strings directly instead of numbers.
Because i pull a list of names from a database and add into an array i can now schedule this directly with below function. No extra step needed to link numbers to names etc.
Please feel free to try it and if it works then leave a comment.
I also have a version which allows for 2 way (home & return) schedule and or shuffle option. If somone is interested in that one then leave a coment as well.
<?php
/**
* #author D.D.M. van Zelst
* #copyright 2012
*/
function scheduler($teams){
if (count($teams)%2 != 0){
array_push($teams,"bye");
}
$away = array_splice($teams,(count($teams)/2));
$home = $teams;
for ($i=0; $i < count($home)+count($away)-1; $i++){
for ($j=0; $j<count($home); $j++){
$round[$i][$j]["Home"]=$home[$j];
$round[$i][$j]["Away"]=$away[$j];
}
if(count($home)+count($away)-1 > 2){
array_unshift($away,array_shift(array_splice($home,1,1)));
array_push($home,array_pop($away));
}
}
return $round;
}
?>
How to use, for example create an array like:
<?php $members = array(1,2,3,4); ?>
or
<?php $members = array("name1","name2","name3","name4"); ?>
then call the function to create your schedule based on above array:
<?php $schedule = scheduler($members); ?>
To display the resulted array schedule simply do like below or anyway you like:
This little code displays the schedule in a nice format but use it anyway you like.
<?php
foreach($schedule AS $round => $games){
echo "Round: ".($round+1)."<BR>";
foreach($games AS $play){
echo $play["Home"]." - ".$play["Away"]."<BR>";
}
echo "<BR>";
}
?>
Leave a note if it worked for you or if you are interested in the 2-way version with shuffle.
There is a fairly simple algorithm for doing round-robin matchups, my solution would be as follows (in pseudo-code):
fetch all the teams out of the database into an array, in any order
for (i = 1; i < number of teams; i++)
print matchups for Round #i:
the teams in the first half of the array are matched up in the same order with the teams in the last half of the array. That is, the team at any index [n] is matched up with the team at index [n + half the number of teams]. If you have 32 teams, [0] is matched with [16], [1] with [17], etc up to [15] and [31].
Now, "rotate" the teams through the array, but leave the first one in the array in place. That is, [1] becomes [2], [2] becomes [3], ..., up to [31] becomes [1], but do not move the team at index [0].
And that's it, that will produce all the matchups you need.
An example, with 4 teams:
First half of the array is on top, second half is on the bottom, match-ups are numbers above/below each other. Array indexes (to illustrate what I mean exactly):
[0] [1]
[2] [3]
Round 1:
1 2
3 4
Round 2:
1 4
2 3
Round 3:
1 3
4 2

Categories