undefined offset and syntax error but code is executed properly - php

ajax:
...
success: function(data){
console.log(data);
}
position.php
Two arrays are transfered ($ids and $indexes), with the equal number of elements.
extract($_POST);
print_r($ids);
print_r($indexes);
for ($i = 0; $i <= count($ids); $i++) {
$stmt = $db->query("UPDATE " . $table . " SET inde = " . $indexes[$i] . " WHERE id = " . $ids[$i]); // this is line 10
}
The code is executed properly, i.e. all table data are updated as expected, but console (after listing the arrays) shows some errors:
Array
(
[0] => 25
[1] => 23
[2] => 18
[3] => 26
[4] => 21
)
Array
(
[0] => 0
[1] => 1
[2] => 2
[3] => 3
[4] => 4
)
<b>Notice</b>: Undefined offset: 5 in <b>D:\matria\s02\admin\position.php</b> on line <b>10</b><br />
<b>Fatal error</b>: Uncaught PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064... near 'WHERE id =' at line 1 in D:\matria\s02\admin\position.php:10
Any help?

The problem is with your for cycle:
for ($i = 0; $i <= count($ids); $i++) {
count($ids) is 5 and when $i reaches 5, 5 <= 5 is still true. However, indexing starts from 0, therefore your possible indexes are between 0 and 4. Change your for to:
for ($i = 0; $i < count($ids); $i++) {
and then it will not try to do things when $i reaches 5.
EDIT:
The original answer dealt with your effective error, but there are still things to be refactored/improved after the fix:
you have an array called $indexes which on its own indexes stores the index as value as well. If there is no counter-example unmentioned in the question, then this whole array is redundant and you can remove $indexes and you can use $i instead of $indexes[$i]
your $i < count($ids) in the for cycle will calculate the number of $ids on each iteration. It is much more elegant to calculate it only once before the for with something like $myCount = count($ids); and then use $i < $myCount in your for, to calculate things only once
Suggested code:
extract($_POST);
print_r($ids);
$myCount = count($ids);
for ($i = 0; $i < $myCount; $i++) {
$stmt = $db->query("UPDATE " . $table . " SET inde = " . $i . " WHERE id = " . $ids[$i]); // this is line 10
}

Related

PHP with mysql SELECT LIMIT

I have a request (mysql) in PHP who gives me many results and it works like a charm. Here is an example of a request who gives me 115 results :
public static function getInfo($start, $limit) {
$sql = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS(
SELECT blablabla FROM blabla ORDER BY bla LIMIT $start,$limit
);
return $sql;
}
In my php file, i've got :
$rq = getInfo (0, 0);
$count_rq = (int)count($rq);
for ($i = 0; $i < $count_rq; ++$i) {
.... do my things ....
}
Here is the problem : in this configuration, it shows only results 1 to 42...
When i do some modifications/tests, it shows strange results :
Modification => for ($i = 42; $i < $count_rq; ++$i) {.......}
I got 0 result.
Modification => for ($i = 43; $i < $count_rq; ++$i) {.......}
I got results 43 from 115 !..
Modification => for ($i = 60; $i < $count_rq; ++$i) {.......}
I got results 60 from 115 !..
I've tried to modify the "LIMIT" but i don't understand. Here is the result when i do modifications/test :
(i'm using for ($i = 0; $i < $count_rq; ++$i) {.......})
Modification => $rq = getInfo (0, 20);
I got results 1 to 20.
Modification => $rq = getInfo (0, 50);
I got results 1 to 42.
Modification => $rq = getInfo (10, 0);
I got results 1 to 42.
Modification => $rq = getInfo (10, 30);
I got results 11 to 40.
I don't know how I can show all of my results? I want to have a code who can show 115 results when request got 115 results, or 10 results when request got 10 results. How can I do that? Why am I limited to first 42 results?
You use limit in your request and check that MySQL SELECT is retrieving good results.
Then, why do you start for loop in any other thing than 0??
If your request is getting 115 rows you should use this sentence to iterate through all of them:
for ($i = 0; $i < $count_rq; ++$i)
Good luck.

Randomly pick array values, add them up, until you get a certain value in php

I have the following array with undefined number of elements
$marks=array('2','4','9','3');
target=50;
I want to randomly loop through the array, add up the values I fetch until the total is my target.
$total=0; /////initialize total
for($i=0;$i<=sizeof($marks);++$i)
{
/////////Pick up random values add them up until $total==$target
/////////return the new array with selected elements that sums up to
/////////target
}
I hope my question is clear, also note that the loop should not iterate too many times since the elements might never add up to the total. I have tried adding the items in line but to no avail. Thanks in advance
I think this'll work for you and always return you value of count to be 50 only
$marks = array(6,7,9,6,7,9,3,4,12,23,4,6,4,5,7,8,4);
$target = 50;
function sum($marks, $target) {
$count = 0;
$result = [];
for ($i = 0; $i <= $target; $i++) {
if ($count < $target) {
$add = $marks[array_rand($marks)];
$count = $count + $add;
$result['add'][] = $add;
} elseif ($count == $target) {
break;
} elseif ($count >= $target) {
$extra = $count - $target;
$count = $count-$extra;
$result['extra'] = $extra;
}
}
return $result;
}
print_r(sum($marks, $target));
The way you describe your logic, a while loop might make more sense:
<?php
$marks = array(2, 4, 9, 3);
$target = 50;
$sum = 0;
$i = 0; // to keep track of which iteration we're on
// PHP can natively randomize an array:
shuffle($marks);
while ($sum < $target && $i < count($marks)) {
$sum += $marks[$i];
$i++; // keep track of which iteration we're on
}
// after the loop, we've either added every number in $marks,
// or $sum >= $target
Don't forget that it might exceed $target without ever being equal to it, as Dagon pointed out in a comment.
Look into PHP's native array shuffle: https://secure.php.net/manual/en/function.shuffle.php
This may be a good alternative for the above answer.
Why I say so is that I have set it in such a way that it doesn't let the total go over the target, and when there is such a situation, the current number in the array is decremented by one and added as a new element so that if there is no possible number in the stack, there will be one eventually making this loop not go on infinitely. :)
<?php
$marks = ['2', '4', '9', '3'];
$target = 50;
$total = 0;
$numbersUsed = [];
while($total != $target) {
$index = rand(0, count($marks) - 1);
$number = $marks[$index];
if($number + $total > $target) {
$number = 0;
$marks[] = $marks[$index] - 1;
} else {
$numbersUsed[] = $number;
}
$total += $number;
echo $total . "\n";
}
// To see which numbers were used:
print_r($numbersUsed);
?>
Testing:
Starting with the array ['2', '4', '9', '3'],
We loop and get the result:
4 13 17 20 22 31 35 44 46 48 48 48 48 50
And we get this array which includes the numbers used to get the final result:
Array
(
[0] => 4
[1] => 9
[2] => 4
[3] => 3
[4] => 2
[5] => 9
[6] => 4
[7] => 9
[8] => 2
[9] => 2
[10] => 2
)

Assigning Positions to Students, PHP

I am still a novice at PHP scripting.
I have an Array
$students = array(1201=>94,1203=>94,1200=>91, 1205=>89, 1209=>83, 1206=>65, 1202=>41, 1207=>38,1208=>37, 1204=>37,1210=>94);
From the associative array, the key are the student's exam no and the values are the student's scores. Then I used the 2 inbult PHP functions array_keys and array_values to separate the exam nos from the scores.
$exam_nos=(array_keys($students));
$marks=(array_values($students));
Then I passed the $marks array through the code below:
$i=0;
$occurrences = array_count_values($marks);
$marks = array_unique($marks);
echo '<table border="1">';
foreach($marks as $grade) {
if($grade == end($marks))$i += $occurrences[$grade]-1;
echo str_repeat('<tr><td>'.$grade.': '.($i+1).'</td></tr>',$occurrences[$grade]);
$i += $occurrences[$grade];
}
echo '</table><br />';
output:
94: 1
94: 1
94: 1
91: 4
89: 5
83: 6
65: 7
41: 8
38: 9
37: 11
37: 11
And this is closer to what I want; to rank the scores such that if a tie is encountered, 1 or more positions are skipped, occurs at the end the position the items at the end are assigned a position equivalent toi the total number of ranked items. However, it would be much helpful if this could be done without separating the Array into 2 ...
Questions:
(1) I am pulling my hair how, from the $student array I could have something like:
Exam No Score Position
1201 94 1
1210 94 1
1203 94 1
1200 91 4
1205 89 5
1209 83 6
1206 65 7
1202 41 8
1207 38 9
1204 37 11
1208 37 11
(2) I would like to be able to pick any student by exam no and be able to echo or print out her position e.g
the student 1207 is number 9.
I think I need to capture the postions in a variable, but how do I capture them? Well I don't know!
Could the experts help me here with a better way to achieve my 2 goals (please see questions 1 and 2)? I will try any suggestion that will help me disolve the 'metal blockage' I have hit.
If you're pulling out the students from a database (mentioned in the comments), you could retrieve them with the desired format directly using SQL.
However, I'm going to assume that that's not an option. You could do as follows:
$students = array(1201=>94,1203=>94,1200=>91, 1205=>89, 1209=>83, 1206=>65, 1202=>41, 1207=>38,1208=>37, 1204=>37,1210=>94);
arsort($students);// It orders high to low by value. You could avoid this with a simple ORDER BY clause in SQL.
$result = array();
$pos = $real_pos = 0;
$prev_score = -1;
foreach ($students as $exam_n => $score) {
$real_pos += 1;// Natural position.
$pos = ($prev_score != $score) ? $real_pos : $pos;// If I have same score, I have same position in ranking, otherwise, natural position.
$result[$exam_n] = array(
"score" => $score,
"position" => $pos,
"exam_no" => $exam_n
);
$prev_score = $score;// update last score.
}
$desired = 1207;
print_r($result);
echo "Student " . $result[$desired]["exam_no"] . ", position: " . $result[$desired]["position"] . " and score: ". $result[$desired]["score"];
Hope it helps you.
I would use a custom object to process the students individually and store them in an array.
$students = array(1201=>94,1203=>94,1200=>91, 1205=>89, 1209=>83, 1206=>65, 1202=>41, 1207=>38,1208=>37, 1204=>37,1210=>94);
arsort($students); // Sort the array so the higher scores are on top.
$newStudents = array();
$pos = 0;
$count = 0;
$holder = -1; // Assuming no negative scores.
foreach($students as $k=>$v){
$count++; // increment real counter
if($v < $holder || $holder == -1){
$holder = $v;
$pos = $count;
}
$newStudents[] = makeStudent($pos, $v, $k);
// If you want the exam # as the array key.
// $newStudents[$k] = $student;
}
$newStudents = fixLast($newStudents);
// outputs
print_r($newStudents);
foreach($newStudents as $v){
echo "position : " . $v->position . "<br>";
echo "score : " . $v->score . "<br>";
echo "exam : " . $v->exam . "<br>";
}
function makeStudent($pos, $score,$examNo){
$student = new stdClass(); // You could make a custom, but keeping it simple
$student->position = $pos;
$student->score = $score;
$student->exam = $examNo;
return $student;
}
function fixLast($students){
$length = count($students) -1;
$count = 0;
$i = $length;
while($students[$i]->position == $students[--$i]->position){
$count++;
}
for($i = 0; $i <= $count; $i++){
$students[$length - $i]->position = $students[$length - $i]->position + $count;
}
return $students;
}

How can I simplify a percent (ex. 25%) to a simple statement (ex. 1 out of 4)?

I need to convert an array of numbers and totals into a simple statement.
For example, how can I convert the following, programmatically via PHP, to simple statements like, 1 out of 10, 1 out of 100, and even rounding some (like 2 out of 100 for 9000,400000).
Generate Sample Array:
$arr = array();
for ($i=0; $i < 100; $i++) {
$n = mt_rand(1,1000);
$t = mt_rand(10,100000);
if ($n > $t) continue; // skip!
$arr[] = array($n,$t);
}
/*
// Generates pairs like:
// Array
// (
// [0] => Array ( [0] => 55 [1] => 8774 )
// [1] => Array ( [0] => 814 [1] => 11174 )
// [2] => Array ( [0] => 255 [1] => 32168 )
// ...
// [99] => Array ( [0] => 851 [1] => 24231 )
// )
*/
Run through a function and print simplified results:
foreach ($arr as $a) {
echo $a[0] . '/' . $a[1] . ' ==> ' . simplifyRatio($a[0],$a[1]) . "\r\n";
}
Can you point me in the right direction on how to accomplish this?
Here's the start of a function I'm working on, but the solution is escaping me.
function simplifyRatio($n,$t) {
$p = $n/$t;
if ($p > 0.09) return round($n) . ' out of ' . round($t);
if ($p > 0.009) return round($n) . ' out of ' . round($t);
}
Ideally the denominator should be: 1,2,3...10,20,30...100,200,300...1000,2000,3000...10000,20000,30000...100000 (max)
Assuming always a percentage. You may also want to sprintf $outof before displaying it
function convertPercent($iPercent)
{
// Assume validation on $iPercent
$outof = round(100 / $iPercent);
return "1 out of $outof";
}
For simplicity's sake, I'll assume that you can get your percent in a fraction (ie 25% is 25/100, 0.7% = 7/1000, etc).
You can use Euclid's algorithm to find the GCD of the numerator and the denominator:
http://en.wikipedia.org/wiki/Euclidean_algorithm
In php it'd look something like this:
function gcd ($int1, $int2) {
$tmp = 0;
while ($int1 > 0) {
$tmp = $int1;
$int1 = $int2 % $int1;
$int2 = $tmp;
}
return $int2;
}
This will work as long as $int1 and $int2 are integers greater than 0 (you might want to put in some logic to ensure this). If you need negative numbers, just take the absolute value.
Knowing the GCD, it's easy to figure out the rest:
function reduce($numerator, $denominator) {
$gcd = gcd($numerator, $denominator);
echo ($numerator/$gcd) . " out of " . ($denominator/$gcd);
}
echo reduce(4, 8).'<br>'; // 1 out of 2
echo reduce(38, 897).'<br>'; // 38 out of 897
echo reduce(39, 26).'<br>'; // 3 out of 2
Hope this helps!
I ended up settling for a near match on the pattern 1 out of ___, like so:
function simplifyRatio($n,$t) {
$r = $t/$n;
return '1 out of ' . round($r);
}
// Examples:
$arr = array();
for ($i=0; $i < 100; $i++) {
$n = mt_rand(1,1000);
$t = mt_rand(10,100000);
if ($n > $t) continue; // skip!
$arr[] = array($n,$t);
}
foreach ($arr as $a) {
echo $a[0] . '/' . $a[1] . ' ==> ' . simplifyRatio($a[0],$a[1]) . "\r\n";
}
Example Result:
1000/24819 ==> 1 out of 25
309/50305 ==> 1 out of 163
488/99123 ==> 1 out of 203
322/47610 ==> 1 out of 148
183/54287 ==> 1 out of 297
752/67646 ==> 1 out of 90
240/68854 ==> 1 out of 287
301/81345 ==> 1 out of 270
611/16404 ==> 1 out of 27
522/62992 ==> 1 out of 121
CodePad: http://codepad.org/wu6iOdDq
Initially I had hoped to end up with rounded denominators (10,20...100,200...1000,2000, etc.), but I'm uncertain how to do this well. I'll happily award an answer that cleans up the denominators of the above.

How do I distribute values of an array in three columns?

I need this output..
1 3 5
2 4 6
I want to use array function like array(1,2,3,4,5,6). If I edit this array like array(1,2,3), it means the output need to show like
1 2 3
The concept is maximum 3 column only. If we give array(1,2,3,4,5), it means the output should be
1 3 5
2 4
Suppose we will give array(1,2,3,4,5,6,7,8,9), then it means output is
1 4 7
2 5 8
3 6 9
that is, maximum 3 column only. Depends upon the the given input, the rows will be created with 3 columns.
Is this possible with PHP? I am doing small Research & Development in array functions. I think this is possible. Will you help me?
For more info:
* input: array(1,2,3,4,5,6,7,8,9,10,11,12,13,14)
* output:
1 6 11
2 7 12
3 8 13
4 9 14
5 10
You can do a loop that will automatically insert a new line on each three elements:
$values = array(1,1,1,1,1);
foreach($values as $i => $value) {
printf('%-4d', $value);
if($i % 3 === 2) echo "\n";
}
EDIT: Since you added more information, here's what you want:
$values = array(1,2,3,4,5);
for($line = 0; $line < 2; $line++) {
if($line !== 0) echo "\n";
for($i = $line; $i < count($values); $i+=2) {
printf('%-4d', $values[$i]);
}
}
And if you want to bundle all that in a function:
function print_values_table($array, $lines = 3, $format = "%-4d") {
$values = array_values($array);
$count = count($values);
for($line = 0; $line < $lines; $line++) {
if($line !== 0) echo "\n";
for($i = $line; $i < $count; $i += $lines) {
printf($format, $values[$i]);
}
}
}
EDIT 2: Here is a modified version which will limit the numbers of columns to 3.
function print_values_table($array, $maxCols = 3, $format = "%-4d") {
$values = array_values($array);
$count = count($values);
$lines = ceil($count / $maxCols);
for($line = 0; $line < $lines; $line++) {
if($line !== 0) echo "\n";
for($i = $line; $i < $count; $i += $lines) {
printf($format, $values[$i]);
}
}
}
So, the following:
$values = range(1,25);
print_array_table($values);
Will output this:
1 10 19
2 11 20
3 12 21
4 13 22
5 14 23
6 15 24
7 16 25
8 17
9 18
One solution is to cut the array into chunks, representing the columns, and then print the values in row order:
$cols = array_chunk($arr, ceil(count($arr)/3));
for ($i=0, $n=count($cols[0]); $i<$n; $i++) {
echo $cols[0][$i];
if (isset($cols[1][$i])) echo $cols[1][$i];
if (isset($cols[2][$i])) echo $cols[2][$i];
}
If you don’t want to split your array, you can also do it directly:
for ($c=0, $n=count($arr), $m=ceil($n/3); $c<$m; $c++) {
echo $arr[$c];
for ($r=$m; $r<$n; $r+=$m) {
echo $arr[$c+$r];
}
}
$a = array(1,2,3,4,5);
"{$a[0]} {$a[1]} {$a[2]}\n{$a[3]} {$a[4]}";
or
$a = array(1,2,3,4,5);
"{$a[0]} {$a[1]} {$a[2]}".PHP_EOL."{$a[3]} {$a[4]}";
or
$a = array(1,2,3,4,5);
$second_row_start = 3; // change to vary length of rows
foreach( $a as $index => $value) {
if($index == $second_row_start) echo PHP_EOL;
echo "$value ";
}
or, perhaps if you want a longer array split into columns of 3?
$a = array(1,2,3,4,5,6,7,8,9,10,11,12,13);
$row_length = 3; // change to vary length of rows
foreach( $a as $index => $value) {
if($index%$row_length == 0) echo PHP_EOL;
echo "$value ";
}
which gives
1 2 3
4 5 6
7 8 9
10 11 12
13
one solution is :
your array has N elements, and you want 3 columns, so you can get the value of each cell with $myarray[ column_index + (N/3) + line_index ] (with one or two loops for columns and lines, at least for lines)
I hope this will help you
Bye
Here's something I whipped up. I'm pretty sure this could be more easily accomplished if you were using HTML lists, I've assumed you can't use them.
$arr = array(1,2,3,4,5,6,7,8,9,10,11,12,13,14, 15, 16);
$max = count($arr);
$cols = 3;
$block = ceil($max / $cols);
for ($i = 0; $i < $block ; $i++) {
echo $arr[$i] . ' ';
for ($j = 1; $j < $cols; $j++) {
$nexKey = $i + $block * $j;
if (!isset($arr[$nexKey])) break;
echo $arr[$nexKey] . ' ';
}
echo '<br>';
}
NOTE : You can easily refactor the code inside the loop that uses $nexkey variable by making it into a loop itself so that it works for any number of columns. I've hardcoded it.
Uses loops now.

Categories