Extract specific data from a String in PHP - php

I am capturing some data after taking a user through a number of steps. This data is successfully being captured in the below String format. Note that due to some limitations, I don't have an option to get this data in any other format.
There are two counts that are being captured. One is a Lap and then within each lap, the number of Reps.
Lap 1 Rep 1, 11, Lap 1 Rep 2, 12, Lap 1 Rep 3, 15, Lap 2 Rep 1, 22, Lap 2 Rep 2, 24, Lap 2 Rep 3, 29
I need to get the below values from the above code in a PHP Array.
11
12
15
22
24
29
Please note that since the user selected the number of Laps and Reps in the process, therefore Laps and Reps can go to double-digit as well. But when I am getting the above values, I have the total count of Laps and Reps as well. Like in the above example, I also have the Laps being 2 and Reps of each Lap being 3.
Can anyone help?

Like stated in the comments, you're best to run a regex query (using this as an example: Lap \d Rep \d, (\d+)):
preg_match_all("/Lap \d Rep \d, (\d+)/", $str, $matches);
Now if you look at $matches[1], you'll get the following:
Array (
[0] => 11
[1] => 12
[2] => 15
[3] => 22
[4] => 24
[5] => 29
)
$str being the string you have there as an example.

This will accomplish what you want. I first split the string by using the explode function, then I only keep every other element by using the mod function and checking the value to see if it is 0:
$info = "Lap 1 Rep 1, 11, Lap 1 Rep 2, 12, Lap 1 Rep 3, 15, Lap 2 Rep 1, 22, Lap 2 Rep 2, 24, Lap 2 Rep 3, 29";
$i = 1;
$keepThese = Array();
foreach(explode(", ", $info) as $value) {
if ($i++ % 2 == 0) {
array_push($keepThese, $value);
}
}
var_dump($keepThese);
PSST: You forgot 15 in your example output.

Related

Format Numbers with possible leading zeroes in PHP

I'm having a hard time to figure out, how to format a range of number correctly.
Lets say I have an array with the following values:
array( 1, 2, 001, 02, 012 );
How can I change the output of each value to fit the following format?
0 => 1
1 => 2
2 => 0.01
3 => 0.2
4 => 0.12
I'm not looking for a loop ( this is just for explanation), just a way to format the number correctly.
This is the way PHP interpret Integer you can see you have no leading zeros on Integer ;-)
var_dump(array( 1, 2, 001, 02, 012 ));
array(5) {
[0]=>int(1)
[1]=>int(2)
[2]=>int(1)
[3]=>int(2)
[4]=>int(10)
}
The only way to solve it is converting it to a String.

Negative value of $step in range(), PHP

