I am building pagination where I want to display '...' at certain points. The amount of pages is dictated by user selection; there are 650-ish posts, and users can choose to display 15, 25, 50, or 100 items at a time. Anyway, let's say that there are 45 pages.
If I am on page one, I want it to look like this:
1 2 3 ... 45
if I am on page 3, I want it to look like this:
1 ... 2 3 4 ... 45
if I am on page 44 (or 45), I want it to look like this:
1... 43 44 45
I am doing this all with JS, I'm not using any extra pagination packages from React. Currently, I have some functionality but I am getting stumped on how to adjust my for loop.
So, right now, if I am on page 1, it looks like :
1 2 ... 43 44 45
If I'm on page 3 :
1 2 3 4 ... 43 44 45
If I am on page 4:
1... 3 4 5 ... 43 44 45
**If I am on page 45:
1 ... 44 45
I looked around for a long time, and finally found a stackoverflow thread that helped. I implemented the basic while loop provided by derpirscher.
let pagination = [], i = 1;
while (i <= totalPageCount) {
if (i <= 1 ||
i >= totalPageCount - 2||
i >= currentPage - 1 && i <= currentPage + 1) {
pagination.push(i);
i++;
} else {
pagination.push('...');
//jump to the next page to be linked in the navigation
i = i < currentPage ? currentPage - 1 : totalPageCount - 2;
}
}
In this example, totalPageCount is how many pages there are total, and currentPage is the page the user is currently on. I tried to incorporate some sibling logic where if, for example, the left sibling index was greater than two, I inserted dots. That just resulted in there being dots on the left side of just about every page number, and I definitely didn't want that.
If it is important, I map through pagination directly in my render function to generate the template.
{pagination.map((pageNumber) => {
return (
<li key={pageNumber}>
<button
onClick={() => onPageChange(pageNumber)}
>
{pageNumber}
</button>
</li>
)
})}
Any thoughts or useful resources on this would be appreciated. I know my JS could be better, which is partly why I am trying to practice doing it this way!
Related
I'm a current software development student looking to get some help with my latest self-learning project.
My coworkers and I play Settlers of Catan, so a couple of weeks ago I thought it would be a neat idea if I could make a site that would tell a person what they can buy with the resource cards they have in their hand.
For those who don't know, the basic Catan purchases are as follows:
Building
Cost
City
2 wheat, 3 ore
Settlement
1 wood, 1 brick, 1 wool, 1 ore
Road
1 wood, 1 brick
Dev. Card
1 wool, 1 wheat, 1 ore
What I need to do is take a players hand, which has any number of resource cards, and run it through a function that will determine a list of possible options along with the cards left over.
As an example it should return something like:
Option 1: You can build 1 City, 1 Road. 1 Sheep left over.
Option 2: You can build 1 Settlement. 3 Ore and 1 Wheat left over.
The function that I am currently using in JavaScript is shown below:
/* Below dictionary is updated via HTML inputs
Also sheep == wool, ignore the lack of using the accurate card name
*/
var resources = { 'wood': 1, 'brick': 1, 'sheep': 1, 'wheat': 2, 'ore': 3 };
/* Takes resources (your hand), then tries to determine what
the player can build with that hand */
function basicBuilder(resources) {
var buildDict = { 'roads': 0, 'settlements': 0, 'cities': 0, 'dcards': 0 }
while (resources['wheat'] >= 2 && resources['ore'] >= 3) {
resources['wheat'] -= 2;
resources['ore'] -= 3;
buildDict['cities'] += 1;
}
while (resources['wood'] >= 1 && resources['brick'] >= 1 && resources['sheep'] >= 1 && resources['wheat'] >= 1) {
resources['wood'] -= 1;
resources['brick'] -= 1;
resources['sheep'] -= 1;
resources['wheat'] -= 1;
buildDict['settlements'] += 1;
}
while (resources['sheep'] >= 1 && resources['wheat'] >= 1 && resources['ore'] >= 1) {
resources['sheep'] -= 1;
resources['wheat'] -= 1;
resources['ore'] -= 1;
buildDict['dcards'] += 1;
}
while (resources['wood'] >= 1 && resources['brick'] >= 1) {
resources['wood'] -= 1;
resources['brick'] -= 1;
buildDict['roads'] += 1;
}
return buildDict;
}
This way would work fine if none of the buildings shared resources, but since some buildings use the same cards it doesn't suit my purpose. For example if you use the resources that I supplied, buildDict['settlements'] will equal 0 since all the wheat was already used up by the city.
There has to be a better and cleaner way to go about this, so my question to all of you is this:
How can I best determine the players possible build choices on their turn?
As a reminder, the input will be a hand of resources in dictionary form.
The output should be similar to:
Option {n}: You can build {x} Cities, {x} Settlements, {x} Roads, etc... with {x} cards leftover
Bonus points for any of the following:
Solutions that easily allow for additional buildings later on (expansion packs)
Solutions that can somehow accept resource trading (in Catan, 4 of a resource can be traded in for 1 of any other resource. If you own a port, this can be reduced to either 3 or 2 for one trades)
Solutions that will still allow for the display of what the leftover cards are
EDIT - Reworded the question to be more specific per bots request, very new to stackoverflow. See past question above
With a hand (dictionary) full of resource cards, what is a good JavaScript way to find all the possible combinations of buildings that could be purchased?
Each building could be purchased multiple times
There is no maximum or fixed hand size
When a resource is used for a building, it is removed from the hand
I thought that this kata will be very cool and easy to get back to js after a break.
I was so wrong lol.
So the URL to kata is here with all of the logic and math informations, i'll put the necessary ones below.
URL:
https://www.codewars.com/kata/51fda2d95d6efda45e00004e/train/javascript
Code:
class User {
constructor() {
this.rank = -8;
this.progress = 0;
this.rankTable=[-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8];
this.rankIndicator=0;
}
incProgress(rankOfActivity) {
if (rankOfActivity == 0 || rankOfActivity > 8 || rankOfActivity < -8) throw new error("Rank input out of range");
if (rankOfActivity <= this.rank - 2) return;
let diff = this.rankTable.indexOf(rankOfActivity)-this.rankTable.indexOf(this.rank);
if (diff==0) this.progress+=3;
else if(diff==-1) this.progress+=1;
else if(this.rank!=8){
this.progress+= 10*diff*diff;
while(this.progress>=100 && this.rank<8){
this.progress-=100;
this.rankIndicator++;
this.rank=this.rankTable[this.rankIndicator];
}
}
if (this.rank==8) this.progress=0;
}
}
Completing an activity that is ranked one ranking lower than the user's will be worth 1 point
After i saw that, my point of view was:
If the difference between activity rank and user's rank was -1 ,for example:
User's rank is -2 (index 6 in rankTable array)
Activity ranked at -3 (index 5 in rankTable array)
The difference would be 5-6 = -1
So it should add up 1 point of progress, and it looks like it doesn't do that and i cant figure it out why it doesn't add up.
Here are bunch of errors to show that it happens on any rank.
After applying rank of -1 the progress was expected to be 21, but was actually 20
After applying rank of 3 the progress was expected to be 61, but was actually 60
After applying rank of 8 the progress was expected to be 51, but was actually 50
//...
//if (rankOfActivity <= (this.rank - 2)) return;
//bug
let diff = (this.rankTable.indexOf(rankOfActivity)) - (this.rankTable.indexOf(this.rank));
if (diff <=-2)return;//
//fixed
//...
Personally, i do not like array and indexOf use here. Using a rank counter of [0 to 15] would be a little better.
In my app, I have some pagination code which calculates the pagination based on data from a REST API. When I add the page of pages, it is calculating from page 0 not from 1, so it says 0 of 9 and when it gets to the end it says 8 of 9, when it should say 1 of 9 and 9 of 9 at the end. So far my code is:
HTML
<p>Page {{page}} of {{pageCount}}</p>
JS
data: function() {
return {
page: 0
};
},
computed: {
pageCount() {
let l = this.result.length,
s = this.size;
return Math.floor(l / s);
},
paginated() {
const start = this.page * this.size,
end = start + this.size;
return this.result.slice(start, end);
}
},
Any ideas? Maybe I am calculating the math.floor method wrong?
Your page variable is 0 indexed instead of 1 index. You can keep it this way so that your pagination continues to work as intended, but where you are outputting it to the user you can simply add 1 so it makes sense to a user.
<p>Page {{page + 1}} of {{pageCount}}</p>
From what I understood, your pageCount() method is correct because you indeed have 9 pages so math.floor is not the problem, your page variable is the one that is incorrect but I cannot see where you are getting that variable from but a simple yet coarse solution would be just to add 1 to the variable.
${totalItems} = 22
${pageSize} = 10
I would like to know how can I create a calculation.
like that: first will show 1 - 10 items, if i click in the next page will show: 11 - 20 and the last one will be 21 - 22.
so basically the calculation will be showing the numbers 1 -10 then 11-20 everytime i click in the next page in my pagination.
can someone give a javascript sample for this? the only value I have is 22, so I would like do a calculation like I said above. and display this in my html.
Getting the index of the first item on a page
To get the first index of a page, you multiple the current page number by the number of items per page:
firstIndex = pageSize * pageNr
For your example:
console.log(
"Start of each page:",
[0, 1, 2].map(pageNr => 10 * pageNr)
);
Getting the index of the last item on a page
To get the index of the last item in a page, we can use this function to get the starting index of the next page, and subtract 1:
lastIndex = pageSize * (pageNr + 1) - 1
console.log(
"End of each page:",
[0, 1, 2].map(pageNr => 10 * (pageNr + 1) - 1)
);
Finding out how many pages there are
To see how many pages we need to render all our items, we divide the total number of items by the page size and round upwards using Math.ceil:
nrOfPages = Math.ceil(nrOfItems / pageSize)
Writing the actual code
Now that we've got the basic "math" covered, we can start writing actual functions and create a small app.
Depending on the format of the data, you can build some safety checks to make sure you cannot:
Request a page number that is out of range,
Request a negative page number
Input a 0 or negative page size,
etc.
Since you haven't provided anything useful to work with, I'm going to skip these measures and show you an example based on one array of items to paginate:
function getPageStart(pageSize, pageNr) {
return pageSize * pageNr;
};
function getPageLabel(total, pageSize, pageNr) {
const start = Math.max(
getPageStart(pageSize, pageNr),
0
);
const end = Math.min(
getPageStart(pageSize, pageNr + 1),
total
);
return `${start + 1} - ${end}`;
}
const itemsToShow = Array.from({ length: 22 }, (_, i) => `Item ${i + 1}`);
const size = 10;
const pages = Array.from(
{ length: Math.ceil(itemsToShow.length / size) },
(_, i) => getPageLabel(itemsToShow.length, size, i)
)
console.log(pages);
So Iam making a birt report on enclipse my goal is to output on each page amount of sales of a given person, not overall amount of sales of that person.
Consider this table(consider we only have 1 person):
personID sales date
------- ----- -----
111 10 2010-02-02
111 15 2010-02-03
111 5 2010-03-03
111 7 2010-04-03
111 8 2011-01-01
111 9 2013-01-01
111 20 2014-01-01
111 25 2014-03-02
The scenario on each page:
It shows 3 results(which I want)
but below each page its showing 99 sales (which I don't want)
What I want is that:
on 1st page it shows 30 sales (for 3 rows)
on 2nd page it shows 24 sales(for 3 rows)
and last page it shows 45(for 2 remaining rows)
what I did is that (i dont know if its right approach):
DENSE_RANK() OVER (ORDER BY sales) AS Row,
convert(integer,ROW_NUMBER() over(order by sales)/4) as page
which turned my table into
personID sales date Row page
------- ----- ----- ---- ----
111 10 2010-02-02 1 0
111 15 2010-02-03 2 0
111 5 2010-03-03 3 0
111 7 2010-04-03 4 1
111 8 2011-01-01 5 1
111 9 2013-01-01 6 1
111 20 2014-01-01 7 1
111 25 2014-03-02 8 2
As you can see there's another issue which is:
the 1st 3 rows got page(0)
but row 4-5-6-7 got page 1 which is wrong it should have been row 4-5-6 page 1
and row 7-8 page 2
I am also working on eclipse using JavaScript
<method name="onPageEnd"><![CDATA[var sales = this.getInstancesByElementName("sales");
var tmp=0;
if( sales != null )
{
for(var i=0; i< sales.length; i++ )
{
for(var j=0;j<3;j++)
{
//Instance of DataItemInstance
var sales = sales[i];
tmp+=parseInt(sales.getValue());
}
}
}
Once your query results are correct, you should compute SUM(sales) based on the page.
This can be done either with SQL (Oracle SQL syntax)
with x as
(
your_query_here
)
select x.*,
sum(sales) over (partition by page) -- IIRC
from x
or with BIRT, if you create a GROUP (called "page" for example) in your (layout) table, and an aggregate column binding based on this group.
Now to the query itself:
I don't think that the DB actually shows the results you state with your query.
Probably "Row" is defined in your query as
DENSE_RANK() OVER (ORDER BY "date" AS "Row"
I often use something like this for "matrix reports in SQL" and the like (asssuming you want 3 rows/page). This is a puire SQL solution:
with
y as (
select ...,
ROW_NUMBER() over(order by "sales") partition by ("personID") - 1 as "Row"
-- Note: Row is zero-based: 0,1,2,...
),
x as (
select y.*,
MOD(y."Row", 3) + 1 as "RowOnPage"
trunc(y."Row"/3) + 1 as "Page"
from y
)
select x.*,
sum("sales") over (partition by "personId", "Page") as SumSalesPerPersonAndPage
-- IIRC
from x
This is probably not quite correct (because I don't know how you intend to handle the different persons), but you get the idea...
For creating reports, it is a great advantage to know analytic functions.
I usually test my queries outside of BIRT (eg with SQL*Developer).