Building Pagination - pagination

Though this question has been asked several times but I really couldn't find any clue from it.
I am working on a client project in which the page numbers are generated like this:
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] and so on ...
So if there are 100 pages, these numbers will count to ... [100].
Here is the code that I am using:
{foreach var=$page from=$pages counter=$counter}
<li>$counter
{/foreach}
I am looking to paginate them such that at most only 10 pages numbers are displayed per page.
Any help will be highly appreciate.
Thanks in advance.

First you have to remember page number of the current page. Then decide about the behavior of the pagination. Example current page, next four pages, three previous pages, first page and last page
So for page nr 9 it looks like this:
[1] ... [6] [7] [8] [bold 9] [10] [11] [12] [13] ... [100]
Next (hardest) thing is defining an efficient algorithm.
For example:
Define an empty list
Put current page on the list.
Add desired amount of elements before the current page (for example first element and m elements to the left of the current page)
Add desired amount of elements after the current page (for example n elements to the right of the page and the last element)
Iterate over the list to produce the pagination.
Iteration example:
foreach element on list:
if element.value == current_page
print_bold [element.value]
else
print [element.value]
nextElement = list.nextElement
if nextElement.value - element.value > 1:
print "..."

Here is an algorithm you might want to use, mostly because it is easy and actually works: (I just grabbed it from one of my projects)
Create an empty list. (This will be filled with with integers representing the page numbers)
Add page 1 and 2
Add the current page - 1
Add the current page
Add the current page + 1
Add the last page - 1
Add the last page
Only keep the unique records
Sort the list ascending
Filter each item below 0 or bigger than the last page
The last page can be calculated by: round(Total items / items per Page) + 1
You may add more items in the front, middle or end simply by adding more items with a greater offset like current page + 2.
You may also not use filters and only add items if they meet the condition upfront, but this is a very readable solution in my opinion.
Reference implementation:
$totalPages = ceil(count($items) / $perPage);
$pagination = array_unique([
1, 2,
$page - 1,
$page,
$page + 1,
$totalPages - 1,
$totalPages,
]);
sort($pagination);
$pagination = array_filter(
$pagination,
fn($page) => $page > 0 && $page <= $totalPages
);
If you want the dots between your list, you need to check on each iteration rendering an item if the difference between the last item and the current item is greater than one. Be sure to make sure a last item exists or to not run the code in the first iteration.
$page - $pagination[$i - 1] > 1
Good luck!

For large numbers of pages, consider "logarithmic" pagination, described here (PHP code included):
How to do page navigation for many, many pages? Logarithmic page navigation

Related

Using fqdn_rand and remove numbers from a pool

Is there anyway I can ensure that the pool of numbers used when calling fqdn_rand will never pick the same number in a single puppet run?
I have a cronjob and I never want any of my 5 jobs to run on the same day. I've been using fqdn_rand to generate the jobs on random days but finding some servers will have jobs which run on the same days. In my mind if these numbers are removed from the possible selected numbers, I'll never get the same result.
Is there anyway I can ensure that the pool of numbers used when calling fqdn_rand will never pick the same number in a single puppet run?
You cannot ensure that multiple calls to fqdn_rand() will all return distinct values. However, you can use fqdn_rand() to perform a selection of multiple items without duplication. There are several ways you could do that, but I'm going to suggest combining it with the reduce() function to perform a (partial) shuffle of the list of options from which you want to select.
It isn't clear in the question whether you want to select from days of the week, days of the month, or something else, but for simplicity, I'll assume that you are selecting among days of the week, in the form of numbers 1 through 7:
$day_numbers = [ 1, 2, 3, 4, 5, 6, 7 ]
Although Puppet does not have general purpose looping statements such as for or while, you can iterate (among other ways) by using one of several iteration functions to process an iterable value. For example:
$selected = [ 0, 1, 2, 3, 4 ].reduce($day_numbers) |$numbers, $i| {
$selection = fqdn_rand($day_numbers.length - $i - 1, $i)
[
$numbers[0, $i], # The already selected numbers
$numbers[$selection + $i, -1], # The tail starting at the next selection
$numbers[$i, $selection] # The slice between the previous two
].flatten
}[0, 5]
That executes the lambda five times, associating successive values from 0 to 4 with variable $i. On the initial iteration, $day_numbers is associated with $numbers. On each subsequent iteration, the return value of the lambda's previous iteration is associated with $numbers.
On each iteration of the lambda, the $i elements selected in previous iterations occupy the first $i positions of $numbers. The fqdn_rand() function is used to compute the index of one of the other elements, then the tail is rotated to bring the selected element into position.
Ultimately, an array containing the first five elements of the result (those being all the selected values) is assigned to $selected.

