How to make an algorithm for complex pagination in JS? - javascript

I'm trying to make pagination work but it's a bit complex for me so I need help. If someone has an idea how to make it, I would be thankful.
I need to show items that the user owns. One page can have maximum of 12 items displayed.
I have a pageData array, that returns the number of items for a category. Category "shirts" has 2 items.
pageData = [ {id:'pants',totalItems: 15},
{id:'shirts',totalItems: 2}, {id:'dresses',totalItems: 13}]
On first load I need to show 12 items (lets say 12 pants), on second load (click on load more button) I need to show 3 more pants, 2 shirts and 7 dresses.
Metadata for an item has to be fetched via api (this is just an example how to get id of specific item, I'm trying to put it somehow into code but don't have clear idea how):
for (let i = 0; i < pageData['pants'].totalItems; i++) {
const metadata = await api.getMetadata({
userId: 'userId',
id: i,
});
}
This is some code that I have but like I said, I don't really have an idea how to make it:
const getItems = async (pageData, currentPage) => {
const itemsPerPage = 12;
let fetchedData = [];
let total = 0;
let start = currentPage * itemsPerPage;
for (let i = 0; i < pageData.length; i++) {
const minRange = total;
const maxRange = minRange + itemsPerPage;
if (start >= minRange && start <= maxRange) {
const minId = minRange - total;
const maxId = maxRange - total;
for (let j = minId; j < maxId; j++) {
const metadata = await api.getMetadata({
userId: 'userId',
id: j,
});
fetchedData.push(metadata);
}
}
total += itemsPerPage;
}
};

Related

Cannot run the same code multiple times on the same page

In order to run the same code twice on the same page, while changing the values of the 'to' and 'from' parameters in the URL endpoint to NPR-AED, to prevent any errors or conflicts, what modifications should I make?
When I try to run the code first one run but the second one doesn't. Can anyone help me with it.
const API_URL_AED_NPR = "https://api.fastforex.io/fetch-one?from=AED&to=NPR&api_key=xxxx-xxx-xxx";
const PAGE_SIZE = 50;
let exchangeRates = [];
// Fetch exchange rate data from the API
fetch(API_URL_AED_NPR)
.then(response => response.json())
.then(data => {
// Extract the exchange rate from the response data
const rate = data.result.AED;
// Generate the exchange rate data for AED values from 1 to 100
for (let i = 1; i <= 100; i++) {
exchangeRates.push({
aed: i,
npr: (rate * i).toFixed(2)
});
}
// Generate the exchange rate data for AED values from 100 to 900 in hundreds
for (let i = 100; i <= 900; i += 100) {
exchangeRates.push({
aed: i,
npr: (rate * i).toFixed(2)
});
}
// Generate the exchange rate data for AED values from 1000 to 10000 in thousands
for (let i = 1000; i <= 10000; i += 1000) {
exchangeRates.push({
aed: i,
npr: (rate * i).toFixed(2)
});
}
// Generate the numeric pagination buttons
const NUM_PAGES = Math.ceil(exchangeRates.length / PAGE_SIZE);
const numericPagination = document.getElementById("numeric-pagination");
for (let i = 2; i <= NUM_PAGES; i++) {
const button = document.createElement("button");
button.classList.add("page-toggle-button");
button.id = `page-${i}`;
button.textContent = i;
button.onclick = function() {
renderPage(i);
};
numericPagination.appendChild(button);
}
// Render the first page
renderPage(1);
});
// Get the table element
const exchangeRateTable = document.getElementById("conversion-rates-aed-npr");
function renderPage(page) {
// Clear the table
exchangeRateTable.innerHTML = "";
// Calculate the starting and ending indices for the current page
const startIndex = (page - 1) * PAGE_SIZE;
const endIndex = startIndex + PAGE_SIZE;
// Get the exchange rates for the current page
const pageRates = exchangeRates.slice(startIndex, endIndex);
// Populate the table with the exchange rates for the current page
for (const {
aed,
npr
} of pageRates) {
// Create a new table row
const row = document.createElement("tr");
// Create the AED and NPR cell elements
const aedCell = document.createElement("td");
const nprCell = document.createElement("td");
// Set the text content of the cells to the AED and NPR values
aedCell.textContent = `${aed} AED`;
nprCell.textContent = `${npr} NPR`;
// Append the cells to the row
row.appendChild(aedCell);
row.appendChild(nprCell);
// Append the row to the table
exchangeRateTable.appendChild(row);
}
// Highlight the current page button
document.querySelector('.page-toggle-button.active').classList.remove('active');
document.getElementById(`page-${page}`).classList.add('active');
}
<table>
<thead>
<tr>
<th>AED</th>
<th>NPR</th>
</tr>
</thead>
<tbody id="conversion-rates-aed-npr">
</tbody>
</table>
<div id="pagination-controls">
<div id="numeric-pagination">
<button class="page-toggle-button" id="page-1" onclick="renderPage(1)">1</button>
</div>
</div>

