I'm trying to do a web application with php and MySQL that can graph the estimate effort of a person based on the data stored in the data base
$luna_start = date('n', strtotime($row_task['data_i']));
$luna_end = date('n', strtotime($row_task['data_s']));
$luna_curenta=$luna_start;
$ultimazi_luna_curenta= cal_days_in_month(CAL_GREGORIAN, $luna_curenta,"2012");
while ($luna_curenta<=$luna_end){
//construieste vectorul
for ($i = 1; $i <= $ultimazi_luna_curenta; $i++) {
$data_curenta= date("Y-m-d", mktime(0, 0, 0, $luna_curenta, $i, 2012));
if (($data_curenta>=$row_task['data_i'])&&($data_curenta<=$row_task['data_s'])){
$efort_a['a'.$row_task['task_id']][$data_curenta]=$efort_mediu;
}
}
$luna_curenta++;
}
all that trouble for getting an array so that i can graph. the array $efort_a looks like this:
Array(
[a19] => Array(
[2012-09-20] => 2.84
[2012-09-21] => 2.84
.......
[2012-10-21] => 2.84
[2012-10-22] => 2.84
)
[a22] => Array
(
[2012-10-01] => 0.1
[2012-10-02] => 0.1
.....
[2012-11-05] => 0.1
[2012-11-06] => 0.1
[2012-11-07] => 0.1
......
[2012-11-25] => 0.1
......
[2012-11-30] => 0.1
)
[a16] => Array
(
[2012-10-08] => 4
[2012-10-09] => 4
[2012-10-10] => 4
[2012-10-11] => 4
......
[2012-10-18] => 4
[2012-10-19] => 4
)
)
further more I'm doing a little more array processing.
extract($efort_a);
$graf_data= array_add($a19,$a22,$a16);
$data = new GoogleChartData($graf_data);
$chart->addData($data);
the problem with all this code is that it isn't a dynamic one. the arrays a19, a22, a16 are named statically and in this particular case the user had only 3 tasks (task id 19, 22 and 16) ... but what if the user has multiple tasks ?.
so the questions is can this code be reorganized, maybe as a function i don't know how, to be a dynamic one ?
could it be possible to do a function that graph's all the users effort (one graph, multiple lines- one for each user)
ps: the function to do the adding of arrays based on keys is:
function array_add($a1, $a2) {
// adds the values at identical keys together
$aRes = $a1;
foreach (array_slice(func_get_args(), 1) as $aRay) {
foreach (array_intersect_key($aRay, $aRes) as $key => $val) $aRes[$key] += $val;
$aRes += $aRay;
}
return $aRes;
}
========================================= day 2 =======================
after some thinking i thought to give another try... so in order to name the keys of array effort_a dynamically i need to know where to stop with the incrementation so :
$sql_nrtask='SELECT *, COUNT(user) AS nr_task
FROM task
WHERE user="ASD"
ORDER BY data_i ASC
';
$query_nrtask= mysql_query($sql_nrtask) or die ('rrr');
while ($row_nrtask= mysql_fetch_array($query_nrtask)) {
$nr_task=$row_nrtask['nr_task']; // no we know how many sub-arrays we'll have
}
for ($j++;$j<=$nr_task;$j++){
while ($luna_curenta<=$luna_end){
//construieste vectorul
for ($i = 1; $i <= $ultimazi_luna_curenta; $i++) {
$data_curenta= date("Y-m-d", mktime(0, 0, 0, $luna_curenta, $i, 2012));
if (($data_curenta>=$row_task['data_i'])&&($data_curenta<=$row_task['data_s'])){
$efort_a['$a'.$j][$data_curenta]=$efort_mediu;
}
}
$luna_curenta++;
}
}
the problem is know that i don't know hot to merge the arrays within $efort_a with 2 conditions:
keeping indexes where they are the same and adding values
adding a new index where it is a new element
Related
I need an advice how to display data from array (result of sql query) in PHP.
Here's the problem:
My query result is 2 row (top figure). Currently, I'm using foreach method to display all data, thus, the foreach will iterate 2 times, right?
Now, I want to ask:
In my web page, how I can display like the bottom figure??
How to join the second row (the theory's score is 0, and the lab's score is 75 and 80) to the first row? So it'll be ONLY 1 row for ID 112.
EDIT:
The SQL query is not my part. I've just received that data (top figure), and my job only to display it to web page.
Well as you used to display rows with foreach you could do it the same way:
* create new array
* loop through your current array
* save to new array by ID and do SUMs
* you can do output from newly created array.
create new array by loop through your current array which will filter out 0 values probably by using array_walk_recursive() or array_map() or array_filter()
then at this stage you got filtered array
you can do output from filtered array
I'm wondering why you have two rows with same 'id'. Usually 'id' should be the primary key...
Anyway, you could simply combine the rows with same 'id', adding the equally named fields casting them like integers.
EDIT 1-2:
Supposing code is in associative arrays:
$rows = array(
0 => array(
'id' => 112,
'theory' => array('score1' => 70, 'score2' => 80),
'lab' => array('score1' => 0, 'score2' => 0)
),
1 => array(
'id' => 112,
'theory' => array('score1' => 0, 'score2' => 0),
'lab' => array('score1' => 75, 'score2' => 80)
),
2 => array(
'id' => 110,
'theory' => array('score1' => 0, 'score2' => 0),
'lab' => array('score1' => 75, 'score2' => 80)
)
);
// New buffer for data
$newArray = array();
// Loop to combine buffer
foreach ($rows as $row) {
if (array_key_exists($row['id'], $newArray))
{
$newArray[$row['id']]['theory']['score1'] += $row['theory']['score1'];
$newArray[$row['id']]['theory']['score2'] += $row['theory']['score2'];
$newArray[$row['id']]['lab']['score1'] += $row['lab']['score1'];
$newArray[$row['id']]['lab']['score2'] += $row['lab']['score2'];
} else {
$newArray[$row['id']]['theory']['score1'] = $row['theory']['score1'];
$newArray[$row['id']]['theory']['score2'] = $row['theory']['score2'];
$newArray[$row['id']]['lab']['score1'] = $row['lab']['score1'];
$newArray[$row['id']]['lab']['score2'] = $row['lab']['score2'];
}
}
// Loop to show data
foreach ($newArray as $key => $value) {
echo $key." ".$value['theory']['score1']." ".$value['theory']['score2']." ".$value['lab']['score1']." ".$value['lab']['score2']."\n";
}
Output:
112 70 80 75 80
110 0 0 75 80
Let's say that you receive your data as Arrays:
$data=array();
$data[0]=[112,70,80,0,0];
$data[1]=[112,0,0,75,80];
$data[2]=[113,71,81,0,0];
$data[3]=[113,0,0,76,81];
If you know in advance that each ID will have exactly two records:
echo '<table>';
echo '<thead>';
echo '<tr>';
echo '<th rowspan="2">ID</th><th colspan="2">theory</th><th colspan="2">lab</th>';
echo '</tr>';
echo '<tr>';
echo '<th>score1</th><th>score2</th><th>score1</th><th>score2</th>';
echo '</tr>';
echo '</thead>';
echo '<tbody>';
for($i=0;$i<count($data);$i++)
{
$d=$data[$i];
echo '<tr>';
echo '<td>'.$d[0].'</td>'; //ID
echo '<td>'.$d[1].'</td><td>'.$d[2].'</td>';
$d=$data[++$i]; //take next element from array
echo '<td>'.$d[3].'</td><td>'.$d[4].'</td>';
echo '</tr>';
}
echo '</tbody>';
echo '</table>';
Code on PHPfiddle.
But generally, your SQL query should be changed in order to represent pivoted data and then you will not have this issue. However, you emphasized that this is out of your power.
Let say I have array like this :
Array (
[2015-03-14] => 3
[2015-05-23] => 10
[2015-06-21] => 7
[2015-05-24] => 3
[2015-06-27] => 10
[2015-06-29] => 7
)
Explanations :
The Key is for DATE and there are several different months and days ( 03 = March, 05 = May, etc )
Questions
How to count total of value based on the Key, for example someone set the date range field ( in frontend page ) and will get the total value from Key 2015-03-14 to 2015-06-27 ?
Or, is there any others method to make it more simple?
If you know your keys will always be dates, you can construct date objects using your keys and use date comparisons.
<?php
$arr = array(
'2015-03-14' => 3,
'2015-05-23' => 10,
'2015-06-21' => 7,
'2015-05-24' => 3,
'2015-06-27' => 10,
'2015-06-29' => 7,
);
$rangeStart = date('2015-03-14');
$rangeEnd = date('2015-06-27');
$sum = 0;
foreach ($arr as $key=>$value) {
$d = date($key);
if ($d >= $rangeStart && $d <= $rangeEnd) {
$sum += $arr[$key];
}
}
echo $sum;
First thing I have to ask, is do you have access to an SQL interface to get this information? Things like this are much better handled by SQL. The resulting code would perform better, be easier to understand and easier to write if you could just select only the entries you need from the database from the start.
Depending on your schema, the code might look like this (assuming PDO which you SHOULD be using):
$stmt = $db->prepare("
SELECT SUM(your_count_field)
FROM your_table
WHERE your_date_field BETWEEN '?' AND '?'
");
$result = $stmt->execute([$date_start, $date_end]);
In the absence of that, I don't think there's an easy way. #mittmemo has a pretty good solution, but I wouldn't use it if you have to select from more than 100 entries or so, or if you will one day need that kind of capacity.
If using PHP 5.6 or more,
$input = [
'2015-03-14' => 3,
'2015-05-23' => 10 ,
'2015-06-21' => 7 ,
'2015-05-24' => 3,
'2015-06-27' => 10 ,
'2015-06-29' => 7
];
$start_date = '2015-05-23';
$end_date = '2015-06-29';
$sum = array_sum(array_filter($input, function($date) use ($start_date, $end_date){
return ( ( $date >= $start_date ) && ( $date <= $end_date ) );
}, ARRAY_FILTER_USE_KEY));
Im trying to make a multidimensional array with two columns. Name and Counter. I can do a single array with all the names. But I dont know how to make it multidimensional and be able to still update the counters. Code i got so far is
if (!in_array($prodname, $da)){
array_push($da, $prodname);
}
and then I can dump it back out with a foreach. How do I make it two dimensional? How can I say alright this exists update the old value? etc.
If you only need name and counter then you should just be able to use a normal array:
$nameCountArray = array();
foreach($names as $name){
if(!array_key_exists($name,$nameCountArray)){
$nameCountArray[$name] = 1;
}else{
$nameCountArray[$name] = $nameCountArray[$name] + 1;
}
}
If you do need multidimensional arrays these are just arrays of arrays and can be accessed as such. A good example of this is using a 2d array to store locations (say on a 3 by 3 grid):
$twoDArray = array(
0 => array(0 => 1,
1 => 4,
2 => 7),
1 => array(0 => 2,
1 => 5,
2 => 8),
2 => array(0 => 3,
1 => 6,
2 => 9)
);
//Grab the item at 1,2
$item = $twoDArray[1][2];//Will give '8'
Supposing you want $da to look like this:
Array(
"name1" => array("score1" => 80, "score2" => 100),
"name2" => array("score1" => 50, "score2" => 60),
"name3" => array("score1" => 90, "score2" => 80),
...
)
Then all you need to do is something like:
function setScore($prodName, $scoreName, $score)
{
global $da;
if (!array_key_exists($prodName, $da)) {
$da[$prodName] = array();
}
$da[$prodName][$scoreName] = $score;
}
setScore("name1", "score1", 80);
setScore("name1", "score2", 100);
setScore("name2", "score1", 50);
...
Unless I'm misunderstanding your question, which is very possible.
I'm using jsTree to view hierarchical data that is stored in a mySQL database as a nested set (left, right, level, etc.). This is working fine, but I need to allow users to import data by uploading a CSV file. When they do so, any existing data in the table will be removed so I don't have to worry about updating the left/right fields.
The data they will be uploading will be in this format:
"Code","Title"
"100","Unit 100"
"200","Unit 200"
"101","Task 101: This is a task"
"102","Task 102: Another task"
"201","Task 201: Yet another"
"300","Unit 300"
"301","Task 301: Another one"
Everything will be a child of a main "Group" that is a level 1 node. All of the "codes" divisible by 100 (ie. 100, 200, 300) will be level 2 (parent nodes.. children of "Group"). All others will be level 3 (child) nodes of their respective parent nodes (ie. 101 and 102 are children of 100, 201 is a child of 200, etc.)
The resulting table in mySQL should look like this:
id parent_id position left right level title
1 0 0 1 18 0 ROOT
2 1 0 2 17 1 Group
3 2 0 3 8 2 Unit 100
4 2 1 9 12 2 Unit 200
5 3 0 4 5 3 Task 101: This is a task
6 3 1 6 7 3 Task 102: Another task
7 4 0 10 11 3 Task 201: Yet another
8 2 2 13 16 2 Unit 300
9 8 0 14 15 3 Task 301: Another one
The tree would then look like this:
My question is: using PHP, what is the best method to accomplish this? I already have code in place that pulls the data contained in the uploaded CSV file and stores it in an array, but I'm not sure what the logic to convert this to a nested set should look like.
Right now, the data is stored in a 2-dimensional array called $data (in the format $data[$col][$row]):
$data[0][0] = "Code";
$data[0][1] = "100";
$data[0][2] = "200";
$data[0][3] = "101";
$data[0][4] = "102";
$data[0][5] = "201";
$data[0][6] = "300";
$data[0][7] = "301";
$data[1][0] = "Title";
$data[1][1] = "Unit 100";
$data[1][2] = "Unit 200";
$data[1][3] = "Task 101: This is a task";
$data[1][4] = "Task 102: Another task";
$data[1][5] = "Task 201: Yet another";
$data[1][6] = "Unit 300";
$data[1][7] = "Task 301: Another one";
Array ( [0] => Array ( [0] => Code [1] => 100 [2] => 200 [3] => 101 [4] => 102 [5] => 201 [6] => 300 [7] => 301 ) [1] => Array ( [0] => Title [1] => Unit 100 [2] => Unit 200 [3] => Task 101: This is a task [4] => Task 102: Another task [5] => Task 201: Yet another [6] => Unit 300 [7] => Task 301: Another one ) )
Any help would be very much appreciated. I now have the parent_id, position, and level being calculated correctly... I just need to figure out the left/right part. Here is the code I'm currently using (thanks for getting me started Matteo):
$rows = array();
// insert ROOT row
$rows[] = array(
'id' => 1,
'parent_id' => 0,
'position' => 0,
'left' => 1,
'right' => 10000, // just a guess, will need updated later
'level' => 0,
'title' => 'ROOT',
);
echo "<br>";
print_r($rows[0]);
// insert group row
$rows[] = array(
'id' => 2,
'parent_id' => 1,
'position' => 0,
'left' => 2,
'right' => 9999, // just a guess, will need updated later
'level' => 1,
'title' => 'Group',
);
echo "<br>";
print_r($rows[1]);
// next ID to be used
$id = 3;
// keep track of code => ID correspondence
$map = array();
// parse data
for ($i = 1, $c = count($data[0]); $i < $c; ++$i) {
// save ID in the map
$map[$data[0][$i]] = $id;
// initialize the current row
$row = array(
'id' => $id,
'parent_id' => 1,
'position' => 0,
'left' => 0,
'right' => 0,
'level' => 1,
'title' => $data[1][$i],
);
// if the code is multiple of 100
if ($data[0][$i] % 100 == 0) {
$row['parent_id'] = 2;
$row['level'] = 2;
$row['position'] = (floor($data[0][$i] / 100)) - 1;
} else {
// get parent id from map
$row['parent_id'] = $map[floor($data[0][$i] / 100) * 100];
$row['level'] = 3;
$row['position'] = $data[0][$i] % 100;
}
// add the row
$rows[] = $row;
++$id;
echo "<br>";
print_r($row);
}
Given your $data array, you could parse it like this:
// this will contain all the rows to be inserted in your DB
$rows = array();
// insert ROOT row
$rows[0] = array(
'id' => 1,
'parent_id' => 0,
'position' => 0,
'level' => 0,
'left' => 1,
'right' => 10000,
'title' => 'ROOT',
);
// insert group row
$rows[1] = array(
'id' => 2,
'parent_id' => 1,
'position' => 0,
'level' => 1,
'left' => 2,
'right' => 9999,
'title' => 'Group',
);
// keep trace of code => ID correspondence
$map = array();
// next ID to be used
$id = 3;
// keep father => sons relationship
$tree = array();
// keep trace of code => row index correspondence
$indexes = array();
// next row index
$index = 2;
// parse your data
for ($i = 1, $c = count($data[0]); $i < $c; ++$i) {
// current code
$code = $data[0][$i];
// save ID in the map
$map[$code] = $id;
// update the indexes map
$indexes[$code] = $index;
// prepare the current row
$row = array(
'id' => $id,
'title' => $data[1][$i],
)
// get the value of code mod 100
$mod = $code % 100;
// if the code is multiple of 100
if ($mod == 0) {
// the parent_id is 2
$row['parent_id'] = 2;
// it is level two
$row['level'] = 2;
// compute position
$row['position'] = floor($code / 100) - 1;
}
else {
// get the parent code
$parent = floor($code / 100) * 100;
// get parent id from map using parent code
$row['parent_id'] = $map[$parent];
// it is level three
$row['level'] = 3;
// save position
$row['position'] = $mod;
// save in relationship tree
$tree[$parent][] = $code;
}
// add the row
$rows[$index] = $row;
// prepare next id
++$id;
// update row index
++$index;
}
// sort the relationship tree base on the parent code (key)
ksort($tree, SORT_NUMERIC);
// next left value
$left = 3;
// now, using the relationship tree, assign left and right
foreach ($tree as $parent => $sons) {
// calculate parent left value
$parentLeft = $left;
// prepare next left value
++$left;
// to be sure that the sons are in order
sort($sons, SORT_NUMERIC);
// assign values to sons
foreach ($sons as $son) {
// index in the rows array
$index = $indexes[$son];
// set left value
$rows[$index]['left'] = $left;
// set right value
$rows[$index]['right'] = $left + 1;
// increment left value
$left += 2;
}
// calculate parent right value
$parentRight = $left;
// prepare next left value
++$left;
// index of parent in the rows array
$index = $indexes[$parent];
// set the values
$rows[$index]['left'] = $parentLeft;
$rows[$index]['right'] = $parentRight;
}
// update group row right value
$rows[1]['right'] = $left;
// prepare next left value
++$left;
// update root row right value
$rows[0]['right'] = $left;
At this point, you can insert all the rows one at a time.
EDIT: now the script should handle all the required values correctly.
I would use Doctrine2 with a Nested Set Extension. You could use a nice and convenient API and don't have to worry about the nested set implementation:
See
http://www.gediminasm.org/article/tree-nestedset-behavior-extension-for-doctrine-2
or http://wildlyinaccurate.com/simple-nested-sets-in-doctrine-2
There are several extensions on github. Actually, i don't know which one is best.
https://github.com/l3pp4rd/DoctrineExtensions
https://github.com/guilhermeblanco/Doctrine2-Hierarchical-Structural-Behavior
https://github.com/blt04/doctrine2-nestedset
List item
If your data is flat, you could parse for keywords like 'Unit' or 'Task' to arrange your elements to the needed hierarchical order.
I have banners advertising with number of views, like CPM system.
And for example :
i have 3 banner:
banner1 with 20.000 nr of views
banner2 with 10.000 nr of views
banner3 with 5.000 nr of views
and on my website the banner must to appear in this position (when the page is reloaded) :
banner1 banner2 banner1 banner2 banner3
if the number of views is higher then the probability of apparition is higher
how can i do this in php?
First of all, your system is just... stupid. It perpetuates banners with lots of views while newly created banners with 0 or few views will never get a chance to be picked and thus will never be actually seen...
That being said, if you have an array that looks like this:
$banners = array
(
'banner1' => 1,
'banner2' => 2,
'banner3' => 4,
'banner4' => 8,
'banner5' => 16,
);
You can use a function like this one to weightily pick one banner:
function Probability($data)
{
if (is_array($data) === true) {
$result = 0;
$probability = mt_rand(1, array_sum($data));
foreach ($data as $key => $value) {
$result += $value;
if ($result >= $probability) {
return $key;
}
}
}
return false;
}
Usage (test it # CodePad.org or # IDEOne):
echo Probability($banners); // banner5
Sample from 100 executions:
Array
(
[banner5] => 41
[banner4] => 38
[banner3] => 10
[banner2] => 8
[banner1] => 3
)
Here's a php way to do it
I'm imagining your array will look something like this...
$banners = array(
array (
'name' => 'banner1',
'views' => 20
),
array (
'name' => 'banner2',
'views' => 10
),
array (
'name' => 'banner3',
'views' => 5
)
);
This function basically loops through the banners and however many views a banner has, that many items of its array index are added to an array. Then a random one is chosen. Items with more views have a better chance of being chosen.
function getWeightedRandom( $array ) {
$universe_array = array();
foreach ( $array as $k => $b ) {
$universe += $b['views'];
$universe_array = array_pad( $universe_array, $universe, $k );
}
$rand = mt_rand( 0, count( $universe_array ) -1 );
return $array[ $universe_array[ $rand ] ];
}
$r = getWeightedRandom($banners);
print_r($r);
A simple mysql option is:
select * from banners order by rand() * views desc limit 1
banners with more views will have a higher chance of being the top result