I'm building a VERY simple threaded message system, primarily for users of my site to communicate bugs and problems to me and each other as I expand my site's functionality. But, I'm having no luck creating a properly nested list of each thread.
I have two tables: Threads and Posts, which I've simplified for presentation here.
The Threads table is substantially like so, ordered by most recently modified thread:
+----+-----------------+---------------------+
| id | thName | thModified |
+----+-----------------+---------------------+
| 5 | Thread Number 5 | 2019-06-29 20:54:59 |
+----+-----------------+---------------------+
| 4 | Thread Number 4 | 2019-06-29 20:45:22 |
+----+-----------------+---------------------+
| 3 | Thread Number 3 | 2019-06-29 20:44:20 |
+----+-----------------+---------------------+
| 2 | Thread Number 2 | 2019-06-29 20:43:00 |
+----+-----------------+---------------------+
| 1 | Thread Number 1 | 2019-06-29 20:39:25 |
+----+-----------------+---------------------+
The Posts table is substantially like so:
+----+------+-----+----------------------------------+
| Id | thID | pID | postMessage |
+----+------+-----+----------------------------------+
| 1 | 1 | 0 | First message of thread number 1 |
+----+------+-----+----------------------------------+
| 2 | 2 | 0 | First message of thread number 2 |
+----+------+-----+----------------------------------+
| 3 | 3 | 0 | First message of thread number 3 |
+----+------+-----+----------------------------------+
| 4 | 4 | 0 | First message of thread number 4 |
+----+------+-----+----------------------------------+
| 5 | 5 | 0 | First message of thread number 5 |
+----+------+-----+----------------------------------+
| 6 | 5 | 5 | First response to post 5 |
+----+------+-----+----------------------------------+
| 7 | 5 | 5 | Second response to post 5 |
+----+------+-----+----------------------------------+
| 8 | 5 | 6 | First response to post 6 |
+----+------+-----+----------------------------------+
| 9 | 1 | 1 | First response to post 1 |
+----+------+-----+----------------------------------+
Where each post is related to a thread on the other table, and parent/child relationships are determined in this table by parsing the parentID column. Posts with "0" as a parent are root nodes.
My basic plan of attack is this:
Get all threads, sorted by most recent
For each thread, get ALL posts by matching thread_ids, sorted by parent_id
For each thread, somehow (recursively?) iterate thru this list of posts and create a PHP ordered list, properly indented showing the relationship between parents and children.
Sadly, it's the last step that has brought me to an utter standstill for the past 3 days. Using "Thread 5" as an example, after step 2 I have an array that looks like this:
[0] => Array
(
[post_id] => 5
[thread_id] => 5
[parent_id] => 0
[user_id] => 9
[post_message] => First message of thread number 5
[post_created] => 2019-06-29 20:54:59
[thread_title] => Thread Number 5
)
[1] => Array
(
[post_id] => 6
[thread_id] => 5
[parent_id] => 5
[user_id] => 9
[post_message] => First response to post 5
[post_created] => 2019-06-29 21:39:00
[thread_title] => Thread Number 5
)
[2] => Array
(
[post_id] => 7
[thread_id] => 5
[parent_id] => 5
[user_id] => 9
[post_message] => Second response to post 5
[post_created] => 2019-06-29 21:52:00
[thread_title] => Thread Number 5
)
[3] => Array
(
[post_id] => 8
[thread_id] => 5
[parent_id] => 6
[user_id] => 0
[post_message] => First response to post 6
[post_created] => 2019-06-29 21:55:00
[thread_title] => Thread Number 5
)
From that array, I'd like to generate a nested list that looks something like:
Thread Number 5 - First message of thread number 5
Thread Number 5 - Second response to post 5
Thread Number 5 - First response to post 5
Thread Number 5 - First response to post 6
Notice that responses are sorted by post date (most recent first), and of course for subsequent threads I'd like the indentation to go back to the zero location again.
Added to clarify intent: in production each post would be a link that
opens to display the full text of the message. Responses would be the
same "thread name" with user and date appended. So, for example, the
tread might read "Found bug during login" and my response (1st child)
would read: "Found bug during login - Chris Conlee 19/07/01 09:10" I
realized the example above seems weird without context.
I honestly don't have any code that's working well enough to post it here. At one point I had a recursive routine which only traversed the left-most leg, and then skipped the second response to post 5.
At another point I had a routine which displayed all the nodes in duplicate and triplicate, and the indentation never worked properly.
I apologize profusely, as it seems like it should be a VERY simple exercise, but I just have worked myself into knots trying to get my head around the recursive nature of it, coupled with multiple threads, etc. If anybody can throw me a lifeline it would be HUGELY appreciated.
Well, I finally slowed down and stepped thru the iteration element by element and figured it out.
Feeding an array of posts, relative to a given thread, such as this:
[0] => Array
(
[post_id] => 5
[thread_id] => 5
[parent_id] => 0
[user_id] => 9
[post_message] => First message of thread number 5
[post_created] => 2019-06-29 20:54:59
[thread_title] => Thread Number 5
)
[1] => Array
(
[post_id] => 6
[thread_id] => 5
[parent_id] => 5
[user_id] => 9
[post_message] => First response to post 5
[post_created] => 2019-06-29 21:39:00
[thread_title] => Thread Number 5
)
[2] => Array
(
[post_id] => 7
[thread_id] => 5
[parent_id] => 5
[user_id] => 9
[post_message] => Second response to post 5
[post_created] => 2019-06-29 21:52:00
[thread_title] => Thread Number 5
)
[3] => Array
(
[post_id] => 8
[thread_id] => 5
[parent_id] => 6
[user_id] => 0
[post_message] => First response to post 6
[post_created] => 2019-06-29 21:55:00
[thread_title] => Thread Number 5
)
Into the following method:
public function buildForum($postsToThread, &$forum, $parent_id = 0) {
foreach ($postsToThread as $post) {
$time = strtotime($post['post_created']);
$tmpCurrentAuthorName = $this->getPostAuthor($post['user_id']);
$tmpCurrentThreadTitle = $post['thread_title'];
$tmpCurrentPostDate = date("M d, Y g:i A", $time);
if ($post['parent_id'] == $parent_id) {
$forum .= "<ol><li><a href='/freetools/forumViewPost/" .$post['post_id'] . "'>" . $tmpCurrentThreadTitle .= " by " . $tmpCurrentAuthorName . "</a> on " . $tmpCurrentPostDate . "</li>";
$parent_id = $post['post_id'];
$this->buildForum($postsToThread, $forum, $parent_id);
$parent_id = $post['parent_id'];
$forum .= "</ol>";
}
}
}
Recursively traverses the tree and returns a result similar to:
(source: post-tools.com)
Related
I'm looking for a formula (PHP) that I can use to assign a score base on rarity of a number inside a collection. Serial#1 being the rarest.
For example, I have few sets of collections.
Collection 1 - Total number of items = 10 (Serial #1 to Serial #10)
Collection 2 - Total number of items = 100 (Serial #1 to Serial #100)
Collection 3 - Total number of items = 3500 (Serial #1 to Serial #3500)
Based on the 3 example sets. Collection 1 is considered the rarest collection because of only 10 items available in the set.
In addition to this. Each item is assigned with its serials, #1 being the best (rarest)
Here is the table of how I visualize the scoring system.
Collection 1
| Serial#| Score |
|:------:| :-----:|
| 1 | 1000 |
| 2 | 900 |
| 3 | 800 |
| 4 | 700 |
| 5 | 600 |
| 6 | 500 |
| 7 | 400 |
| 8 | 300 |
| 9 | 200 |
| 10 | 100 |
Collection 2
| Serial#| Score |
|:------:| :----:|
| 1 | 800 |
| 2 | 700 |
| 3 | 600 |
| 4 | 500 |
| ... | ... |
| 99 | 12 |
| 100 | 10 |
I just made up those scores just for representation.
With the tables above, Serial #1 in Collection 1 has a higher score compared to Serial #1 in Collection 2 because of the rarity of Collection 1.
I have few collections ranging from 1 of a kind (rarest of them all),10, 20, 150, 350, 1000, 5000, 10000, 50000” What “score” is this item supposed to get then?
for the rarest 1 of 1 the score will be based on the score of the
other Serial #1. If for example Collection with 10 items the serial#
get 1000 points then the 1 of 1 i can give 5000 points (this one no
need to calculate)
2. Are all the scores inside a collection supposed to add up to the same value?
No. It doesn't need to add up to the same value as the other collections
Your “made up” scores aren’t really helpful in explaining what kind of scoring logic you want to apply here in the first place
In the example table (made up scores). I just want to show that the different serial number scores. The higher serial number will have a lower score compare to the lower serial#. But the scores will differ from the other collections with the same serial number. Thus I categorized them by collections. Lower item count collections are considered rarer than those with higher item count.
4. But Serial #1 is supposed to have a higher score, than Serial #2, inside the collection? If so, then what would that be based on? Just the ascending order of those #1, #2, etc.?
Maybe it will be based on the ascending order.
5. All 10 items in collection 1 could get the same score?
No.
I don't have any preference on the score the serial number will get. I mean Serial #1 can get X score as long as it will be relative to the rarity of the collection. Serial #1 score will not be the same across collection unless they belong to the same collection rarity.
If I understand you correctly you want to count occurrences of score (value) and sort that occurrence count in a way that the lowest number (serial) will represent the values (score) that are the rarest and the higher the serial number is the value is more common.
For the:
Input Colleciton:
Array
(
[0] => 5
[1] => 4
[2] => 3
[3] => 2
[4] => 1
[5] => 10
[6] => 10
[7] => 11
[8] => 11
[9] => 12
[10] => 12
[11] => 13
[12] => 13
[13] => 14
[14] => 14
[15] => 100
[16] => 100
[17] => 100
[18] => 101
[19] => 101
[20] => 101
[21] => 102
[22] => 102
[23] => 102
[24] => 103
[25] => 103
[26] => 103
[27] => 104
[28] => 104
[29] => 104
)
this code:
<?php
header('Content-Type: text/plain');
// generate colleciton
$collection = [];
# most unique values
$start = 5;
$end = 0;
for ($i = $start; $i > $end; $i--) {
$collection[] = $i;
}
# less unique values (2x the same values)
$start = 10;
$end = 15;
for ($i = $start; $i < $end; $i++) {
$collection[] = $i;
$collection[] = $i;
}
# least unique values
$start = 100;
$end = 105;
for ($i = $start; $i < $end; $i++) {
$collection[] = $i;
$collection[] = $i;
$collection[] = $i;
}
echo "Input Colleciton:\n";
print_r($collection);
# array of [value => how_many_occurences, ... => ...]
$valueCount = array_count_values($collection);
echo "Value count:\n";
print_r($valueCount);
$uniqueBins = [];
# convert to unique bins
foreach ($valueCount as $value => $key) {
$uniqueBins[$key][] = $value;
}
echo "Unique bins:\n";
print_r($uniqueBins);
// optionally sort by value
foreach ($uniqueBins as $key => $bin) {
asort($bin);
$uniqueBins[$key] = $bin;
}
echo "Unique bins after sort:\n";
print_r($uniqueBins);
generates the output:
Unique bins after sort:
Array
(
[1] => Array
(
[4] => 1
[3] => 2
[2] => 3
[1] => 4
[0] => 5
)
[2] => Array
(
[0] => 10
[1] => 11
[2] => 12
[3] => 13
[4] => 14
)
[3] => Array
(
[0] => 100
[1] => 101
[2] => 102
[3] => 103
[4] => 104
)
)
Where values from the array under the key 1 are rarest and values from the array under the key 3 are most common.
The key number is not starting from zero but from 1 because values that are rarest have only single (1) occurrence in the whole collection.
If you want the output bin array to have keys starting from zero and continues in numbering (0,1,2,...) then use array_values(), this way:
$uniqueBins = array_values($uniqueBins);
at the end. By doing that you loose the information about the count (rarity) of values however you still have arrays of values in the order of their rarity (starting from most to the least)
To make this code work you need to have all values into single array $collection but if you have your values in multiple arrays then you can merge all of them into single $collection by using array_merge
Here are headlines to get you started....
Create a DB
Like this
Category :Rarest
uniqueid item name score
hashed#125 Dragon 1000000
hashed#122 Hydra 800000
hashed#100 Medusa 750000
Category :Rarer
uniqueid item name score
hashed other1 50000
hashed other2 30000
hashed other3 10000
Category :Rare
uniqueid item name score
hashed other1 5000
hashed other2 2000
hashed other3 800
This is how you can approach this...
0.hidden readonly checkbox[contains uniquehash] associate with clickable images[showing name of item]
1.[Connect to DB] mysql here
2.[query from tables (all uniqueids 1,2,3 and...)]
3.[from Post get user inputs as array][say user click image showing medusa and hydra - the hidden checkboxes send hashed#100 and hashed#122]
4.[foreach DB values as #strings]( unique ids are broken to individual strings)
5.[foreach user array as #strings]( uniqueids from userinputs are broken to individual strings)
6.[if user string match DB #strings](match each hash]
7.[return query [score] and [name of item] from where #string is found ](only matching ids return their score in this case score 800000 and 750000 as well as Hydra + Medusa)
8.[then [score for match1]] + [score for match2] + match 3 and so on]
(800000+750000)
echo [total score for user selections] + [names of items]
(1.55mil,hydra + medusa ) you can even show they are from the rarest collection
Since the beginning of the process the user in the frontend does not know what the item score is and there is no way to know they just get the total
I have a table named questions in the following form:
Table Name: questions
qn_id | question |
---------------------------------------------------------------
19 | What is your rating on your Team Work?
20 | What is your rating on your Skills?
21 | What is your rating on your Personal Quality?
I have another table named rating as shown below:
Table Name: rating
id | employee_id | question_id | self_score | supervisor_score
-----------------------------------------------------------------
1 | 205 | 19 | 4 | 3
2 | 205 | 20 | 5 | 4
3 | 205 | 21 | 4 | 5
Note: There are two people involved in providing the score. One is supervisee and another is supervisor.
The score provided by the supervisee is kept in the column named self_score. In the table, employee_id is the id of the supervisee. Supervisor also gives the rating to the supervisee for the same question as shown aboove in the table.
I have generated the data from the database as an Array which is shown below:
$params = Array
(
[selfscore_19] => 4
[supervisorscore_19] => 3
[selfscore_20] => 5
[supervisorscore_20] => 4
[selfscore_21] => 4
[supervisorscore_21] => 5
)
Note: In the key named [selfscore_19] , selfscore_ is the string and 19 is the question id.
Questions are same for both - 'self' and 'supervisor'.
So I have appended the strings named 'selfscore' and 'supervisorscore' with the question_id.
In the array shown below, key is: [selfscore_19] which has been appended with the question_id i.e 19 and
the value is score provided by 'self' which is 4.
And other data follows similar pattern.
Attempt:
The code below shows the attempt done by me so far:
Note: I have only shown the main part of the method.
foreach($params as $key => $value){
$exp_key = explode('_', $key);
if($exp_key[0]=='selfscore'){
$arr_result[] = $value;
}
}
foreach($params as $key => $value){
$exp_key = explode('_', $key);
if($exp_key[0]=='supervisorscore'){
$arr_result[] = $value;
}
}
if(isset($arr_result)){
echo '<pre>';
print_r($arr_result);
echo '<pre>';
}
This is the output of the above code:
Current Output:
Array
(
[0] => 4
[1] => 5
[2] => 4
[3] => 3
[4] => 4
[5] => 5
)
Now I want to tranform the Current Output in the form of following Output.
Although I have already researched about this case, but I am finding it a bit tricky.
What modification should I do to achieve the required output?
Suggestions are highly appreciated.
Required Output:
$score['self_score']=array(
array('question_id'=>19,'score'=>4)
array('question_id'=>20,'score'=>5)
array('question_id'=>21,'score'=>4)
);
$score['supervisor_score']=array(
array('question_id'=>19,'score'=>3),
array('question_id'=>20,'score'=>4)
array('question_id'=>21,'score'=>5)
);
Do it with a single loop. Use the first part of $exp_key as the key of the resulting array, and the second part as the value of the question_id key of the sub-array.
foreach ($arr_result as $key => $value) {
$exp_key = explode("_", $key);
$score[$exp_key[0]][] = array("question_id" => $exp_key[1], "score" => $value);
}
I think I'll recommend that you use a UNION to join two simple queries so that your initial resultset is very close to what you want (close enough to simply iterate and/or modify it).
SQL: (Demo)
(SELECT 'group' AS 'self_score', question_id, self_score
FROM rating
WHERE employee_id = 205
ORDER BY id)
UNION
(SELECT 'supervisor_score', question_id, supervisor_score
FROM rating
WHERE employee_id = 205
ORDER BY id)
Resultset:
group | question_id | self_score
------------------|---------------|------------
self_score | 19 | 4
self_score | 20 | 4
self_score | 21 | 5
supervisor_score | 19 | 3
supervisor_score | 20 | 4
supervisor_score | 21 | 5
If you are open to a PDO solution, fetchAll(PDO::FETCH_GROUP) will come in pretty handy.
I have a table called salary_raise which looks like:
id | employee_id | salary | year | month | date
1 | 1 | 1000 | 2014 | 2 | 2014-01-30
2 | 1 | 1200 | 2015 | 3 | 2015-02-20
3 | 1 | 1300 | 2015 | 4 | 2015-03-29
... and so on for multiple employees. It keeps records only when a raise of salary occurs (and this can occur random over a period of time).
Right now I can extract an array which looks like $salary['year']['month'] = $salary_value:
[2014]
[2] => 1000
[2015]
[3] => 1200
[4] => 1300
I need to extract a full report with the salary in each month up to date (filling automatically the months/years missing), like:
[2014]
[2] => 1000
[3] => 1000
[4] => 1000
[5] => 1000
[6] => 1000
[7] => 1000
[8] => 1000
[9] => 1000
[10] => 1000
[11] => 1000
[12] => 1000
[2015]
[1] => 1000
[2] => 1000
[3] => 1200
[4] => 1300
[5] => 1300
I can't figure it out how to do it by using php and/or mysql.
Many thanks!
In order to 'fill the gaps' you need to source the information somewhere.
Easiest way to do this in mysql is to have some generic calendar tables, which list single values for things like month numbers, years etc.
So given a table months(month), and a table years(year), the former contains 1..12 and the latter contanis 2010..2017 (for example), we can cross join the two of those to give us every month for every year, and then left join the salary_raise table to this.
That will leave us a table with a few entries and a lot of nulls, so then we need to use some variables to populate the null values with the last salary entry.
select `year`, `month`, if(salary is null, #prev_sal, #prev_sal := salary) salary
from (
select `years`.`year`, `months`.`month`, salary
from `years` cross join `months`
left join salary_raise sr
on sr.month = `months`.`month`
and sr.year = `years`.`year`
and sr.employee_id = 1
where (`years`.`year` = 2014 or `years`.`year` = 2015)
order by `years`.`year` asc, `months`.`month` asc
) q cross join (select #salary := null) qq
demo here
Of course you'll need to limit that to not check dates in the future -- that should be pretty easy tho.
I need a solution how to find all possible combinations of a schedule. There are courses on given dates that can vary by length. There is a given number of weeks. I need to find a solution to all possible combinations including all courses, with no overlapping.
$courses = array ("A", "B", "C", "D");
$duration = array (2, 3, 1, 2); // duration of $courses (in weeks)
$start_dates = array (
$courses[0] => array (1, 3, 5),
$courses[1] => array (1, 2, 5, 6),
$courses[2] => array (3, 4, 5, 9),
$courses[3] => array (1, 4, 8));
I would like to get all the possible combinations, e.g. this:
Week | Course
1 | A
2 | A
3 | C
4 | D
5 | D
6 | B
7 | B
8 | B
9 | -
10 | -
Second combination would be e.g. this:
Week | Course
1 | A
2 | A
3 | -
4 | C
5 | B
6 | B
7 | B
8 | D
9 | D
10 | -
The preferred output would be an array with week => course, like this:
$combinations = array (
0 => array (
1 => $courses[0],
2 => $courses[0],
3 => $courses[2],
4 => $courses[3],
...
),
1 => array (
1 => $courses[0],
2 => $courses[0],
4 => $courses[2],
5 => $courses[1],
6 => $courses[1],
7 => $courses[1],
...
)
);
I am really stuck with this problem. Thanks a lot for your help.
I have a binary, the database table of relationships looks like this:
+----+----------+---------+-----+
| id | parentID | childID | pos |
+----+----------+---------+-----+
| 1 | 1 | 2 | l |
| 2 | 1 | 3 | r |
| 3 | 2 | 4 | l |
| 4 | 3 | 5 | r |
| 5 | 4 | 6 | l |
| 6 | 5 | 7 | r |
+----+----------+---------+-----+
I am able to extract or children of for example 1 - but I have very clumsy function for that, so I need something that works better.
The output I need should look like this:
Array
(
[0] => Array
(
[id] => 2
[parentID] => 1
[pos] => l
)
[1] => Array
(
[id] => 4
[parentID] => 2
[pos] => l
)
[2] => Array
(
[id] => 6
[parentID] => 4
[pos] => l
)
[3] => Array
(
[id] => 3
[parentID] => 1
[pos] => r
)
[4] => Array
(
[id] => 5
[parentID] => 3
[pos] => r
)
[5] => Array
(
[id] => 7
[parentID] => 5
[pos] => r
)
)
So far I came up with this function, however it returns nested array, I want it flattened ... but whenever I tried it it just fails.
function children($pid) {
//set sql
$sql = "SELECT * FROM relationships WHERE parentID = ".$pid;
//save query to result
$result = mysql_query ($sql)
or die("Bad request " . mysql_error());
while ($item = mysql_fetch_array($result)):
$topchild["id"] = $item["childID"];
$topchild["parentID"]= $item["parentID"];
$topchild["pos"] = $item["pos"];
$children[] = $topchild;
$children[] = children($item["childID"]);
endwhile;
return $children;
}
What do I do wrong there?
I want it flattened
$children[] = children($item["childID"]);
instead add each of the items in the return value separately:
foreach (children($item['childID'] as $child)
$children[]= $child;
(Also shouldn't $topchild be initialised inside the loop?)
If you are doing a lot of recursive queries like this, a parent-child relation table is not a good choice of data structure. Consider one of the hierarchically-oriented solutions such as nested sets.