Pick random index and change till all unique are done then restart

I have image gallery with 6 image slot and i have a array with n no of image object like this
"src" : {
"1x" : "/clients/Logo-1.png",
"2x" : "/clients/Logo-1#2x.png",
"3x" : "/clients/tLogo-1#3x.png"
},
"alt" : "xyz"
}
what i want is to show random 6 image from array and then every 5 sec randomly one slot need to be change and get update with a new unique image which must not be in first 6 slot and then after finishing all it should continue the 5 sec change with a new unique image which must not be in those 6 slot.
what i have tried
let randomList = this.shuffleArray(this.LogosListObj);
let FirstSixImg = randomList.slice(0, 6);
let LeftAllImg = randomList.slice(6 + 1);
let RandomIndex = this.randomNoRepeats([0,1,2,3,4,5])
let RandomSecoundImg = this.randomNoRepeats(Array.apply(null, new Array(LeftAllImg.length)).map(function(el, i) {return i}))
let RandomFirstImg = this.randomNoRepeats(Array.apply(null, new Array(FirstSixImg.length)).map(function(el, i) {return i}))
this.ImageToShowList = [...FirstSixImg];
let checkArr = [];
let checkArr2 = [];
let flag = false;
let index,secndIndex,thirdIndex;
const LogoChange = (arr) =>{
if(!flag) {
secndIndex = RandomSecoundImg();
console.log('1st',secndIndex)
if(checkArr.indexOf(secndIndex) == -1) {
index = RandomIndex();
checkArr.push(secndIndex)
ctl.ImageToShowList[index] = {};
ctl.ImageToShowList[index] = LeftAllImg[secndIndex];
Vue.set(ctl.ImageToShowList, index, LeftAllImg[secndIndex])
ctl.PreviousImgObj = {...LeftAllImg[secndIndex]};
} else {
flag = true;
checkArr = [];
}
}
if(flag) {
thirdIndex = RandomFirstImg();
console.log('2nd',thirdIndex)
if(checkArr2.indexOf(thirdIndex) == -1) {
checkArr2.push(thirdIndex)
ctl.ImageToShowList[thirdIndex] = {};
ctl.ImageToShowList[thirdIndex] = FirstSixImg[thirdIndex];
Vue.set(ctl.ImageToShowList, thirdIndex, FirstSixImg[thirdIndex])
ctl.PreviousImgObj = {...FirstSixImg[thirdIndex]};
}else {
flag = false;
checkArr2 = [];
LogoChange();
}
}
}
setInterval(()=>{
LogoChange();
}, 1000);
where randomNoRepeats is
randomNoRepeats : (array) => {
var copy = array.slice(0);
return function() {
if (copy.length < 1) { copy = array.slice(0); }
var index = Math.floor(Math.random() * copy.length);
var item = copy[index];
copy.splice(index, 1);
return item;
};
and shuffleArray is
shuffleArray : (array) => {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
},
this.ImageToShowList is used in html part to display
Any help with logic or change will be appreciate.
I have my example fiddle here, but this is the breakdown:
Your data would look something like this:
let galleryPool = [
{
"src" : {
"1x" : "/clients/Logo-1.png",
"2x" : "/clients/Logo-1#2x.png",
"3x" : "/clients/tLogo-1#3x.png"
},
"alt" : "xyz",
"color": "red"
},
...
]
(I added a color property so you could see the changes since I don't actually have any images.)
The first thing I do is drop in my handy-dandy Fisher–Yates shuffle, since this is a great way to get a random element out of an array.
Array.prototype.shuffle = function() {
let currentIndex = this.length, randomIndex;
while (currentIndex != 0) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
[this[currentIndex], this[randomIndex]] = [this[randomIndex], this[currentIndex]];
}
return this;
}
I initialize the gallery first, and set up the 5-second timeout to change images in the gallery.
let displayedGallery = []
initialGallerySetup();
function initialGallerySetup() {
let galleryContainer = document.getElementById("gallery-container")
galleryPool.shuffle()
displayedGallery = galleryPool.splice(0, 6)
for(let index = 0; index < displayedGallery.length; index++) {
let data = displayedGallery[index]
galleryContainer.appendChild(generateGalleryItem(data))
}
galleryTickUpdate()
}
this function makes an img dom element I can then add to the container. I'm using your src here but you can change whatever values in here to alter how all of the images in the gallery are displayed.
function generateGalleryItem(data) {
let item = document.createElement("img")
item.style.backgroundColor = data.color
item.src = data.src[0]
item.className = "gallery-item"
return item
}
This function calls itself every 5 seconds, and will pick a random item from the gallery to swap with another item not currently displayed.
function galleryTickUpdate() {
setTimeout(() => {
let randomIndex = Math.floor(displayedGallery.length * Math.random())
swapItemAtIndex(randomIndex)
galleryTickUpdate()
},5000)
}
Here's the magic. I grab the randomly chosen item out of the displayed gallery items, pick a new item from the unused gallery pool items, put the old one back in the pool and the new one gets pushed back into the gallery display in the same spot.
function swapItemAtIndex(index) {
let galleryContainer = document.getElementById("gallery-container")
let displaySlot = galleryContainer.children[index]
let returning = displayedGallery[index]
galleryPool.shuffle()
let newDisplay = galleryPool.pop();
displayedGallery[index] = newDisplay
galleryPool.push(returning)
galleryContainer.insertBefore(generateGalleryItem(newDisplay), displaySlot)
galleryContainer.removeChild(displaySlot)
}
If you want to enforce running through the whole array, just check when the galleryPool array is empty, then repopulate it and re-run the init function. Otherwise, this will happily run forever.
here is a simple script. you will need the following
pool this will contain all possible values you would like
single function to update the show array
Test Here
I've tried to put as much comments as possible
// Pool with all possible images (you may use any type of text as long as each value is unique)
const pool = [
"1.png",
"2.png",
"3.png",
"4.png",
"5.png",
"6.png",
"7.png",
"8.png",
"9.png",
"10.png",
];
// this will store the "6" unique elements which you will be displaying
let show = [];
// get the first 6 random but unique elements. we will monitor the length of the `show` array
while(show.length < 6){
// randomly sort the pool and get the first element of the sorted array and store it into a `pooled` variable.
let pooled = pool.sort(() => 0.5 - Math.random())[0];
// check if the pooled value exists in the `show` array if it doesnt then add it and repeat the loop till all 6 slots are filled with unique values
if(!show.includes(pooled)){
// add `pooled` item to the `show` array
show.push(pooled);
}
}
// do the same as the above with a slight change, of only replacing one till all are unique.
function after5Mins(){
// get a new random item from the pool
let newPoolItem = pool.sort(() => 0.5 - Math.random())[0];
// using a while loop check if the new `pool item` is in the show array, if not then skip and add it
while(show.includes(newPoolItem)){
newPoolItem = pool.sort(() => 0.5 - Math.random())[0]; // set the pool item to a random one, then loop and check, follow previous comment
}
// once a new un used item is found, then assign it to a random position
show[Math.floor(Math.random()*show.length)] = newPoolItem;
}
// call the after 5 mintes using
setTimeout(()=>{
after5Mins();
}, 300000);

Optimizing process determining license usage in time

I am trying to find an efficient way to go through a big amount of data to determine how many units are processed at once.
So the data that I am receiving are just simple pairs:
{timestamp: *, solvetime: *}
What I need, is to see how many things are processed at each second.
To help you visualize what I mean: here is an example of data that I receive:
{{timestamp: 5, solvetime: 3},{timestamp: 7, solvetime: 5},{timestamp: 8, solvetime: 2},{timestamp: 12, solvetime: 10},{timestamp: 14, solvetime: 7}}
The chart below should help you understand how it looks in time:
https://i.stack.imgur.com/LEIhW.png
This is a simple case where the final calculation contains every second, but if the timeframe is much wider I show only 205 different times in this timeframe. E.g. if the time btw the first and the last timestamp is 20500 seconds I would calculate the usage for every second and divide the time into 205 parts - 100 seconds each and show only the second with the highest usage.
What I am doing right now is to iterate through all the pairs of input data and create a map of all the seconds, once I have it I go through this map again to find the highest usage in each time period (of 205 time periods I divide the whole time-frame in) and append it to the map of 205 timestamps.
It's working correctly, but it's very very slow and I feel like there is some better way to do it, a table might be faster but it is still not too efficient is it?
Here is the actual code that does it:
// results contain all the timestamps and solvetimes
// Timeframe of the chart
var start = Math.min.apply(Math, msgDetailsData.results.map((o) => { return o.timestamp; }))
var end = Math.max.apply(Math, msgDetailsData.results.map((o) => { return o.timestamp; }))
// map of all seconds in desired range (keys) the values are counter ofprocesses run in a given second
let mapOfSecondsInRange = new Map();
for (let i = start; i <= end; i++) {
mapOfSecondsInRange.set(i, 0);
}
// we go through every proces and add +1 to the value of each second in which the task was active
for (let element of msgDetailsData.results) {
var singleTaskStart = element.timestamp - Math.ceil(element.solveTime);
if (singleTaskStart < start) {
for (let i = singleTaskStart; i < start; i++) {
mapOfSecondsInRange.set(i, 0);
}
start = singleTaskStart;
}
for (let i = singleTaskStart; i < element.timestamp; i++) {
mapOfSecondsInRange.set(i, mapOfSecondsInRange.get(i) + 1);
}
}
// Preparation for the final map - all the seconds in the range divided into 205 parts.
const numberOfPointsOnChart = 205;
var numberOfSecondsForEachDataPoint = Math.floor((end - start) / numberOfPointsOnChart) + 1;
var leftoverSeconds = ((end - start) % numberOfPointsOnChart) + 1;
var highestUsageInGivenTimeframe = 0;
var timestampOfHighestUsage = 0;
let mapOfXXXDataPoints = new Map();
var currentElement = start;
for (let i = 0; i < numberOfPointsOnChart; i++) {
if (leftoverSeconds === 0) {
numberOfSecondsForEachDataPoint = numberOfSecondsForEachDataPoint - 1;
}
if (currentElement <= end) {
for (let j = 0; j < numberOfSecondsForEachDataPoint; j++) {
if (j === 0) {
highestUsageInGivenTimeframe = mapOfSecondsInRange.get(currentElement);
timestampOfHighestUsage = currentElement;
}
else {
if (mapOfSecondsInRange.get(currentElement) > highestUsageInGivenTimeframe) {
highestUsageInGivenTimeframe = mapOfSecondsInRange.get(currentElement);
timestampOfHighestUsage = currentElement;
}
}
currentElement = currentElement + 1;
}
mapOfXXXDataPoints.set(timestampOfHighestUsage, highestUsageInGivenTimeframe);
leftoverSeconds = leftoverSeconds - 1;
}
}

Issue with Pagination in nodejs with mongoose and query optimization

Having issue with database query in mongoose I am setting value but not getting correct not sure why, Also want to optimize the database query. I am using mongoose for counting how many records are there with matching query params(pagination) I have to make separate query. and finding the actual records with model.find({}) have to make separate query.
But actual problem is with pagination details I am trying to get
Example in below code if I set page = 1, page_size = 10 and my row_count is 3 then I suppose to get from 1, and to 1 but instead I am getting from 1 and to 11.
Not sure what I am doing wrong here.
const pagination = async (model, query, page_number, page_size, order, order_by, next) => {
const pageS = parseInt(page_number)
let page = +pageS || 1;
const limit = parseInt(page_size)
let per_page = +page_size || 10;
if (page < 1) {
page = 1;
}
if (per_page < 1) {
per_page = 1;
}
const startIndex = (
page - 1
) * per_page;
const endIndex = page * page_size
const key = `${order}`
const results = {}
// here reading the data count from database
const resultCount = await model.countDocuments(query).exec();
if (endIndex < resultCount) {
results.next = {
page: page + 1,
page_size: limit
}
}
if (startIndex > 0) {
results.previous = {
page: page - 1,
page_size: limit
}
}
try {
// here trying to search the query with applied pagination
const data = await model.find(query)
.limit(per_page)
.skip(startIndex)
.sort({ [key] : order_by })
.exec()
// here I am passing details but not getting exact to and from; from is working expected but not to
// Example if I set page = 1, page_size = 10 and my row_count is 3 then I suppose to get from 1, and to 1 but intead I am getting from 1 and to 11.
const pagination_details = {
data: data,
meta: {
page,
page_size: per_page,
row_count: parseInt(resultCount, 10),
page_count: Math.ceil(resultCount / per_page ),
from:startIndex + 1,
to: endIndex + 1,
order: order,
order_by: order_by
}
}
return pagination_details
next()
} catch (e) {
console.log(e);
console.error(e);
}
};
Can anyone help me here to achieve the right data, what I am making mistake here. Might be something logical mistake
You have forgotten to divide the start and end indexes by the per_page to get page numbers, try replacing:
from:startIndex + 1,
to: endIndex + 1,
with:
from: Math.floor(startIndex / per_page) + 1,
to: Math.ceil(endIndex / per_page) + 1,

React For Loop - Uncaught (in promise) RangeError: Maximum call stack size exceeded

I have this for loop in React, that loops through limited number of items, and creates JSX based on that, my render function looks like this:
public render(): React.ReactElement<ITilesProps> {
let numOfColumns = 2;
let items = this.state.items;
let rowsJsx = []
let initCounter = items.length - numOfColumns;
let jumper = initCounter > 0 ? numOfColumns : items.length;
for (let i = 0; i < items.length; i += jumper) {
let rowJsx = []
let limit = (i + jumper - 1) >= items.length ? items.length - 1 : (i + jumper - 1)
for (let j = i; j <= limit; j++) {
let props = {
previewImages: [
{
url: items[j].EncodedAbsUrl,
previewImageSrc: items[j].EncodedAbsUrl,
imageFit: ImageFit.cover,
width: 318,
height: 196
}]
}
rowJsx.push(<SingleCardContainer><DocCard previewImageProps={props} ></DocCard></SingleCardContainer>)
}
rowsJsx.push(<TileRow>{rowsJsx}</TileRow>)
}
return (
<div className="ms-Grid" dir="ltr">
{rowsJsx}
</div>
)
}
What I am creating, is a row of tiles, the first loop is looping through items, and skipping the # of tiles in each row, the second loop is looping through tiles in each row, creating the JSX for it, and adding it to the array of tiles in each row, which in turn is added to the all rows JSX array.
Any idea why this might give an error?
Thanks.
It appears it was just simple typo.. Easy done.
rowsJsx.push(<TileRow>{rowsJsx}</TileRow>)
In the above you have accidentally added rowsJsx instead of rowJsx , so your accumulated rows are getting added each time.
rowsJsx.push(<TileRow>{rowJsx}</TileRow>)

Categories