Source data and visible data answer different questions
state.items answers, "What resources did we load?" A filtered array answers, "Which resources match the current criteria?"
Suppose a library owns 100 books and a visitor searches for "JavaScript". Showing 8 matching books does not mean the library should throw away the other 92. The full collection and the currently visible result serve different purposes.
function getVisibleItems() {
const normalizedSearch = state.search.trim().toLowerCase();
return state.items.filter((item) => {
const matchesSearch = item.title.toLowerCase().includes(normalizedSearch);
const matchesTopic = state.topic === 'all' || item.topic === state.topic;
const matchesAvailability = !state.availableOnly || item.available;
return matchesSearch && matchesTopic && matchesAvailability;
});
}
Then rendering uses the derived result:
function render() {
const visibleItems = getVisibleItems();
renderResources(visibleItems);
renderCount(visibleItems.length, state.items.length);
}
Why filter does not destroy the original array
filter() creates a new array containing references to the matching elements. It does not remove nonmatching elements from the input array.
const available = state.items.filter((item) => item.available);
console.log(available.length); // perhaps 2
console.log(state.items.length); // still 3
The objects inside both arrays can still be shared. Changing available[0].title would also affect the same object in state.items. A new array is not a deep copy of every object.
For now, remember only the first fact: filter() gives you another array and leaves the source array length unchanged. The note about shared objects matters only if you later modify an object.
The destructive bug
state.items = state.items.filter((item) => item.topic === state.topic);
After choosing frontend, resources from other topics disappear from the source state. Choosing all cannot restore them without loading again. The application replaced its complete collection with one derived view.
Keep criteria in state and calculate visible items when rendering. This makes every filter reversible.
Trace two interactions
Start with three items. Set state.topic to frontend, render, then set it back to all and render again. Record both state.items.length and visibleItems.length after each action.
Do not continue until returning to all shows every original item again.
What to remember
Preserve loaded data as the source collection. Search and filters create derived results. Render the derived result without replacing the source that future interactions still need.