First of all thanks to stackoverflow to provide this plattform and you, to give a newbie a help ;-)
Now ..
I have 2 tables: pages and sections
Each has an own id.
A page has one or more sections.
A section belongs to exactly 1 page.
The list sequence of both id's is handled in a different field.
I read both tables and create an (unsorted) array.
What I finally need is a sorted list as shown below, having the page_id's and section_id's in the correct sequence..
Here an example of my tarray after retrieve data:
myArr[] = array( page_id=>2, section_id=>2, parent_id=>0, level=>0, page_seq=>1, section_seq=>2, page_title=>p1 );
myArr[] = array( page_id=>2, section_id=>9, parent_id=>0, level=>0, page_seq=>1, section_seq=>1, page_title=>p1 );
myArr[] = array( page_id=>3, section_id=>3, parent_id=>0, level=>0, page_seq=>2, section_seq=>1, page_title=>p2 );
myArr[] = array( page_id=>4, section_id=>4, parent_id=>0, level=>0, page_seq=>3, section_seq=>1, page_title=>p3 );
myArr[] = array( page_id=>5, section_id=>5, parent_id=>3, level=>1, page_seq=>3, section_seq=>1, page_title=>p2-3 );
myArr[] = array( page_id=>6, section_id=>6, parent_id=>3, level=>1, page_seq=>2, section_seq=>1, page_title=>p2-2 );
myArr[] = array( page_id=>7, section_id=>7, parent_id=>4, level=>1, page_seq=>1, section_seq=>1, page_title=>p3-1 );
myArr[] = array( page_id=>8, section_id=>8, parent_id=>7, level=>2, page_seq=>1, section_seq=>1, page_title=>p3-1-1 );
myArr[] = array( page_id=>9, section_id=>10, parent_id=>5, level=>2, page_seq=>1, section_seq=>1, page_title=>p2-1-1 );
myArr[] = array( page_id=>9, section_id=>11, parent_id=>5, level=>2, page_seq=>1, section_seq=>2, page_title=>p2-1-1 );
myArr[] = array( page_id=>10, section_id=>12, parent_id=>3, level=>1, page_seq=>1, section_seq=>1, page_title=>p2-1 );
My problem is the sorting.
The section_seq is a sequence of the section within the page.
The page_seq is a sequence of the page within the level for the same parent.
I find some rekursive loop examples already here, but - to be honest - I'm not able to adapt them to my needs. And, do I need a rekursive loop ?
What should be the key of my array: the section_id, because it is unique over all pages ?
How to do the correct sort ?
Just to be noted: the page title could unfortunately not to be used - as in the example above - for sorting purposes ;-) as it is free text...
So what I need is:
read first page (1) with level 0 and page_seq = 1
read first page (2) having level 1 -if exists- with page (1) as parent and page_seq = 1
read first page (3) having level 2 -if exists- with page (2) as parent and page_seq = 1
... continue as long as no more deeper level exists
read second page (4) having level 2 -if exists- with page (2) as parent and page_seq = 1
... continue as long as no more deeper level exists and also no more pages on this level having page (2) as parent
read second page (5) having level 1 -if exists- with page (1) as parent and page_seq = 1
... continue as long as no more deeper level exists and also no more pages on this level having page (5) as parent
read second page (6) with level 0 and page_seq = 2
and so on.
Any powerfull help & ideas ?
Thanks in advance
Wolfgang
My idea would be to use the database to do the sorting. With MySQL you could use a left join on both tables to get the sections and pages data in one results table. In the query you could use the ORDER BY keyword to specify the column(s) according to which the results must be sorted.
So finally I found a solution
1) create a list of all pages to be process. The $key generated looks on first level like "1", on 2nd level like "1.1", on 3rd level like "1.1.1" etc.
read all pages
while ( $fetch_obj_pages = $obj_pages->fetchRow() ):
// do some validations and keep only those in mind which should be processed
....
// get and save key for sorting
if ( $isValid == TRUE ):
$key = '';
if ( array_key_exists ( $fetch_obj_pages[ 'parent_id' ], $pageSortList ) == TRUE ):
$key = $pageSortList[ $fetch_obj_pages[ 'parent_id' ]] . '.';
endif;
$key .= $fetch_obj_pages[ 'page_seq' ];
$pageSortList[ $fetch_obj_pages[ 'page_id' ]] = $key;
endif;
endwhile;
2) create a list of all sections to process. It is taken the key generated in pages list above and enhance it with section sorting sequence, then save it per section_id as key.
read all sections
while ( $fetch_obj_sections = $obj_sections->fetchRow() ):
// do some validations and keep only those in mind which should be processed
....
// get and save key for sorting
if ( $isValid == TRUE ):
// get key from page and save key for section
$key = '';
if ( array_key_exists ( $fetch_obj_sections[ 'page_id' ], $pageSortList ) == TRUE ):
$key = $pageSortList[ $fetch_obj_sections[ 'page_id' ]] . '-';
endif;
$key .= $fetch_obj_sections[ section_seq ];
$sectionSortList[ $fetch_obj_sections[ 'section_id' ]] = $key;
endif;
endwhile;
3) sort the section list
natsort ( $sectionSortList );
4) building the result by looping over $sectionSortList and getting the neccessary data due to section_id used as key in this list.
Before someone ask: Of course, all possible filters are done in the SQL. But some are not possible and due to need to be done in a loop-
Most probably, step 1) and 2) could be done in one step using a JOIN and a proper ORDER BY.
Last-Bit-Saving-Junkies will find lot of places for code optimizations and reduction.
However, I like the alternate functionality with : and endif; ;-)
In case someone sees a possibility to make things really faster, feel welcome to provide your ideas.
Related
I have an app where users can customize the product they are going to purchase by choosing options from a menu. This menu has many sections, and each section may have a list of checkboxes for multichoice, or radiobuttons when only one option can be selected. The user must select at least one option in each section. The menu structure is something like this:
$sections = array();
$sections[1] = array(
'multichoice' => true,
'options' => array('A','B','C')
);
$sections[2] = array(
'multichoice' => false,
'options' => array('A','B','C','D')
);
$sections[3] = array(
'multichoice' => false,
'options' => array('A','B')
);
$sections[4] = array(
'multichoice' => true,
'options' => array('A','B','C','D','E')
);
Example: Sandwich is the product. Type of bread is one "section" of choice. You may want light bread, dark bread, milk bread or vegan bread. Only one option can be chosen under this section. Now in the "salad" section, you may choose more than one type of salad to add to the bread.
Now, my boss asked for me to create a page listing all possible combinations in case the user is too lazy to build the product himself. So I must be able to generate a structure like this:
$combinations = array(
array(
1 => array('A','B'),
2 => 'A',
3 => 'A',
4 => array('B','D','E')
),
array(
1 => array('A'),
2 => 'B',
3 => 'A',
4 => array('A','B')
)
// etc...
);
I have managed to find all possible combinations using a random approach, generating hashes for comparision with what has already been generated. This actually works, but runs very slowly (this is brute-force basically):
...
function generate(){
$result = array();
$ids = array();
foreach($this->getSections() as $sect){
$items = $this->getSectionOptions($sect['id']);
if($sect['multi']=='N'){
$item = $items[rand(0, count($items)-1)];
$result[$sect['id']] = $item['id'];
$ids[] = $item['id'];
} else {
$how_many = rand(1,count($items));
shuffle($items);
for($i=1;$i<=$how_many;$i++){
$item = array_shift($items);
$result[$sect['id']][] = $item['id'];
$ids[] = $item['id'];
}
}
}
sort($ids);
return array(
'hash' => implode(',',$ids),
'items' => $result
);
}
function generateMany($attempts=1000){
$result = array();
$hashes = array();
for($i=1;$i<=$attempts;$i++){
$combine = $this->generate();
if(!in_array($combine['hash'],$hashes)){
$result[] = $combine['items'];
$hashes[] = $combine['hash'];
}
}
return $result;
}
...
I want your help to create something more precise and faster. Remember that each combination must have at least one option of each section. And also keep in mind that the order of options in multichoice sections is irrelevant, (i.e. E,B,A is the same as B,E,A)
Thanks
Thanks this was a puzzle really fun to do!
So how did I solve, recursion recursion recursion :D
I've started with the multi choice since it is the hardest one! (and actually it will solve also the mixing)
Explanation
To explain how I got it to work, lets take an example with choices A, B, C. We would have then the following combinations:
A B
A B C
A C
B
B C
C
If we take a look closer, we can see some how a pattern. Lets take the result list the first element (A)
B
B C
C
---
B
B C
C
Hmm, interesting... Now lets take again the first element (B)
C
---
C
It's a simple case but this will happen in any size case.
So I've made the recursion script to get to the end, then adding backwards, the iteration combination and duplicating it with the previous value.
And voila! this is it!
For the final mix where all the elements are demanded, I've made a very similar method, but it must contain 1 element of each
And it's quite fast!
Total time for 100000 iterations with 126 combinations: 14.410287857056 seconds
if you find any mistake ping me :D
Code
https://gist.github.com/MLoureiro/a0ecd1ef477e08b6b83a
I am trying to reorganize a PHP array (on a WordPress page). My site was coded by someone else, and I'm trying to make things better. I think they did things in a confusing way (poor use of the categories). Rather than recode the entire site, I wanted to see if someone could help me work around a fix.
So, basically, I have an array inside an array. A list of categories, and each category has specific details. I'm calling the categories, based on it's parent. I need to reorganize the list/array of categories, so they can print out in the order that I need them to.
$cat_tree = get_category_parents(wt_get_category_ID(), FALSE, ':', TRUE);
$top_cat = split(':',$cat_tree);
$parent = $top_cat[0];
$idObj = get_category_by_slug($parent);
$args = array(
'child_of' => $idObj->term_id,
'orderby' => 'name',
'order' => 'ASC',
'hide_empty' => 0);
$counter_c=1;
$categories = get_categories( $args );
if($counter_c == 1) $counter_c = '';
foreach ($categories as $category) {
?>
<div> this is where each category array info gets printed... information goes:<br />
Name: <?=$category->cat_name ?> (<?=$category->cat_ID ?>)
</div>
<?php if($counter_c == '') $counter_c = 1; $counter_c++; } // Reset Query
wp_reset_query();
?>
On one page, $cat_tree outputs "entertainment:actors:".
$parent or $topcat[0] outputs "entertainment"
$topcat[1] outputs "actors"
When the "foreach" runs, I get an list sorted by the ID ...
Actors (13)
Artists (14)
Directors (25)
Writers (26)
DeeJays (35)
I need to reorganize this list into a specific order. Each page is different. So I may have to reorganize a list differently for each page. I may need...
Writers (26)
Actors (13)
DeeJays (35)
Artists (14)
Directors (25)
So, I need a way to do this. I thought that maybe I could say "if" on the "actor page", slide "Writers" to the end of the array (I know there is some way to do this), then slide Actors to the end, then DeeJays, then Artists, then Directors. Then run the foreach.
Or, if page is something else, I can slide other elements to the end of the array. That seems a bit much, but it's the only work around that I could think of. Of course, my php knowledge is limited, so any help is appreciated.
You could try a custom sort:
$order = array('Writers', 'Actors', 'DeeJays', 'Artists', 'Directors');
function catsort($a, $b) {
global $order;
$ak = array_search($a->cat_name, $order);
$bk = array_search($b->cat_name, $order);
if($ak === false) $ak = 0;
if($bk === false) $bk = 0;
return $ak - $bk;
}
usort($categories, 'catsort');
Each page would just need a different order array.
I came across an article about Join decomposition.
SCENARIO #1 (Not good):
Select * from tag
Join tag_post ON tag_post.tag_id=tag.id
Join post ON tag_post.post_id=post.id
Where tag.tag='mysql'
SCENARIO #2 (good):
Select * from tag where tag='mysql'
Select * from tag_post Where tag_id=1234
Select * from post where post.id in (123,456,9098,545)
It was suggested to stick to scenario #2 for many reasons specially caching.
The question is how to join inside our application. Could u give us an example with PHP
after retrieving them individually?
(I have read MyISAM Performance: Join Decomposition?
but it did not help)
You COULD use an SQL subselect (if I understand your question). Using PHP would be rather odd while SQL has all the capabilities.
SELECT *
FROM `post`
WHERE `id` IN (
SELECT `post_id`
FROM `tag_post`
WHERE `tag_id` = (
SELECT `tag_id`
FROM `tag`
WHERE `tag` = 'mysql'
)
)
I'm not sure how your database structure looks like, but this should get you started. It's pretty much SQL inception. A query within a query. You can select data using the result of a subselect.
Please, before copying this SQL and telling me it's not working, verify all table and column names.
Before anyone starts to cry about speed, caching and efficiency: I think this is rather efficient. Instead of selecting ALL data and loop through it using PHP you can just select smaller bits using native SQL as it was ment to be used.
Again, I highly discourage to use PHP to get specific data. SQL is all you need.
edit: here's your script
Assuming you have some multi-dimensional arrays containing all data:
// dummy results
// table tag
$tags = array(
// first record
array(
'id' => 0,
'tag' => 'mysql'
),
// second record
array(
'id' => 1,
'tag' => 'php'
)
// etc
);
// table tag_post
$tag_posts = array(
// first record
array(
'id' => 0,
'post_id' => 0, // post #1
'tag_id' => 0 // has tag mysql
),
// second record
array(
'id' => 1,
'post_id' => 1, // post #2
'tag_id' => 0 // has tag mysql
),
// second record
array(
'id' => 2,
'post_id' => 2, // post #3
'tag_id' => 1 // has tag mysql
)
// etc
);
// table post
$posts = array(
// first record
array(
'id' => 0,
'content' => 'content post #1'
),
// second record
array(
'id' => 1,
'content' => 'content post #2'
),
// third record
array(
'id' => 2,
'content' => 'content post #3'
)
// etc
);
// searching for tag
$tag = 'mysql';
$tagid = -1;
$postids = array();
$results = array();
// first get the id of this tag
foreach($tags as $key => $value) {
if($value['tag'] === $tag) {
// set the id of the tag
$tagid = $value['id'];
// theres only one possible id, so we break the loop
break;
}
}
// get post ids using the tag id
if($tagid > -1) { // verify if a tag id was found
foreach($tag_posts as $key => $value) {
if($value['tag_id'] === $tagid) {
// add post id to post ids
$postids[] = $value['post_id'];
}
}
}
// finally get post content
if(count($postids) > 0) { //verify if some posts were found
foreach($posts as $key => $value) {
// check if the id of the post can be found in the posts ids we have found
if(in_array($value['id'], $postids)) {
// add all data of the post to result
$results[] = $value;
}
}
}
If you look at the length of the script above, this is exactly why I'd stick to SQL.
Now, as I recall, you wanted to join using PHP, rather doing it in SQL. This is not a join but getting results using some arrays. I know, but a join would only be a waste of time and less efficient than just leaving all results as they are.
edit: 21-12-12 as result of comments below
I've done a little benchmark and the results are quite stunning:
DATABASE RECORDS:
tags: 10
posts: 1000
tag_posts: 1000 (every post has 1 random tag)
Selecting all posts with a specific tag resulted in 82 records.
SUBSELECT RESULTS:
run time: 0.772885084152
bytes downloaded from database: 3417
PHP RESULTS:
run time: 0.086599111557
bytes downloaded from database: 48644
Please note that the benchmark had both the application as the database on the
same host. If you use different hosts for the application and the database layer,
the PHP result could end up taking longer because naturally sending data between
two hosts will take much more time then when they're on the same host.
Even though the subselect returns much less data, the duration of the requests is nearly 10 times longer...
I've NEVER expected these results, so I'm convinced and I will certainly use this information when I know that performance is important however I will still use SQL for smaller operations hehe...
I’m having a heck of a time figuring out how to post a rating for an individual item in a list of items,
this code let’s me rate multiple items, but not single items:
for($i=0;$i<2;$i++){
$doc_item_id = $_POST['item_id0'][$i];
$doc_rating = $_POST['document_rating'][$i];
$it_rt = array(
'item_id' => $doc_item_id,
'rating' => $doc_rating,
);
$this->purchases_model->update_document($it_rt);
}
whereas this code let’s me rate only the first item (or last item depending on where i put the "break;"):
foreach($_POST['item_id0'] as $doc_item_id){
foreach($_POST['document_rating'] as $doc_rating){
}
break;
}
$it_rt = array(
'item_id' => $doc_item_id,
'rating' => $doc_rating,
);
$this->purchases_model->update_document($it_rt);
any thoughts on how to correct either of these such that the user could rate the individual item of their choosing would be greatly appreciated,
If the user is supposed to choose the item to rate (instead of rating all items at the same time), you should allow him to do so (showing only one item, let him select one using radio buttons...), and then you should be able, by PHP side, to retrieve the index of the item to modify.
Finally, in order to modify only one item, your code should look like (PHP side, you will certainly have to update your HTML form as well)
$i = $_POST['item_index']; // Here I'm supposing that you have added radio buttons
// named 'item_index' to allow user to choose the item to rate
$doc_item_id = $_POST['item_id0'][$i];
$doc_rating = $_POST['document_rating'][$i] ;
$it_rt = array(
'item_id'=> $doc_item_id,
'rating' => $doc_rating,
);
$this->purchases_model->update_document($it_rt);
In fact it would be nearly your original code without the for loop.
Looping through the entire list just to limit the item you want, is kinda bad.
Here's an example on how to do it using some array functions:
$last=true; // false for first
if($last){
$id=end($_POST['item_id0']);
}else{
$id=reset($_POST['item_id0']);
}
// alternative: $id=($last)?end($_POST['item_id0']):reset($_POST['item_id0']);
// test id
if($id===false){
// no item was supplied
}
if(!isset($_POST['document_rating'][$id])){
// somehow, the item id doesn't have a matching document rating
}
// everything is okay!
$doc_item_id = $_POST['item_id0'][$id];
$doc_rating = $_POST['document_rating'][$id];
$it_rt = array(
'item_id' => $doc_item_id,
'rating' => $doc_rating,
);
$this->purchases_model->update_document($it_rt);
I have a question about associative arrays in php.
I have following array in which there are two items named 4 and 2 respectively.
$items = array(4,2);
Now i want to associate each item's quantity to it which can be done as follows:
$items['4']=23;
$items['2']=0;
which means that there are 23, 'item 4s' and no 'item 2'.
But I sometimes don't know in advance what is there in the $items so i want to associate quantity on basis of location. I wanted to do something like associate 23 to whatever is there on the zero location of the item array:
$items['items[0]']=23;
This of course did not work because its not the right way to extract whatever is placed on the zero location of items. Can anyone please tell me how do i do that?
You are confusing in the use of item and items. I imagine you have both an item array and an items array, else things can easily get hairy.
Anyhow, you just refer to it as a variable, not as a string:
$items[$item[0]] = 23;
Let me get this straight. So you start with an array that looks like this:
$items = array( 0 => 4, 1 => 2 )
And you want to end up with an array that looks like this: ?!
$items = array( 0 => 4, 1 => 2, 2 => 0, 4 => 23 )
I think you should use your array as a kind of "map". The item number is your key, and the quantity your value.
By calling
$items = array(4,2);
you create
$items[0] = 4;
$items[1] = 2;
but you want to use the 4 and 2 as a key in your array. So you should instead use
$items = array( 4 => false, 2 => false );
where false stands for an item that has not yet a quantity associated (could also be e.g. -1).
This creates
$items[2] = false;
$items[4] = false;
When using false, you can check for not assigned values by calling
if ($items[4] === false) {
echo "No quantity set!";
}
And now the second step.. if you want to assign the item #4 a quantity of 23, just call
$items[4] = 23;
So I don't think you will want to rely on the order inside your array..