Hi there I'm looking if there's a better way to render my todos
I have this
{
todos.map((todo) => (
todo.status === 1 && (
<p>{todo.title}</p>
)
))
}
{
todos.map((todo) => (
todo.status === 2 && (
<p>{todo.title}</p>
)
))
}
{
todos.map((todo) => (
todo.status === 3 && (
<p>{todo.title}</p>
)
))
}
Is there a way to do this?
Thanks a lot
CodePudding user response:
If you want them to be in order, then sort them before you map them
todos.sort((a, b) => a.status - b.status).map((todo) => .....)
if you do not want to change the order of the original array then copy it
todos.slice().sort((a, b) => a.status - b.status).map((todo) => .....)
CodePudding user response:
I'm assuming they really are all right next to each other like that. If so:, either:
Sort
todos
before renderingUse an outer loop of status values
Sort
Sort the todos
array, ideally just the once prior to rendering/re-rendering.
todos.sort((a, b) => a.status - b.status);
Note that this assumes there are only those three status
values. If there are others, you may want to have a separate filtered and sorted array that you rebuild when todos
changes.
Outer loop
{[1, 2, 3].map(
status => todos.map(
todo => todo.status === status && <p>{todo.title}</p>
)
)}
Side note: Those p
elements need keys.
CodePudding user response:
You can filter those todos first then render later
todos.filter(todo => todo.status === 1).map(todo => (
<p>{todo.title}</p>
))
todos.filter(todo => todo.status === 2).map(todo => (
<p>{todo.title}</p>
))
todos.filter(todo => todo.status === 3).map(todo => (
<p>{todo.title}</p>
))
or create a reuseable function to render those todos
function renderTodoWithStatus(todos, status) {
return todos.filter(todo => todo.status === status).map(todo => (
<p>{todo.title}</p>
))
}
CodePudding user response:
Making no assumptions about the status, you could do something like this:
const getTodos = (status) => todos
.filter((todo) => todo.status === status)
.map((todo) => <p>{todo.title}</p>);
...
{getTodos(1)}
{getTodos(2)}
{getTodos(3)}
CodePudding user response:
Another possibility (assuming you want to e.g. put <h2>
headings between the different types of todos) would be to use filter
:
// get all todos with status 1
todos.filter(todo => todo.status === 1).map(todo => (<p key={todo.id}>{todo.title}</p>))
If you want to show them as a continuous array, the sort approach is better.
EDIT: Since @epascarello insisted that more loops were bad, I wrote a quick little benchmark (takes a while before the console output starts showing up, simplified not to use React)
const REPEAT_TESTS = 1000;
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
for (let elements of [1, 10, 100, 1000, 100000]) {
console.warn(`trying all three approaches with ${elements} todo items`);
const array = [];
for (let i = 0; i < elements; i ) {
array.push({
title: "item " i,
status: getRandomInt(3) 1
})
}
// map only
let start = performance.now();
for (let i = 0; i < REPEAT_TESTS; i ) {
const result1 = array.map(elem => (elem.status === 1 && elem.title));
const result2 = array.map(elem => (elem.status === 2 && elem.title));
const result3 = array.map(elem => (elem.status === 3 && elem.title));
}
let elapsed = performance.now() - start;
console.log(`map only took ${elapsed} ms (${elapsed/REPEAT_TESTS}ms per iteration)`);
// .filter.map()
start = performance.now();
for (let i = 0; i < REPEAT_TESTS; i ) {
const result1 = array.filter(elem => elem.status === 1).map(elem => elem.title);
const result2 = array.filter(elem => elem.status === 2).map(elem => elem.title);
const result3 = array.filter(elem => elem.status === 3).map(elem => elem.title);
}
elapsed = performance.now() - start;
console.log(`.filter().map() took ${elapsed} ms (${elapsed/REPEAT_TESTS}ms per iteration)`);
// test 3
start = performance.now();
for (let i = 0; i < REPEAT_TESTS; i ) {
const sorted = array.slice().sort((a, b) => a.status - b.status).map(elem => elem.title);
}
elapsed = performance.now() - start;
console.log(`Test 3 took ${elapsed} ms (${elapsed/REPEAT_TESTS}ms per iteration)`);
}
This clearly shows that for todos.length >= 100
- OPs solution is fastest (because yes, the filter adds some loops. But OPS solution also returns
false
for all the non matching elements which is completely unusable in data processing and might incur some performance penalty in React's handling of the values) .filter.map
is slightly slower but more readable and does not have the problem of returningfalse
valuessort
is slow for more than a couple of elements and gets slower quickly. I tried this example in Nodejs with 10.000.000 elements and these were the results:
map only took 42667.75609499961 ms (42.66775609499961ms per iteration)
.filter.map took 56544.36275900155 ms (56.54436275900155ms per iteration)
sort took 107536.24900600314 ms (107.53624900600315ms per iteration)
In conclusion I would go