Using xpath for an array of WebElements

I need to scrape some data off tags in a page which further has more DOM elements.
The articles are repeated and they have an xpath as:
//*[#id="post_page"]/div/div[2]/main/div/div/div/div[2]/div[2]/div/div[3]/div/article[N]
where 'N' represents the Nth article.
And within each article, the xpath for the element I'm interested in is:
/div/div/div/div/div/div/div[3]/div[1]/button[1]/span
The first thing I did was to use
Elements = driver.find_elements(By.XPATH, <first_path>)
And it fetched me all the articles in the page. PS: I did not add [N] because that would only fetch a specific article, and I'm interested in all.
Then, for each element in the list, I used find_element using the second path as follows:
for elem in Elements:
Required.append(elem.find_element(By.XPATH, <second_path>))
Where Required is a list in which I'll be storing the data. And this is where I got the element does not exist error.
I also tried adding a . before <second_path> but that didn't solve the issue either.
The complete xpath of the element is:
//*[#id="post_page"]/div/div[2]/main/div/div/div/div[2]/div[2]/div/div[3]/div/article[N]/div/div/div/div/div/div/div[3]/div[1]/button[1]/span
And the CSS Selector for the same is:
#post_page > div > div._UuSG.w77Za._21rSD._3SBW4 > main > div > div > div > div._UuSG._ayWa._3dGg1.Vlb1o._1vyTb > div._UuSG.qzupC._3cqkW > div > div:nth-child(3) > div > article:nth-child(N) > div > div > div > div > div > div > div._UuSG._3VzCT._2FoTG > div._UuSG._3dGg1._2VJFi._2h1-g > button:nth-child(1) > span
I also tried an approach using a loop where I increment a counter variable and use that as N for the whole xpath, but that didn't seem to work either. Got the same error.
Any help would be greatly appreciated.
EDIT[1]
The last span has the following class names:
<span class="_UuSG _3_54N a8-QN _2cSLK L4pn5 RiX17">Stuff I need</span>
Which are unique (collectively) in the page. This information might be relevant somehow.
I think I know your problem. When you do
Elements = driver.find_elements(By.XPATH, <first_path>)
you have already found all the elements you need here. So in your for loop, just use elem, no more "finding" is needed.
for elem in Elements:
Required.append(elem)
I would use .// to select using descendent-or-self axis starting from the current node (. means current node).
You have already tried with ./, which is pretty close.
xpath ".//span", what does the dot mean?
What is meaning of .// in XPath?

What built in function can I use to get the last 3 elements in a list in python

I wanted to get the last 3 items or elements in a list. Also making sure that my code works no matter how many items are in the list.
sports_goods=["bat","ball","base_ball","hockey","basket_ball"]
I have tried to use Splice(:-1) and also sports_goods[-1]
but, couldn't get it working.
sports_goods=[bat,ball,base_ball,hockey,basket_ball]
My code is
sports_goods.splice(-3:-1)
And
sports_goods[-1]
Expected result:
The last 3 elements which are base_ball, hockey and basket_ball.
Actual results:
Shows error...Name error to be specific.
you should at least post valid python code. your list isnt valid since your words are not wrapped in quotes to make them strings. However if you have a list you can just select elements from the third last index to the end like so
sports_goods=['bat', 'ball', 'base_ball', 'hockey', 'basket_ball']
print('the last 3 elements are: ', ", ".join(sports_goods[-3:]))
OUTPUT
the last 3 elements are: base_ball, hockey, basket_ball

Problems to extract links with webscraping

I want to extract the links of the toys listed in this webpage:
https://cebra.com.ar/category/73/Juego-de-Construccion.html
I have an entire procedure (I don´t copye here because it´s very long and complicated), in which in some part I have the following code that doesn´t work:
Cells(erow, 1) = html.getElementsByTagName("a").href
Any idea to solve this?
Thanks a lot!
getElementsByTagName returns a collection and indeed you would need to index into it to get a particular element.
However, you don't want all a tags. That is inefficient and you would need an additional test to limit to those of interest. You want specifically the links for products so use an attribute = value css selector to get those:
Dim links As Object, i As Long
Set links = html.querySelectorAll("[href^=product]")
For i = 0 to links.Length - 1
ActiveSheet.Cells(erow + i, 1) = links.item(i).href
Next
This:
[href^=product]
looks for href attributes whose value starts with, ^, product.
If you look at the page html you can see each of your target links begins with that substring
The function getElementsByTagName() of the object HTMLDocument returns a list, but you're trying to access the property .href of one object as if it was a single object.
You should replace this:
Cells(erow, 1) = html.getElementsByTagName("a").href
with this
Cells(erow, 1) = html.getElementsByTagName("a")[yourIndex].href
... where yourIndex is a number representing the index of your list (0, 1,... n).
Of course you'll have to find the correct rule to get the right a element at the right place, as just getting all the elements of the document with tag a retrieves 278 elements in your page (including all the page headers, footers and other things I don't really think you need):