This is taken from the php.net manual for json_decode(), Example 4, 5th line from the bottom there:
foreach (range(4, 3, -1) as $depth)
I am not sure what is the purpose of that -1 there. I tried range(4, 3, 1) and range (4, 3, -1) and it gives the same results. The 1 in the first example is the default so it could be range(4, 3). I was only trying it with something like print_r(range(4, 3, -1); so the example on the php.net may be a different thing. I was looking on the Net and there is no info about it, or not right away.
It will actually ignore the sign of the $step argument, and determine whether to increment or decrement based purely on whether $start > $end or $end > $start. For example:
<?php print_r( range( 20, 11, 3 ) ); ?>
Array
(
[0] => 20
[1] => 17
[2] => 14
[3] => 11
)
<?php print_r( range( 11, 20, -3 ) ); ?>
Array
(
[0] => 11
[1] => 14
[2] => 17
[3] => 20
)
Step is automatically cast to a positive number (yes, the manual is incorrect here)
So all of these will work:
var_dump(
range(1, 5, 1), // positive integer
range(1, 5, -1), // negative integer
range(1, 5, 2),
range(1, 5, .5), // positive fractional number
range(1, 5, "-0.1") // negative fractional number passed as string
);
Also, if you use a float value as $step, all the values will be float too, even if they are whole numbers.

SQL query how to match 3 out of 4 results

I have searched a number of different items but I have not found any answers. Chances are I just don't know how to word this correctly.
Anyway, I have set up a system in PHP/SQL to allow instantaneous scanning of thousands of results. Each data entry has 4 numbers, and it can easily scan for entries that match all 4 of these numbers. What I am trying to achieve is to have the script search the database for entries that match exactly 3 out of the 4 entries with the other being incorrect, kind of like a lottery.
For example, we have the entries:
Steve - 1, 4, 10, 13
Bill - 3, 4, 10, 13
Tom - 1, 17, 20, 39
Jill - 1, 4, 13, 21
Jane - 5, 10, 13, 18
Now, I would scan based on the results 1, 4, 10, 13, and would like to return the following results, as these matched 3 of the 4 entries:
Bill - 3, 4, 10, 13
Jill - 1, 4, 13, 21
How would I achieve this?
Many thanks
EDIT: Sorry yes the table has the structure
Name - Number1 - Number2 - Number3 - Number4
So yes, stored as separate fields
You can do this by counting the matches and setting this equal to 3:
select t.*
from t
where (val1 in (1, 4, 10, 13) +
val2 in (1, 4, 10, 13) +
val3 in (1, 4, 10, 13) +
val4 in (1, 4, 10, 13)
) = 3;
In MySQL a TRUE boolean expression evaluates to 1. You can add these together to get the number of matches.

How to make a Round Robin? or Is there an easier way other than Round Robin?

The problem that I face is in what way if there is issue like the example below:
Codes 1000, 2000, 3000, 4000, 5000
ID 1, 2, 3
========================================
This:
ID number 1 has codes 1000, 2000, 3000, 4000
ID number 2 has codes 2000, 4000, 3000
ID number 3 has codes 3000, 4000, 5000
========================================
When all the fields are connected, each ID has found the same codes.
From the example above, I want to produce fair result and adjusted to the code that it had before on each ID as below: (producing fair codes over the set of ID's)
========================================
To be:
ID number 1 has codes 1000, 2000 (1000 must be on number 1 cause only it has than other)
ID number 2 has codes 3000, 4000
ID number 3 has codes 5000 (5000 must be on number 3 cause only it has than other)
========================================
Some say using Round Robin, but I never heard Round Robin before and I don't have idea how to use it, such a blank mind.
Is there another easier way like to use PHP may be? I'm lost.
Thanks.
=============================================================
Explanation:
I'm making an application where each user has a predefined code and does not have the same code. For example user A has a range of codes between 1000-1500, the user B has a range of codes between 1600 to 2000. And user C has a range of codes between 1300-1550. As we see, the distance of a code on the C contained in the codes on the A (A -> 1000-1500, C -> 1300-1550), will certainly get duplicate between the two user.
With this condition, how to separate and divide it to make it more fair. Let C has 1300, A has 1301, C has 1302 et cetera until 1500.
I thought the simple example I gave before could quite understand, but it seemed like a mess, my mistake.
$codes = array(1000, 2000, 3000, 4000, 5000);
// set up the receiving "containers"
$ids = array(
array(),
array(),
array(),
);
$n_ids = count($ids);
$i = 0;
foreach ($codes as $code) {
// use ($i % $n_ids) to distribute over $n_ids containers
$ids[$i % $n_ids][] = $code;
++$i;
}
print_r($ids);
Output:
Array
(
[0] => Array
(
[0] => 1000
[1] => 4000
)
[1] => Array
(
[0] => 2000
[1] => 5000
)
[2] => Array
(
[0] => 3000
)
)
This problem is a simple distribution task: Distribute N items over M containers.
For every i (0 <= i < N) you select a container to put item N[i] in; the selection is done by using this expression: i mod M (i modulo M).
This expression is what you could call the round-robin, because it goes round like this:
i : 0 1 2 3 4
i % M: 0 1 2 0 1
Even faster
The array_chunk function does this task as well, but I figured you would like to understand the problem first. Also, array_chunk produces a somewhat different result.
$ids = array_chunk(array(1000, 2000, 3000, 4000, 5000), round(count($codes) / 3));
Output:
Array
(
[0] => Array
(
[0] => 1000
[1] => 2000
)
[1] => Array
(
[0] => 3000
[1] => 4000
)
[2] => Array
(
[0] => 5000
)
)

Rank array values with potential duplicate values and skipping some positions if there is a tie

I am working with database data that manipulates college students exam results. Basically, I am pulling the records from a MySQL database and pulling one class at any given time. I want to rank the students with the highest performer given the rank of 1.
Here is an illustration;
Marks: 37, 92, 84, 83, 84, 65, 41, 38, 38, 84.
I want to capture MySQL data as a single array. Once I have the data in an array, I should then assign each student a position in the class such as 1/10 (number 1, the 92 score), 4/10 etc. Now the problem is that if there is a tie, then the next score skips a position and if there are 3 scores at one position then the next score skips 2 positions. So the scores above would be ranked as follows;
92 - 1
84 - 2,
84 - 2,
84 - 2,
83 - 5,
65 - 6,
41 - 7,
38 - 8,
38 - 8 ,
37 - 10
The grading system requires that the number of positions (ranks, if you will) will be maintained, so we ended up with 10 positions in this class since positions 3, 4, 5 and 9 did not have any occupants. (The alternative of filling every number will have given us only 8 positions!)
Is it possible (humanly/programmatically possible) to use PHP to rank the scores above in such a way that it can handle possible ties such as 4 scores at one position? Sadly, I could not come up with a function to do this. I need a PHP function (or something in PHP) that will take an array and produce a ranking as above.
If it's possible to do this with MySQL query data without having it in an array, then that will also be helpful!
I assume the grades are already sorted by the database, otherwise use sort($grades);.
Code:
$grades = array(92, 84, 84, 84, 83, 65, 41, 38, 38, 37);
$occurrences = array_count_values($grades);
$grades = array_unique($grades);
foreach($grades as $grade) {
echo str_repeat($grade .' - '.($i+1).'<br>',$occurrences[$grade]);
$i += $occurrences[$grade];
}
Result:
92 - 1
84 - 2
84 - 2
84 - 2
83 - 5
65 - 6
41 - 7
38 - 8
38 - 8
37 - 10
EDIT (Response to discussion below)
Apparently, in case the tie occurs at the lowest score,
the rank of all lowest scores should be equal to the total count of scores.
Code:
$grades = array(92, 84, 84, 84, 83, 65, 41, 38, 37, 37);
$occurrences = array_count_values($grades);
$grades = array_unique($grades);
foreach($grades as $grade) {
if($grade == end($grades))$i += $occurrences[$grade]-1;
echo str_repeat($grade .' - '.($i+1).'<br>',$occurrences[$grade]);
$i += $occurrences[$grade];
}
Result:
92 - 1
84 - 2
84 - 2
84 - 2
83 - 5
65 - 6
41 - 7
38 - 8
37 - 10
37 - 10
$scores = array(92, 84, 84, 84, 83, 65, 41, 38, 38, 37);
$ranks = array(1);
for ($i = 1; $i < count($scores); $i++)
{
if ($scores[$i] != $scores[$i-1])
$ranks[$i] = $i + 1;
else
$ranks[$i] = $ranks[$i-1];
}
print_r($ranks);
I needed to end up with a map of values to rank. This method may be more efficient for the original question too.
public static function getGrades($grades)
{
$occurrences = array_count_values($grades);
krsort($occurrences);
$position = 1;
foreach ($occurrences as $score => $count) {
$occurrences[$score] = $position;
$position += $count;
}
return $occurrences;
}
If you print_r on $occurrences you get
Array
(
[92] => 1
[84] => 2
[83] => 5
[65] => 6
[41] => 7
[38] => 8
[37] => 10
)
Based on the original answer, so thanks!
Using array_count_values() followed by a foreach() is doing 2 loops over the input array, but this task can be done will one loop (minimizing/optimizing the time complexity).
Code: (Demo)
// assumed already rsort()ed.
$scores = [92, 84, 84, 84, 83, 65, 41, 38, 38, 37];
$gappedRank = 0;
$result = [];
foreach ($scores as $score) {
++$gappedRank;
$gappedRanks[$score] ??= $gappedRank;
$result[] = [$score => $gappedRanks[$score]];
}
var_export($result);
For a flat, associative lookup array of scores and their rank, unconditionally increment the counter and only push a new element into the lookup array if the key will be new. (Demo)
$gappedRank = 0;
$lookup = [];
foreach ($scores as $score) {
++$gappedRank;
$lookup[$score] ??= $gappedRank;
}
var_export($lookup);
The first snippet provides "gapped ranking". I have another answer which implements a similar approach but with a different input data structure and with the intent of modifying row data while looping.
Get dense rank and gapped rank for all items in array
In the realm of ranking, there is also "dense ranking". See my time complexity optimized answers at:
Populate multidimensional array's rank column with dense rank number
Add order column to array to indicate rank from oldest to youngest

Categories