Hi guys I'm in a bit on a fix here. I know how easy it is to build simple pagination links for dynamic pages whereby you can navigate between partial sets of records from sql queries. However the situation I have is as below:
COnsider that I wish to paginate between records listed in a flat file - I have no problem with the retrieval and even the pagination assuming that the flat file is a csv file with the first field as an id and new reocrds on new lines.
However I need to make a pagination system which paginates backwards i.e I want the LAST entry in the file to appear as the first as so forth. Since I don't have the power of sql to help me here I'm kinda stuck - all I have is a fixed sequence which needs to be paginated, also note that the id mentioned as first field is not necessarily numeric so forget about sorting by numerics here.
I basically need a way to loop through the file but backwards and paginate it as such.
How can I do that - I'm working in php - I just need the code to loop through and paginate i.e how to tell which is the offset and which is the current page etc.
I'm assuming you have a well-formed document with delimiters.
$array = explode("<>", $source); //parse data into an array
$backward = array_reverse($array); //entire array is reversed - last elements are now first
Use this code for a jumping off point.
$records = file('filedata.csv');
$recordsInOrder = array_reverse($records);
$first = 5;
$last = 10;
for($x = $first; $x <= $last; $x++) {
$viewTheseResults[] = $recordsInOrder[$x];
}
You can use an offset to determine the starting and ending keys in the array similar to how you would if you were pulling the data from a database.
Related
I'm trying to write a PHP function that takes some text to be displayed on a web page, and then based on some entered search terms, highlights the corresponding parts of the text. Unfortunately, I'm having a couple of issues.
To better explain the two issues I'm having, let's imagine that the following innocuous string is being searched on and will be displayed on the web page:
My daughter was born on January 11, 2011.
My first problem is that if more than one search term is entered, any placeholder text I use to mark the start and end of any matches for the first term may then be matched by the second term.
For example, I'm currently using the following delimiting strings to mark the beginning and end of a match (upon which I use the preg_replace function at the end of the function to turn the delimiters into HTML span tags):
'#####highlightStart#####'
'#####highlightEnd#####'
The problem is, if I do a search like 2011 light, then 2011 will be matched first, giving me:
My daughter was born on January 11, #####highlightStart#####2011#####highlightEnd#####.
Upon which when light is searched for, it will match the word light within both #####highlightStart##### and #####highlightEnd#####, which I don't want.
One thought I had was to create some really obscure delimiting strings (in perhaps a foreign language) that would likely never be searched on, but I can't guarantee that any particular string will never be searched on and it just seems like a really kludgy solution. Basically, I imagine that there is a better way to do it.
Any advice on this first point would be greatly appreciated.
My second question has to do with how to handle overlapping matches.
For example, with the same string My daughter was born on January 11, 2011., if the entered search is Jan anuar, then Jan will be matched first, giving me:
My daughter was born on #####highlightStart#####Jan#####highlightEnd#####uary 11, 2011.
And because the delimiting text is now a part of the string, the second search term, anuar will never be matched.
Regarding this issue, I am quite perplexed and really have no clue how to solve it.
I feel like I need to somehow do all of the search operations on the original string separately and then somehow combine them at the end, but again, I'm lost on how to do this.
Perhaps there's a way better solution altogether, but I don't know what that would be.
Any advice or direction on how to solve either or both of these problems would be greatly appreciated.
Thank you.
In this case I think it's simpler to use str_replace (though it won't be perfect).
Assuming you've got an array of terms you want to highlight, I'll call it $aSearchTerms for the sake of argument... and that wrapping the highlighted terms in the HTML5 <mark> tag is acceptable (for the sake of legibility, you've stated it's going on a web-page and it's easy to strip_tags() from your search terms):
$aSearchTerms = ['Jan', 'anu', 'Feb', '11'];
$sinContent = "My daughter was born on January 11, 2011.";
foreach($aSearchTerms as $sinTerm) {
$sinContent = str_replace($sinTerm, "<mark>{$sinTerm}</mark>", $sinContent);
}
echo $sinContent;
// outputs: My d<mark>au</mark>ghter was born on <mark>Jan</mark>uary <mark>11</mark>, 20<mark>11</mark>.
It's not perfect since, using the data in that array, the first pass will change January to <mark>Jan</mark>uary which means anu will no longer match in January - something like this will, however, cover most usage needs.
EDIT
Oki - I'm not 100% certain this is sane but I took a totally different approach looking at the link #AlexAtNet posted:
https://stackoverflow.com/a/3631016/886824
What I've done is looked at the points in the string where the search term is found numerically (the indexes) and built an array of start and end indexes where the <mark> and </mark> tags are going to be entered.
Then using the answer above merged those start and end indexes together - this covers your overlapping matches issue.
Then I've looped that array and cut the original string up into substrings and glued it back together inserting the <mark> and </mark> tags at the relevant points (based on the indexes). This should cover your second issue so you're not having string replacements replacing string replacements.
The code in full looks like:
<?php
$sContent = "Captain's log, January 11, 2711 - Uranus";
$ainSearchTerms = array('Jan', 'asduih', 'anu', '11');
//lower-case it for substr_count
$sContentForSearching = strtolower($sContent);
//array of first and last positions of the terms within the string
$aTermPositions = array();
//loop through your search terms and build a multi-dimensional array
//of start and end indexes for each term
foreach($ainSearchTerms as $sinTerm) {
//lower-case the search term
$sinTermLower = strtolower($sinTerm);
$iTermPosition = 0;
$iTermLength = strlen($sinTermLower);
$iTermOccursCount = substr_count($sContentForSearching, $sinTermLower);
for($i=0; $i<$iTermOccursCount; $i++) {
//find the start and end positions for this term
$iStartIndex = strpos($sContentForSearching, $sinTermLower, $iTermPosition);
$iEndIndex = $iStartIndex + $iTermLength;
$aTermPositions[] = array($iStartIndex, $iEndIndex);
//update the term position
$iTermPosition = $iEndIndex + $i;
}
}
//taken directly from this answer https://stackoverflow.com/a/3631016/886824
//just replaced $data with $aTermPositions
//this sorts out the overlaps so that 'Jan' and 'anu' will merge into 'Janu'
//in January - whilst still matching 'anu' in Uranus
//
//This conveniently sorts all your start and end indexes in ascending order
usort($aTermPositions, function($a, $b)
{
return $a[0] - $b[0];
});
$n = 0; $len = count($aTermPositions);
for ($i = 1; $i < $len; ++$i)
{
if ($aTermPositions[$i][0] > $aTermPositions[$n][1] + 1)
$n = $i;
else
{
if ($aTermPositions[$n][1] < $aTermPositions[$i][1])
$aTermPositions[$n][1] = $aTermPositions[$i][1];
unset($aTermPositions[$i]);
}
}
$aTermPositions = array_values($aTermPositions);
//finally chop your original string into the bits
//where you want to insert <mark> and </mark>
if($aTermPositions) {
$iLastContentChunkIndex = 0;
$soutContent = "";
foreach($aTermPositions as $aChunkIndex) {
$soutContent .= substr($sContent, $iLastContentChunkIndex, $aChunkIndex[0] - $iLastContentChunkIndex)
. "<mark>" . substr($sContent, $aChunkIndex[0], $aChunkIndex[1] - $aChunkIndex[0]) . "</mark>";
$iLastContentChunkIndex = $aChunkIndex[1];
}
//... and the bit on the end
$soutContent .= substr($sContent, $iLastContentChunkIndex);
}
//this *should* output the following:
//Captain's log, <mark>Janu</mark>ary <mark>11</mark>, 27<mark>11</mark> - Ur<mark>anu</mark>s
echo $soutContent;
The inevitable gotcha!
Using this on content that's already HTML may fail horribly.
Given the string.
In January this year...
A search/mark of Jan will insert the <mark>/</mark> around 'Jan' which is fine. However a search mark of something like In Jan is going to fail as there's markup in the way :\
Can't think of a good way around that I'm afraid.
Do not modify original string and store the matches in the individual array, either starts in odd and ends in even elements or store them in records (arrays of two items).
After searching for the several keywords, you end up with several arrays with matches. So the task now is how to merge two lists of segments, producing the segments that covers the areas. As the lists are sorted, this is a trivial task that can be solved in O(n) time.
Then just insert highlight tokens into positions recorded in the resulting array.
I need help to find workaround for getting over memory_limit. My limit is 128MB, from database I'm getting something about 80k rows, script stops at 66k. Thanks for help.
Code:
$posibilities = [];
foreach ($result as $item) {
$domainWord = str_replace("." . $item->tld, "", $item->address);
for ($i = 0; $i + 2 < strlen($domainWord); $i++) {
$tri = $domainWord[$i] . $domainWord[$i + 1] . $domainWord[$i + 2];
if (array_key_exists($tri, $possibilities)) {
$possibilities[$tri] += 1;
} else {
$possibilities[$tri] = 1;
}
}
}
Your bottleneck, given your algorithm, is most possibly not the database query, but the $possibilities array you're building.
If I read your code correctly, you get a list of domain names from the database. From each of the domain names you strip off the top-level-domain at the end first.
Then you walk character-by-character from left to right of the resulting string and collect triplets of the characters from that string, like this:
example.com => ['exa', 'xam', 'amp', 'mpl', 'ple']
You store those triplets in the keys of the array, which is nice idea, and you also count them, which doesn't have any effect on the memory consumption. However, my guess is that the sheer number of possible triplets, which is for 26 letters and 10 digits is 36^3 = 46656 possibilities each taking 3 bytes just for key inside array, don't know how many boilerplate code around it, take quite a lot from your memory limit.
Probably someone will tell you how PHP uses memory with its database cursors, I don't know it, but you can do one trick to profile your memory consumption.
Put the calls to memory-get-usage:
before and after each iteration, so you'll know how many memory was wasted on each cursor advancement,
before and after each addition to $possibilities.
And just print them right away. So you'll be able to run your code and see in real time what and how seriously uses your memory.
Also, try to unset the $item after each iteration. It may actually help.
Knowledge of specific database access library you are using to obtain $result iterator will help immensely.
Given the tiny (pretty useless) code snippet you've provided I want to provide you with a MySQL answer, but I'm not certain you're using MySQL?
But
- Optimise your table.
Use EXPLAIN to optimise your query. Rewrite your query to put as much of the logic in the query rather than in the PHP code.
edit: if you're using MySQL then prepend EXPLAIN before your SELECT keyword and the result will show you an explanation of actually how the query you give MySQL turns into results.
Do not use PHP strlen function as this is memory inefficient - instead you can compare by treating a string as a set of array values, thus:
for ($i = 0; !empty($domainWord[$i+2]); $i++) {
in your MySQL (if that's what you're using) then add a LIMIT clause that will break the query into 3 or 4 chunks, say of 25k rows per chunk, which will fit comfortably into your maximum operating capacity of 66k rows. Burki had this good idea.
At the end of each chunk clean all the strings and restart, set into a loop
$z = 0;
while ($z < 4){
///do grab of data from database. Preserve only your output
$z++;
}
But probably more important than any of these is provide enough details in your question!!
- What is the data you want to get?
- What are you storing your data in?
- What are the criteria for finding the data?
These answers will help people far more knowledgable than me to show you how to properly optimise your database.
hello guys i need your help please
iam creating a form which store data into mysql database and every thing is good tell now.
i need to generate a code for each member fill in the form this code i need it to be like ( SUD001 ) i want it to be unique and auto increasing one i means first one fill in the form get code of (SUD001) second one gets (SUD002) thread (SUD003) and so on.
what i need is a way to do that and then insert that code into mysql database, i got no problem in inserting it into the data base but i need a function or a way to generat it.
the way that i found now is to do it like that
<?php
$id_num = "SUD";
for ($i = 0; $i < 4; $i++) {
$id_num .= rand (0, 5);
}
echo $id_num;
?>
but this generates random number and iam not sure even it is unique or not any one
can help me please
you can do
$id = uniqid('SUD');
The trick here is recursion.
Steps:
Create a field with a UNIQUE constraint in the designated MySQL table.
Write a function 'generateCode' to generate a (long enough) code. *
Write a function that gets that code and tries to enter it in the database.
Fetch an eventual unique constraint violation and repeat the process from step 2 until it succeeds.
*) Be sure to use a code long enough to support your application for the coming years. 6 digits or a combination of alpha's and digits would most likely be enough.
I am new to taking an object oriented approach with php...actually I have a lot of learning to do overall, but the only way to learn is by doing.
So I have an array that holds several DateTime objects..I know this to be true because I have ran var_dump on the array and it indeed is holding several objects.
I need to go through each date and make sure there is never a difference greater than one day.
My research would seem to indicate that we cannot access or modify an object using the subscript operator:
$foo = $neat_array[$i+1]->format('U') ; //looking to format DateTime object as unix
//this returns an error every time
Okay I am fine with that, but I simply cannot figure out the syntax to access a specific item in the array so that it is seen as an object and does not pull an error.
I have pieced together that using -> is how I need to do it, but I never get any useable result.
Here is pseudo code of what I am trying to do
foreach($date_array as $date)
{
//check to see if the difference between the next date in the array and the current date of the array is greater than one day.
//I cannot use diff because I am on php 5.2 so I am trying this
$date->format('U') and then doing the math
}
Since learning by experimenting (and failing) is IMO a great way to learn, here is something to get you started.
$date_array = array(
new DateTime("tomorrow"),
new DateTime("now"),
new DateTime("yesterday"),
new DateTime("last day of october")
);
for( $i = 0, $count = count( $date_array); $i < $count; $i += 2) {
// Get the current object AND the next one in the array
echo $date_array[ $i ]->format('U'); echo '<br />';
echo $date_array[ $i + 1 ]->format('U'); echo '<br />';
// Now that you have the UNIX timestamps, you can do the math you need in here.
}
Note that the above will fail if the array contains an odd number of elements - I'll leave that fix up to you (if you need it).
Demo
Edit: You're probably getting that error for one of two reasons:
You're referencing an element that either isn't a DateTime object
You're referencing an element that doesn't exist in the date array
Here is a demo that shows error #2.
Edit: Here is a working example based off your answer that works for even and odd array sizes. It is essentially the same, except I don't bother with saving the values to variables or subtracting and comparing to 1 (since it's unnecessary).
$session_dates = $date_array = array(
new DateTime("tomorrow"),
new DateTime("yesterday"),
new DateTime("now"),
);
for( $i = 0, $count = count( $date_array) - 1; $i < $count; $i++)
{
if( ($date_array[ $i + 1 ]->format('U') - $date_array[ $i ]->format('U')) > 86400)
{
die( 'The date array does not contain consecutive dates.');
}
}
See it in action.
Okay..first of all, I appreciate everyone's help. Nickb, thanks for the help and the demos. As I surmised earlier, your suggestion that I might be trying to access an element that doesn't exist in the array was absolutely the case. What I am trying to do here is a pretty important part of a site I am creating..this whole mess will serve as a function in a class that checks reservation dates.
This is my first shot at Stack Overflow, so forgive me but I probably should have put more information in my initial question, but I was convinced I knew why the code wasn't working..which was completely incorrect!
See, since I always needed to know the current element, plus the next element in the array, my code was always breaking on the very last run of the loop, because it was trying to access the current key, plus the next one (which didn't exist on the last try).
Now, the real frustrating part is I screwed around with the for loop for this code all night and I still could not get it to cleanly go through every item without breaking...I would always over-run on that last item of the array and pull the error. I think the problem all along was that I never needed to go through every item in the array, just up to the second to last item (plus check the next one) then quit the loop.
Here is my code that finally works, but I don't really like it...I have a feeling there are some simple fixes in syntax and other areas that will make this more efficient...if not, the so be it...this is by the way, basically my testing code full of echos to see what was actually happening, but it is finally doing everything right:
$count = (count($session_dates)-1);
//never could get that last item to not trigger error, so subtracting one from the count was the only thing to end my frustration!!!but as I write this answer I think I see why and that is because we don't need to go through each item of the array, just each but the last item
echo '<p>'.$count.' this is the count of session dates </p>';
for ($i=0; $i<$count; $i++){
$unix_one = $session_dates[$i+1]->format('U');
//make some variables that are in the date format I want to visualize for echoing
$unix_echo_one = $session_dates[$i+1]->format("n".'/'."j".'/'."Y");
$unix_two = $session_dates[$i]->format('U');
$unix_echo_two = $session_dates[$i]->format("n".'/'."j".'/'."Y");
//see if the difference is greater than one day
if(($unix_one-$unix_two)/86400>1){
echo 'these dates are absolutely not consecutive, you fail!!';
//we only need one occurrence to wreck what we are trying to do, so just exit if it happens.
exit();
}
//I needed to see if any of the loop was happening to help try and figure this out!!
echo '<p>'.$unix_echo_one.'||'.$i.'||'.$unix_echo_two.'</p>';
}
I am paging through the elements of an array.
I get the total number of elements in the array with:
$total = count($myarray);
My paging function loads the current element on the page and provides "Previous" and "Next" links that have urls like:
http://myapp.com?page=34
If you click the link I grab that and load it onto the page by getting (I sanitize the $_GET, this is just for example):
$element = $myarray[$_GET['page']];
This should grab the element of the array with a key == $_GET['page'] and it does. However, the problem is that my total count of elements doesn't match some keys because while there are 100 elements in the array, certain numbers are missing so the 100th item actually has a key of 102.
How should I be doing this? Do I need to rewrite the keys to match the available number of elements? Some other method? Thanks for your input.
If you have gaps in the indices, you should reindex the array. You can do that before you generate the links, or probably easier on the receiving page:
$myarray = array_values($myarray);
$element = $myarray[$_GET['page']];
This would give you the 100th element, even if it previously had the key 102. (You could use a temporary array of course, if you need to retain the original indexing.)
you can use
$array = array_values($array);
How should I be doing this? Do I need
to rewrite the keys to match the
available number of elements? Some
other method?
No you don't need to worry about them not matching. Php arrays are associative containers, like dictionaries in other languages. If you define something at 98 and 100, 99 isn't sitting there in memory, the data structure behind the associative container only stores whats there. You're not wasting space by not "filling it up" up to count.
So the practice you describe is fine. If there is no page "99" nothing need show up in your array. It may be nice, however, to see that your array doesn't have anything for the 'page' parameter and display an error message.
But then why, when I access $total =
count($myarray); $myarray[$total]
where $total = 100 I do not get the
last element? I can put page=101 and
get one more record. I should not be
able to do this
Because count is counting how many things are in the array. If you have an array with only the even elements filled in, ie:
$myArray[0] = "This";
$myArray[2] = "is";
$myArray[4] = "even";
Here count($myArray) is 3. There's nothing in [1] or [3]. Maybe this is easier to see when you take numbers out of the equation. Arrays can have string indexes
$myArray = array();
$myArray["Hello"] = "A Bunch of";
$myArray["World"] = "words";
Here count($myArray) is 2.
In the first case, it wouldn't make sense to access $myArray[3] because nothing is there. Clearly in the second example, there's nothing at 2.