The question is very simple : I have an array of items, that all have a date. According to the date, I would like to split this array into several arrays.
In my case, I would like to split the array into 3 arrays : one that contains result of the last 24h, the other 48h, then the last one on the week.
Here is a minimal reproduction of the case, with the final solution.
const now = Date.now();
const day = 1000 * 3600 * 24;
const todayTime = now - day;
const yesterdayTime = now - day * 2;
const weekTime = now - day * 7;
const arr = [
{ id: 1, date: todayTime + 100 },
{ id: 2, date: yesterdayTime + 100 },
{ id: 3, date: weekTime + 100 },
];
const today = arr.filter(item => item.date - todayTime > 0);
const yesterday = arr.filter(item => item.date - yesterdayTime > 0 && item.date - todayTime < 0);
const week = arr.filter(item => item.date - weekTime > 0 && item.date - yesterdayTime < 0);
console.log(today, yesterday, week);
Is there a way to make it more concise ? I'm talking about code length : I would like to reduce it to the maximum, especially the definition of the 3 arrays. I tried with reduce, but the syntax is pretty ugly.
Any ideas ?
It is not shorter, but I would go for a more generic function which groups data into chunks based on a given property ("date" in your case) and some boundary values (you have 3), provided in ascending order. Then that function would return one more value, since 3 boundaries split an infinite range into 4 ranges, ...etc.
So this is what that function would look like:
function groupInRanges(data, prop, boundaries) {
// NB: boundaries must be sorted ascending.
return data.reduce((acc, item) => {
let index = boundaries.findIndex(boundary => item[prop] < boundary);
if (index < 0) index = boundaries.length;
acc[index].push(item);
return acc;
}, [[], ...boundaries.map(() => [])]);
}
// Demo
const now = Date.now();
const day = 1000 * 3600 * 24;
const arr = [
{ id: 1, date: now - day + 100 },
{ id: 2, date: now - 2*day + 100 },
{ id: 3, date: now - 5*day },
];
// We are not interested in the first value (data older than 1 week):
const [, week, yesterday, today] = groupInRanges(arr, "date", [now-day*7, now-day*2, now-day]);
console.log(today, yesterday, week);
Note that if your data could possibly have future dates, you would need one extra boundary set to now.
More specific solution
This assumes that there is no data older than a week or future. Use a ternary operator to determine in which array a data item should be pushed:
const now = Date.now();
const day = 1000 * 3600 * 24;
const todayTime = now - day;
const yesterdayTime = now - day * 2;
const weekTime = now - day * 7;
const arr = [
{ id: 1, date: todayTime + 100 },
{ id: 2, date: yesterdayTime + 100 },
{ id: 3, date: weekTime + 100 },
];
const [today, yesterday, week] = arr.reduce((acc, item) => {
acc[item.date > todayTime ? 0 : item.date > yesterdayTime ? 1 : 2].push(item);
return acc;
}, [[], [], []]);
console.log(today, yesterday, week);
A tiny bit shorter, but less readable, with the comma operator and expression arrow-function syntax:
const [today, yesterday, week] = arr.reduce((acc, item) =>
(acc[item.date > todayTime ? 0 : item.date > yesterdayTime ? 1 : 2].push(item), acc)
, [[], [], []]);
You could move the function into an array and get the index for later pushing the item.
const
update = (array, index, value) => {
if (index !== -1) (array[index] = array[index] || []).push(value);
return array;
},
now = Date.now(),
day = 1000 * 3600 * 24,
todayTime = now - day,
yesterdayTime = now - day * 2,
weekTime = now - day * 7,
arr = [{ id: 1, date: todayTime + 100 }, { id: 2, date: yesterdayTime + 100 }, { id: 3, date: weekTime + 100 }],
fns = [
date => date - todayTime > 0,
date => date - yesterdayTime > 0 && date - todayTime < 0,
date => date - weekTime > 0 && date - yesterdayTime < 0,
],
result = arr.reduce(
(r, item) => update(r, fns.findIndex(f => f(item.date)), item),
fns.map(_ => [])
);
console.log(result);
You want to make following part more concise right?
const today = arr.filter(item => item.date - todayTime > 0);
const yesterday = arr.filter(item => item.date - yesterdayTime > 0 && item.date - todayTime < 0);
const week = arr.filter(item => item.date - weekTime > 0 && item.date - yesterdayTime < 0);
Here's what I've tried:
const [today, yesterday, week] = arr.reduce((result, item) => (index = item.date - todayTime > 0 ? 0 : item.date - yesterdayTime > 0 && item.date - todayTime < 0 ? 1 : 2, result[index].push(item), result), [[], [], []])
Final code is:
const now = Date.now();
const day = 1000 * 3600 * 24;
const todayTime = now - day;
const yesterdayTime = now - day * 2;
const weekTime = now - day * 7;
const arr = [
{ id: 1, date: todayTime + 100 },
{ id: 2, date: yesterdayTime + 100 },
{ id: 3, date: weekTime + 100 },
];
// revised
const [today, yesterday, week] = arr.reduce((result, item) => (index = item.date - todayTime > 0 ? 0 : item.date - yesterdayTime > 0 && item.date - todayTime < 0 ? 1 : 2, result[index].push(item), result), [[], [], []])
console.log(today, yesterday, week)
Here is another option using map instead of reduce like most of the other answers.
This simply works by comparing the dates, removing the item from the array if it matches a certain criteria. In the example below I duplicate the array leaving the old arr intact. However you can simply ignore this if you don't care about arr mutating, or if you'd like to see what items didn't match any criteria (since they are left behind).
The reason I need a reverseEach function is obvious. If I start deleting items from an array that I'm currently iterating over the elements start to shift. When iterating from back to front this doesn't matter since I've already handled those items.
In the example below, order matters. When you put t1w (time 1 week) as first element in the array all the elements of arr will match the criteria and will be present in that group. Make sure you provide the criteria incremental (or decremental depending on the context).
// define some helpers
Array.prototype.deleteAt = function (index) { return this.splice(index, 1)[0]; };
Array.prototype.dup = function () { return this.slice(0); };
Array.prototype.reverseEach = function (callback, thisArg) {
if (thisArg !== undefined) callback = callback.bind(thisArg);
for (let index = this.length - 1; index >= 0; index -= 1) {
callback(this[index], index, this);
}
};
// initialisation
const now = Date.now();
const day = 1000 * 3600 * 24;
const t1d = now - day;
const t2d = now - day * 2;
const t1w = now - day * 7;
const arr = [
{ id: 1, date: t1d + 100 },
{ id: 2, date: t2d + 100 },
{ id: 3, date: t1w + 100 },
];
// actual answer
const groups = [t1d, t2d, t1w].map(function (limit) {
const group = [];
this.reverseEach((item, index) => {
if (item.date > limit) group.unshift(this.deleteAt(index));
});
return group;
}, arr.dup());
console.log(groups);
Related
I'mm trying to calculate sum of serviceCost from Firestore. I managed to get all the docs but it seems that the query calculated only the first cost entered of given month.
Code:
firebase
.firestore()
.collection("users")
.doc(uid)
.collection("confirmed-appointments")
.get()
.then((querySnapshot) => {
let serviceCostTotal = 0; //Will hold currentMonth Total Income.
let monthNumber = 0;
let array = [];
querySnapshot.forEach((doc) => {
monthNumber = parseInt(doc.data().month, 10);
serviceCostTotal =
serviceCostTotal + parseInt(doc.data().serviceCost, 10); //Calculate Total Month income using this formula
array[monthNumber - 1] = serviceCostTotal; //Push the income of month X to array in X place
serviceCostTotal = 0; // after pushing, initialize the sum to 0
});
For example:
I want to calculate the total serviceCost for month 10.
Firestore looks like this:
In the loop I'm trying to take the serviceCost of each doc, and push it to array[monthNumber].
The problem is: if i have 2 docs that their value is the same "month: xx" , the loop calculate only the serviceCost value of the first doc.
Meaning that if my sum needs to be 6000, it will be only 2500.
its calculating only this:
while i have this also:
You're explicitly overwriting each element. Try adding to it instead.
array[monthNumber - 1] = serviceCostTotal
// Should be
array[monthNumber - 1] = (array[monthNumber - 1] || 0) + serviceCostTotal
Edit:
As others have said this code works
let monthNumber = 0;
let array = [];
[{ month: 1, serviceCost: 15 },
{ month: 1, serviceCost: 4 },
{ month: 1, serviceCost: 9 },
{ month: 2, serviceCost: 1 },
{ month: 3, serviceCost: 100 }].forEach((doc) => {
monthNumber = parseInt(doc.month, 10);
serviceCostTotal =
serviceCostTotal + parseInt(doc.serviceCost, 10); //Calculate Total Month income using this formula
array[monthNumber - 1] = (array[monthNumber - 1] || 0) + serviceCostTotal; //Push the income of month X to array in X place
serviceCostTotal = 0; // after pushing, initialize the sum to 0
});
console.log(array);
You have to show us how you are validating this and what the actual input and outputs are.
As you said you already have the data in the array index so you can do like this
const querySnapshot = [{
month: 10,
serviceCost: 2500
}, {
month: 10,
serviceCost: 3500
}, {
month: 11,
serviceCost: 1000
}];
const array = [];
querySnapshot.forEach((doc) => {
const monthNumber = parseInt(doc.month, 10);
array[monthNumber - 1] = (array[monthNumber - 1] || 0) + parseInt(doc.serviceCost, 10);
});
console.log(array);
You basically check if whether there is something in that index of that month and add it or use the serviceCostTotal that you calculated
If the count of number 1 is repeated more than or equal to 12: replace all elements with 2 after last index of number 1.
Original array contains 0,1,-1;
I tried using below it works, if there is any simpler solution please suggest and help with link to documentation for further reference.
var arr = [0, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 0, 1, 1, -1, 0, 1, 0];
var total = arr.reduce((t, i) => i == 1 ? t + i : t, 0);
if (total >= 12) {
var startingIndex = arr.lastIndexOf(1) + 1;
var arr = arr.map((e, i) => i >= startingIndex ? 2 : e);
}
console.log(arr);
If array is [0,1,1,1,1,-1,1,1,1,1,1,0,1,1,-1,0,1,0]
then resulting array should be [0,1,1,1,1,-1,1,1,1,1,1,0,1,1,-1,0,1, 2]
If given array is [-1,1,1,1,1,-1,1,1,1,1,1,1,1,1,-1,0,-1,-1]
then resulting array should be[-1,1,1,1,1,-1,1,1,1,1,1,1,1,1,2,2,2,2]
If given array is [1,1,1,1,1,-1,1,1,1,1,1,0,1,1,1,0,-1,0]
then resulting array should be [1,1,1,1,1,-1,1,1,1,1,1,0,1,1,1,2,2,2]
Use filter to find how many, use fill to fill index starting from last found 1
const oneFill = arr =>
arr.filter(x=>x===1).length >= 12 ? arr.fill(2,arr.lastIndexOf(1)+1) : arr
const array = [
[0,1,1,1,1,-1,1,1,1,1,1,0,1,1,-1,0,1,0],
[-1,1,1,1,1,-1,1,1,1,1,1,1,1,1,-1,0,-1,-1]
]
for(const arr of array)
console.log(JSON.stringify(
oneFill(arr)
))
optimized version using .some, breaks out immediately after finding 12th element
// fills from the 1 found at the 12th instance of 1
const oneFill = arr => {
let count = 0
// if equals 1 increment and check if counter reached 12, fill with 2s
arr.some((x,i)=>x===1 && ++count >= 12 && arr.fill(2,i+1))
return arr
}
const array = [
[0,1,1,1,1,-1,1,1,1,1,1,0,1,1,-1,0,1,0],
[-1,1,1,1,1,-1,1,1,1,1,1,1,1,1,-1,0,-1,-1]
]
for(const arr of array)
console.log(JSON.stringify(
oneFill(arr)
))
// I don't know if you want lastIndex, or just the 12th index
// Below is a version that finds the lastIndex
const oneFill2 = arr => {
let count = 0
arr.some((x,i)=>x===1 && ++count >= 12) && arr.fill(2,arr.lastIndexOf(1)+1)
return arr
}
for(const arr of array)
console.log(JSON.stringify(
oneFill2(arr)
))
This is an alternate approach for #user120242 answer. This is more imperative code but uses less amount of iterations.
Idea:
Create 2 variables:
count: to keep count of valid cases.
index_12: to hold index of 12th valid case.
Now loop over the passed array and return value as:
If count is less than 12 the no processing is required. Return same value/
If count is 12, check if current index is same as index_12. If yes, this is the 12th valid case and return item instead.
If count is greater than 12 or if its equal to 12 but index is not same as index_12, return processed value (in this case 2)
function doSomething(arr) {
let count = 0;
let index_12 = null;
return arr.map((item, index) => {
count = item === 1 ? count + 1 : count;
index_12 = !index_12 && count === 12 ? index : index_12;
return index === index_12 || count < 12 ? item : 2
})
}
console.log( doSomething([0,1,1,1,1,-1,1,1,1,1,1,0,1,1,-1,0,1,0]).join() );
console.log( doSomething([-1,1,1,1,1,-1,1,1,1,1,1,1,1,1,-1,0,-1,-1]).join() );
This is the question:
Given a positive integer num, return the sum of all odd Fibonacci numbers that are less than or equal to num.
The first two numbers in the Fibonacci sequence are 1 and 1. Every additional number in the sequence is the sum of the two previous numbers. The first six numbers of the Fibonacci sequence are 1, 1, 2, 3, 5 and 8.
For example, sumFibs(10) should return 10 because all odd Fibonacci numbers less than or equal to 10 are 1, 1, 3, and 5.
This is what I tried
function sumFibs(num, total = [1, 1], n = (total.length - 1 + total.length - 2)) {
if(n == num){
return total;
}
total.push(n);
sumFibs(num, n = (total.length - 1 + total.length - 2), total);
};
Question
Is it possible to use my method to make this work, if so how do I fix the syntax? If not, how would you solve the problem.
Many thanks!
continuation passing style
Continuation passing style effectively gives you programmatic return. Using a CPS function recursively can make program complexity evaporate into thin air -
const identity = x =>
x
const sumfib = (n = 0, then = identity) =>
n <= 0
? then(0, 1, 1) // base case
: sumfib // inductive: solve smaller subproblem
( n - 1
, (sum, fib, temp) =>
then(sum + fib, temp, fib + temp)
)
console.log
( sumfib(0) // 0 = 0
, sumfib(1) // 1 = 0 + 1
, sumfib(2) // 2 = 0 + 1 + 1
, sumfib(3) // 4 = 0 + 1 + 1 + 2
, sumfib(4) // 7 = 0 + 1 + 1 + 2 + 3
, sumfib(5) // 12 = 0 + 1 + 1 + 2 + 3 + 5
, sumfib(6) // 20 = 0 + 1 + 1 + 2 + 3 + 5 + 8
, sumfib(7) // 33 = 0 + 1 + 1 + 2 + 3 + 5 + 8 + 13
)
loop/recur
loop and recur give us the ability to write recursive programs like the one above, but will not encounter a stack overflow error -
const recur = (...values) =>
({ recur, values })
const loop = f =>
{ let r = f()
while (r && r.recur === recur)
r = f(...r.values)
return r
}
const sumfib = (n = 0) =>
loop // <-- loop with vars
( ( m = n
, sum = 0
, fib = 1
, temp = 1
) =>
m <= 0 // <-- exit condition
? sum // <-- base case
: recur // <-- recur with updated vars
( m - 1
, sum + fib
, temp
, temp + fib
)
)
console.log
( sumfib(0) // 0 = 0
, sumfib(1) // 1 = 0 + 1
, sumfib(2) // 2 = 0 + 1 + 1
, sumfib(3) // 4 = 0 + 1 + 1 + 2
, sumfib(4) // 7 = 0 + 1 + 1 + 2 + 3
, sumfib(5) // 12 = 0 + 1 + 1 + 2 + 3 + 5
, sumfib(6) // 20 = 0 + 1 + 1 + 2 + 3 + 5 + 8
, sumfib(7) // 33 = 0 + 1 + 1 + 2 + 3 + 5 + 8 + 13
)
streamz
so-called streams are interesting because they can possibly generate infinite values, but we don't have to compute them all at once. Again we can define our program in simple terms and let useful primitives do all of the hard work -
const fibs =
stream(0, _ =>
stream(1, _ =>
streamAdd(fibs, fibs.next)))
console.log(streamTake(fibs, 10))
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ]
console.log(streamTake(streamSum(fibs), 10))
// [ 0, 1, 2, 4, 7, 12, 20, 33, 54, 88 ]
We just implement stream, streamAdd, streamSum, and streamTake -
const emptyStream =
Symbol('emptyStream')
const stream = (value, next) =>
( { value
, get next ()
{ delete this.next
return this.next = next()
}
}
)
const streamAdd = (s1, s2) =>
s1 === emptyStream || s2 === emptyStream
? emptyStream
: stream
( s1.value + s2.value
, _ => streamAdd(s1.next, s2.next)
)
const streamSum = (s, sum = 0) =>
s === emptyStream
? emptyStream
: stream
( sum + s.value
, _ => streamSum(s.next, sum + s.value)
)
const streamTake = (s = emptyStream, n = 0) =>
s === emptyStream || n <= 0
? []
: [ s.value, ...streamTake(s.next, n - 1) ]
Expand the snippet below to verify the results in your own browser -
const emptyStream =
Symbol('emptyStream')
const stream = (value, next) =>
( { value
, get next ()
{ delete this.next
return this.next = next()
}
}
)
const streamAdd = (s1, s2) =>
s1 === emptyStream || s2 === emptyStream
? emptyStream
: stream
( s1.value + s2.value
, _ => streamAdd(s1.next, s2.next)
)
const streamSum = (s, sum = 0) =>
s === emptyStream
? emptyStream
: stream
( sum + s.value
, _ => streamSum(s.next, sum + s.value)
)
const streamTake = (s = emptyStream, n = 0) =>
s === emptyStream || n <= 0
? []
: [ s.value, ...streamTake(s.next, n - 1) ]
const fibs =
stream(0, _ =>
stream(1, _ =>
streamAdd(fibs, fibs.next)))
console.log(streamTake(fibs, 10))
// [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ]
console.log(streamTake(streamSum(fibs), 10))
// [ 0, 1, 2, 4, 7, 12, 20, 33, 54, 88 ]
Four things
(1) You don't return the result of the recursive call, therefore it does never get passed up to the caller:
sumFibs(4, [1, 1]) -> sumFibs(4, [1, 1, 2]) -> sumFibs(4, [1, 1, 2, 3])
<- [1, 1, 2, 3]
// v the return you do
// v the return you need too
(2) In the recursive call, the order of arguments is wrong.
(3) I guess instead of taking the arrays length minus 1, you want to access the property at that position in the total array.
(4) Why do you actually n as an argument? As it is only depending on total, it could also just be a variable:
function sumFibs(num, total = [1, 1]) {
const n = total[total.length - 1] + total[total.length - 2];
if(n > num){
return total;
}
total.push(n);
return sumFibs(num, total);
}
console.log(sumFibs(19));
This can be solved without an array accumulator; use n as a counter and curr and prev vars to store the data necessary to compute the Fibonacci series. Whenever we have an odd curr, add it to the running total and pass it up the call stack.
const sumOddFibs = (n, curr=1, prev=0) => {
if (curr < n) {
return sumOddFibs(n, curr + prev, curr) + (curr % 2 ? curr : 0);
}
return 0;
};
console.log(sumOddFibs(10));
As an aside, recursion is a pretty poor tool for just about anything that involves a sequential 0..n counter. Iteration makes more sense: less overhead, easier to understand and no risk of blowing the call stack. I'd also separate computation of the Fibonacci series (which is a good use case for a generator) from filtering oddness and summing so that each step is independent and can be reused:
const sum = arr => arr.reduce((a, e) => a + e);
const odds = arr => arr.filter(e => e % 2);
function *fibsBelow(n) {
for (let prev = 0, curr = 1; curr < n;) {
yield curr;
const tmp = curr;
curr += prev;
prev = tmp;
}
}
console.log(sum(odds([...fibsBelow(10)])));
I am trying to write a Javascript function that takes an array, page_size and page_number as parameters and returns an array that mimics paginated results:
paginate: function (array, page_size, page_number) {
return result;
}
so for example when:
array = [1, 2, 3, 4, 5],
page size = 2,
page_number = 2,
the function should return: [3, 4].
Any ideas would be appreciated.
You can use Array.prototype.slice and just supply the params for (start, end).
function paginate(array, page_size, page_number) {
// human-readable page numbers usually start with 1, so we reduce 1 in the first argument
return array.slice((page_number - 1) * page_size, page_number * page_size);
}
console.log(paginate([1, 2, 3, 4, 5, 6], 2, 2));
console.log(paginate([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 4, 1));
Here's a solution with reduce():
function paginate (arr, size) {
return arr.reduce((acc, val, i) => {
let idx = Math.floor(i / size)
let page = acc[idx] || (acc[idx] = [])
page.push(val)
return acc
}, [])
}
let array = [1, 2, 3, 4, 5]
let page_size = 2
let pages = paginate(array, page_size)
console.log(pages) // all pages
console.log(pages[1]) // second page
It returns an array of pages so you can get a certain page, or loop through all of them.
I saw an example above that did this correctly (kind of) and wanted to expand on it.
This was the example.
function paginate(array, page_size, page_number) {
// human-readable page numbers usually start with 1, so we reduce 1 in the first argument
return array.slice((page_number - 1) * page_size, page_number * page_size);
}
There are a few things wrong with this.
1.) If the page_number is 0 then it will try and set the starting split at -1 * page_size which returns an empty array. So the minimum value of the page_number attr should be 1, never anything less unless you handle that case in the function.
2.) The starting and ending index of the split are the same. Because of this, you get back an empty array. So the split should be:
return array.split(page_number * page_size, page_number * page_size + page_size)
const myArray = [1,2,3,4,5,6,7,8,9,10];
const paginateBad1 = (array, page_size, page_number) => {
return array.slice((page_number - 1) * page_size, page_number * page_size);
};
const paginateBad2 = (array, page_size, page_number) => {
return array.slice(page_number * page_size, page_number * page_size);
};
const paginateGood = (array, page_size, page_number) => {
return array.slice(page_number * page_size, page_number * page_size + page_size);
};
console.log("Bad 1", paginateBad1(myArray, 2, 0));
console.log("Bad 2", paginateBad2(myArray, 2, 1));
console.log("Good", paginateGood(myArray, 2, 1));
Another aproach that you can utilize, is using .filter, look:
const paginate = function (array, index, size) {
// transform values
index = Math.abs(parseInt(index));
index = index > 0 ? index - 1 : index;
size = parseInt(size);
size = size < 1 ? 1 : size;
// filter
return [...(array.filter((value, n) => {
return (n >= (index * size)) && (n < ((index+1) * size))
}))]
}
var array = [
{id: "1"}, {id: "2"}, {id: "3"}, {id: "4"}, {id: "5"}, {id: "6"}, {id: "7"}, {id: "8"}, {id: "9"}, {id: "10"}
]
var transform = paginate(array, 2, 5);
console.log(transform) // [{"id":"6"},{"id":"7"},{"id":"8"},{"id":"9"},{"id":"10"}]
You can use Array.filter() with the help of its second parameter (the index of the current element being processed in the array).
You'll also need the currently selected page and the number of items per page to display, so you can find the minimum and maximum index of the elements needed.
const indexMin = selectedPage * elementsPerPage;
const indexMax = indexMin + elementsPerPage;
const paginatedArray = arrayToPaginate.filter(
(x, index) => index >= indexMin && index < indexMax
);
Updating the selectedPage and/or the elementsPerPage value will allow to return the correct items to display.
The use of Array#slice is the expected answer.
Here I use Symbol.iterator to create an iterable.
const arr = [1,2,3,4,5,6,7,8,9,10]
function page({arr, pageSize, pageNumber}) {
const start = pageSize*(pageNumber-1)
const end = pageSize*pageNumber
return {
*[Symbol.iterator]() {
for(let i = start; i < arr.length && i < end; i++) {
yield arr[i]
}
}
}
}
console.log([...page({arr, pageSize: 5, pageNumber: 2})])
Hey I'm sorry I'm a bit late but we can use the Array.splice(start, end) method except this is much simpler
const page = 2
const step = 2;
const start = page * step - step;
const end = start + step;
const array = [1,2,3,4,5,6]
console.log(array.splice(start, end))
Here is another variation using Array.from with Array.slice
const paginate = (array, n) => {
const pageSize = Math.ceil(array.length / n);
return Array.from({ length: pageSize }, (_, index) => {
const start = index * n;
return array.slice(start, start + n);
});
};
function paginate(array, page_size, page_number) {
// human-readable page numbers usually start with 1, so we reduce 1 in the first argument
return array.slice((page_number - 1) * page_size, page_number * page_size);
}
var arr = [1, 2, 3, 4, 5, 6]
const options = {
//page: parseInt(req.query.page) || 1,
page:1,
limit:10
//limit: parseInt(req.query.limit) || 10,
//customLabels: servCustomLabels,
};
let prev_page = 0;
let next_page = 0;
let h_p_p = null;
let h_n_p = null;
let page_count = Math.ceil((arr.length / options.limit));
if (options.page >= page_count ){ // 2 3
next_page = 0;
}
if(options.page >= 1 && options.page < page_count ){
next_page = options.page + 1;
h_n_p = true;
}else{
next_page = 0;
h_n_p = false;
}
if(options.page <= 1 ){
prev_page =0;
h_p_p = false;
}else{
prev_page = options.page -1 ;
h_p_p = true;
}
console.log(paginate(arr, 2, 2));
console.log({paginator: {
totalDocs: arr.length,
perPage: options.limit,
pageCount: page_count,
currentPage: options.page,
//slNo: 2,
hasPrevPage: h_p_p,
hasNextPage: h_n_p,
prev: prev_page,
next: next_page
}})
function paginate(arr, PerPage) {
let map = {};
let startPage = 1;
arr.forEach((current) => {
if (map[startPage] && map[startPage].length < PerPage) {
map[startPage].push(current);
}
if (!map[startPage]) {
map[startPage] = [current];
}
if (map[startPage] && map[startPage].length >= PerPage) {
startPage++;
}
});
return map;
}
you will find an example on this link
The example below is using iter-ops library (I'm the author).
// our inputs...
const array = [1, 2, 3, 4, 5];
const pageSize = 2;
const pageIndex = 1;
The most efficient way is to process an array as an iterable, so you go through it once.
If you never need other pages, then the fastest way is like this:
import {pipe, skip, page} from 'iter-ops';
const p = pipe(
array,
skip(pageSize * pageIndex), // skip pages we don't want
page(pageSize) // create the next page
).first;
console.log(p); //=> [3, 4]
And if you do need other pages, then you can do:
const p = pipe(
array,
page(pageSize), // get all pages
skip(pageIndex) // skip pages we don't want
).first;
console.log(p); //=> [3, 4]
And in case you need to do further processing:
const i = pipe(
array,
page(pageSize), // get all pages
skip(pageIndex), // skip pages we don't want
take(1), // take just one page
// and so on, you can process it further
);
console.log([...i]); //=> [[3, 4]]
A simple solution using filter:
function paginate(array, pageIndex, pageSize) {
const first = pageIndex * pageSize
const last = (pageIndex * pageSize) + pageSize
return array.filter((_, index) => {
return first <= index && index < last
})
}
for (let pageNum = 1; pageNum <= totalPagesCount; pageNum++){
....
const chunk = articles.slice(
(pageNum - 1) * pageSizeNumbered,
pageNum * pageSizeNumbered,
);
.....
}
I'd go with something like this;
const paginateArray = (array, pageNumber, pageSize) => {
const page = array.slice((pageNumber - 1) * pageSize, pageNumber * pageSize);
return page;
};
const array = [1, 2, 3, 4, 5];
const pageSize = 2;
const pageNumber = 2;
console.log(paginateArray(array, pageNumber, pageSize));
I am trying to write a Javascript function that takes an array, page_size and page_number as parameters and returns an array that mimics paginated results:
paginate: function (array, page_size, page_number) {
return result;
}
so for example when:
array = [1, 2, 3, 4, 5],
page size = 2,
page_number = 2,
the function should return: [3, 4].
Any ideas would be appreciated.
You can use Array.prototype.slice and just supply the params for (start, end).
function paginate(array, page_size, page_number) {
// human-readable page numbers usually start with 1, so we reduce 1 in the first argument
return array.slice((page_number - 1) * page_size, page_number * page_size);
}
console.log(paginate([1, 2, 3, 4, 5, 6], 2, 2));
console.log(paginate([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 4, 1));
Here's a solution with reduce():
function paginate (arr, size) {
return arr.reduce((acc, val, i) => {
let idx = Math.floor(i / size)
let page = acc[idx] || (acc[idx] = [])
page.push(val)
return acc
}, [])
}
let array = [1, 2, 3, 4, 5]
let page_size = 2
let pages = paginate(array, page_size)
console.log(pages) // all pages
console.log(pages[1]) // second page
It returns an array of pages so you can get a certain page, or loop through all of them.
I saw an example above that did this correctly (kind of) and wanted to expand on it.
This was the example.
function paginate(array, page_size, page_number) {
// human-readable page numbers usually start with 1, so we reduce 1 in the first argument
return array.slice((page_number - 1) * page_size, page_number * page_size);
}
There are a few things wrong with this.
1.) If the page_number is 0 then it will try and set the starting split at -1 * page_size which returns an empty array. So the minimum value of the page_number attr should be 1, never anything less unless you handle that case in the function.
2.) The starting and ending index of the split are the same. Because of this, you get back an empty array. So the split should be:
return array.split(page_number * page_size, page_number * page_size + page_size)
const myArray = [1,2,3,4,5,6,7,8,9,10];
const paginateBad1 = (array, page_size, page_number) => {
return array.slice((page_number - 1) * page_size, page_number * page_size);
};
const paginateBad2 = (array, page_size, page_number) => {
return array.slice(page_number * page_size, page_number * page_size);
};
const paginateGood = (array, page_size, page_number) => {
return array.slice(page_number * page_size, page_number * page_size + page_size);
};
console.log("Bad 1", paginateBad1(myArray, 2, 0));
console.log("Bad 2", paginateBad2(myArray, 2, 1));
console.log("Good", paginateGood(myArray, 2, 1));
Another aproach that you can utilize, is using .filter, look:
const paginate = function (array, index, size) {
// transform values
index = Math.abs(parseInt(index));
index = index > 0 ? index - 1 : index;
size = parseInt(size);
size = size < 1 ? 1 : size;
// filter
return [...(array.filter((value, n) => {
return (n >= (index * size)) && (n < ((index+1) * size))
}))]
}
var array = [
{id: "1"}, {id: "2"}, {id: "3"}, {id: "4"}, {id: "5"}, {id: "6"}, {id: "7"}, {id: "8"}, {id: "9"}, {id: "10"}
]
var transform = paginate(array, 2, 5);
console.log(transform) // [{"id":"6"},{"id":"7"},{"id":"8"},{"id":"9"},{"id":"10"}]
You can use Array.filter() with the help of its second parameter (the index of the current element being processed in the array).
You'll also need the currently selected page and the number of items per page to display, so you can find the minimum and maximum index of the elements needed.
const indexMin = selectedPage * elementsPerPage;
const indexMax = indexMin + elementsPerPage;
const paginatedArray = arrayToPaginate.filter(
(x, index) => index >= indexMin && index < indexMax
);
Updating the selectedPage and/or the elementsPerPage value will allow to return the correct items to display.
The use of Array#slice is the expected answer.
Here I use Symbol.iterator to create an iterable.
const arr = [1,2,3,4,5,6,7,8,9,10]
function page({arr, pageSize, pageNumber}) {
const start = pageSize*(pageNumber-1)
const end = pageSize*pageNumber
return {
*[Symbol.iterator]() {
for(let i = start; i < arr.length && i < end; i++) {
yield arr[i]
}
}
}
}
console.log([...page({arr, pageSize: 5, pageNumber: 2})])
Hey I'm sorry I'm a bit late but we can use the Array.splice(start, end) method except this is much simpler
const page = 2
const step = 2;
const start = page * step - step;
const end = start + step;
const array = [1,2,3,4,5,6]
console.log(array.splice(start, end))
Here is another variation using Array.from with Array.slice
const paginate = (array, n) => {
const pageSize = Math.ceil(array.length / n);
return Array.from({ length: pageSize }, (_, index) => {
const start = index * n;
return array.slice(start, start + n);
});
};
function paginate(array, page_size, page_number) {
// human-readable page numbers usually start with 1, so we reduce 1 in the first argument
return array.slice((page_number - 1) * page_size, page_number * page_size);
}
var arr = [1, 2, 3, 4, 5, 6]
const options = {
//page: parseInt(req.query.page) || 1,
page:1,
limit:10
//limit: parseInt(req.query.limit) || 10,
//customLabels: servCustomLabels,
};
let prev_page = 0;
let next_page = 0;
let h_p_p = null;
let h_n_p = null;
let page_count = Math.ceil((arr.length / options.limit));
if (options.page >= page_count ){ // 2 3
next_page = 0;
}
if(options.page >= 1 && options.page < page_count ){
next_page = options.page + 1;
h_n_p = true;
}else{
next_page = 0;
h_n_p = false;
}
if(options.page <= 1 ){
prev_page =0;
h_p_p = false;
}else{
prev_page = options.page -1 ;
h_p_p = true;
}
console.log(paginate(arr, 2, 2));
console.log({paginator: {
totalDocs: arr.length,
perPage: options.limit,
pageCount: page_count,
currentPage: options.page,
//slNo: 2,
hasPrevPage: h_p_p,
hasNextPage: h_n_p,
prev: prev_page,
next: next_page
}})
function paginate(arr, PerPage) {
let map = {};
let startPage = 1;
arr.forEach((current) => {
if (map[startPage] && map[startPage].length < PerPage) {
map[startPage].push(current);
}
if (!map[startPage]) {
map[startPage] = [current];
}
if (map[startPage] && map[startPage].length >= PerPage) {
startPage++;
}
});
return map;
}
you will find an example on this link
The example below is using iter-ops library (I'm the author).
// our inputs...
const array = [1, 2, 3, 4, 5];
const pageSize = 2;
const pageIndex = 1;
The most efficient way is to process an array as an iterable, so you go through it once.
If you never need other pages, then the fastest way is like this:
import {pipe, skip, page} from 'iter-ops';
const p = pipe(
array,
skip(pageSize * pageIndex), // skip pages we don't want
page(pageSize) // create the next page
).first;
console.log(p); //=> [3, 4]
And if you do need other pages, then you can do:
const p = pipe(
array,
page(pageSize), // get all pages
skip(pageIndex) // skip pages we don't want
).first;
console.log(p); //=> [3, 4]
And in case you need to do further processing:
const i = pipe(
array,
page(pageSize), // get all pages
skip(pageIndex), // skip pages we don't want
take(1), // take just one page
// and so on, you can process it further
);
console.log([...i]); //=> [[3, 4]]
A simple solution using filter:
function paginate(array, pageIndex, pageSize) {
const first = pageIndex * pageSize
const last = (pageIndex * pageSize) + pageSize
return array.filter((_, index) => {
return first <= index && index < last
})
}
for (let pageNum = 1; pageNum <= totalPagesCount; pageNum++){
....
const chunk = articles.slice(
(pageNum - 1) * pageSizeNumbered,
pageNum * pageSizeNumbered,
);
.....
}
I'd go with something like this;
const paginateArray = (array, pageNumber, pageSize) => {
const page = array.slice((pageNumber - 1) * pageSize, pageNumber * pageSize);
return page;
};
const array = [1, 2, 3, 4, 5];
const pageSize = 2;
const pageNumber = 2;
console.log(paginateArray(array, pageNumber, pageSize));