Reputation: 137
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
Upvotes: 2
Views: 330
Reputation: 4207
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)`);
}
todos.length >= 100
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 returning false
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 always go with .filter.map
chained. The benchmark also assumes the cheapest possible map
function (just returning the name), for a more expensive map
function the difference will be even smaller.
Upvotes: 0
Reputation: 638
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)}
Upvotes: 0
Reputation: 1
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>
))
}
Upvotes: 0
Reputation: 1074295
I'm assuming they really are all right next to each other like that. If so:, either:
Sort todos
before rendering
Use an outer loop of status values
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.
{[1, 2, 3].map(
status => todos.map(
todo => todo.status === status && <p>{todo.title}</p>
)
)}
Side note: Those p
elements need keys.
Upvotes: 3
Reputation: 207511
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) => .....)
Upvotes: 4