Access list element using get()

I'm trying to use get() to access a list element in R, but am getting an error.
example.list <- list()
example.list$attribute <- c("test")
get("example.list") # Works just fine
get("example.list$attribute") # breaks
## Error in get("example.list$attribute") :
## object 'example.list$attribute' not found
Any tips? I am looping over a vector of strings which identify the list names, and this would be really useful.
Here's the incantation that you are probably looking for:
get("attribute", example.list)
# [1] "test"
Or perhaps, for your situation, this:
get("attribute", eval(as.symbol("example.list")))
# [1] "test"
# Applied to your situation, as I understand it...
example.list2 <- example.list
listNames <- c("example.list", "example.list2")
sapply(listNames, function(X) get("attribute", eval(as.symbol(X))))
# example.list example.list2
# "test" "test"
Why not simply:
example.list <- list(attribute="test")
listName <- "example.list"
get(listName)$attribute
# or, if both the list name and the element name are given as arguments:
elementName <- "attribute"
get(listName)[[elementName]]
If your strings contain more than just object names, e.g. operators like here, you can evaluate them as expressions as follows:
> string <- "example.list$attribute"
> eval(parse(text = string))
[1] "test"
If your strings are all of the type "object$attribute", you could also parse them into object/attribute, so you can still get the object, then extract the attribute with [[:
> parsed <- unlist(strsplit(string, "\\$"))
> get(parsed[1])[[parsed[2]]]
[1] "test"
flodel's answer worked for my application, so I'm gonna post what I built on it, even though this is pretty uninspired. You can access each list element with a for loop, like so:
#============== List with five elements of non-uniform length ================#
example.list=
list(letters[1:5], letters[6:10], letters[11:15], letters[16:20], letters[21:26])
#===============================================================================#
#====== for loop that names and concatenates each consecutive element ========#
derp=c(); for(i in 1:length(example.list))
{derp=append(derp,eval(parse(text=example.list[i])))}
derp #Not a particularly useful application here, but it proves the point.
I'm using code like this for a function that calls certain sets of columns from a data frame by the column names. The user enters a list with elements that each represent different sets of column names (each set is a group of items belonging to one measure), and the big data frame containing all those columns. The for loop applies each consecutive list element as the set of column names for an internal function* applied only to the currently named set of columns of the big data frame. It then populates one column per loop of a matrix with the output for the subset of the big data frame that corresponds to the names in the element of the list corresponding to that loop's number. After the for loop, the function ends by outputting that matrix it produced.
Not sure if you're looking to do something similar with your list elements, but I'm happy I picked up this trick. Thanks to everyone for the ideas!
"Second example" / tangential info regarding application in graded response model factor scoring:
Here's the function I described above, just in case anyone wants to calculate graded response model factor scores* in large batches...Each column of the output matrix corresponds to an element of the list (i.e., a latent trait with ordinal indicator items specified by column name in the list element), and the rows correspond to the rows of the data frame used as input. Each row should presumably contain mutually dependent observations, as from a given individual, to whom the factor scores in the same row of the ouput matrix belong. Also, I feel I should add that if all the items in a given list element use the exact same Likert scale rating options, the graded response model may be less appropriate for factor scoring than a rating scale model (cf. http://www.rasch.org/rmt/rmt143k.htm).
'grmscores'=function(ColumnNameList,DataFrame) {require(ltm) #(Rizopoulos,2006)
x = matrix ( NA , nrow = nrow ( DataFrame ), ncol = length ( ColumnNameList ))
for(i in 1:length(ColumnNameList)) #flodel's magic featured below!#
{x[,i]=factor.scores(grm(DataFrame[, eval(parse(text= ColumnNameList[i]))]),
resp.patterns=DataFrame[,eval(parse(text= ColumnNameList[i]))])$score.dat$z1}; x}
Reference
*Rizopoulos, D. (2006). ltm: An R package for latent variable modelling and item response theory analyses, Journal of Statistical Software, 17(5), 1-25. URL: http://www.jstatsoft.org/v17/i05/

Resources