I have an array of AppItems. Each app item has a property that is an array of ProfileItems. I want to filter my AppItems array based on which AppItem's have a Profile who's name contains my search text. It should return True on the first profile that contains the search text.
The problem is I'm for-looping through the profile items within a foreach within the filter function, which I don't think it likes. I have no idea how to do this.
export interface AppState {
appItems: AppItem[];
}
export interface AppItem {
profiles: ProfileItem[];
...
}
export interface ProfileItem {
name: string;
...
}
appItemsFiltered(state) {
return state.appItems
.filter((item: AppItem) => {
if (!state.filters.searchQuery) return true;
item.profiles.forEach(function (profile, index) {
const name = profile.name.toLowerCase()
const text = state.filters.searchQuery?.toLowerCase();
const result = name.indexOf(text)
if (result !== -1) return true;
})
return false
};
}
If the arrays is:
const array = [
{profiles: [{name: 'name 10'}, {name: 'name 11'}]},
{profiles: [{name: 'name 20'}, {name: 'name 21'}]},
// ...
];
Do filter like:
const filterText = 'name 21';
const result = array.filter(x => x.profiles.some(x => x.name === filterText));
result
will be an array of matches.
const hasFound = result.length > 0;
When you return
in a forEach
it is the same as using continue
in a for
loop; it just moves on to the next iteration in the loop.
If you are looking for "at least one result is true
" then consider using Array.prototype.some
, which returns a boolean based on whether a single result in the array resolves as true
.
Here's my attempt at re-writing your code with that solution, though with the data provided I can't do any better than this:
appItemsFiltered(state) {
return state.appItems
.filter((item: AppItem) => {
if (!state.filters.searchQuery) return true;
return item.profiles.some(function (profile, index) {
const name = profile.name.toLowerCase()
const text = state.filters.searchQuery?.toLowerCase();
const result = name.indexOf(text)
if (result !== -1) return true;
})
};
}
I wrote a snippet that introduces two approaches:
join()
the strings & search in the resulting string some()
with find()
const AppState = { appItems: [{ profileItem: ['1.1', '1.2', '1.3'], }, { profileItem: ['2.1', '2.2', '2.3'], }, { profileItem: ['3.1', '3.2', '3.3'], } ] } // creating an HTML representation of the list const itemHtml = (item) => { return `<li>${ item }</li>` } const profileItemsHtml = (profileItems) => { return profileItems.map(item => itemHtml(item)).join('') } const createList = (appItems) => { return appItems.reduce((a, { profileItem }) => { a = [...a, ...profileItem] return a }, []) } // putting out the HTML const updateListContainer = (container, list) => { container.innerHTML = profileItemsHtml(list) } // the full list const fullList = createList(AppState.appItems) const fullListContainer = document.getElementById('full-list') updateListContainer(fullListContainer, fullList) // initiating the filtered list let filteredList1 = fullList const filteredListContainer1 = document.getElementById('filtered-list-1') updateListContainer(filteredListContainer1, filteredList1) let filteredList2 = fullList const filteredListContainer2 = document.getElementById('filtered-list-2') updateListContainer(filteredListContainer2, filteredList2) // setting up filtering on input field input event const filterInput = document.getElementById('filter-input') filterInput.addEventListener('input', function(e) { // FILTER 1: use join() // if the list is made up of only strings, then you // could join them & search the whole string at once // might yield errors in edge cases, but mostly it // should be correct, I think // edge case example: 22 (you can try it) const filtered1 = AppState.appItems.find(({ profileItem }) => profileItem.join('').includes(e.target.value)) if (filtered1 && e.target.value) { updateListContainer(filteredListContainer1, filtered1.profileItem) } else { updateListContainer(filteredListContainer1, fullList) } // FILTER 2: use SOME const filtered2 = AppState.appItems.find(({ profileItem }) => profileItem.some(item => item.includes(e.target.value))) if (filtered2 && e.target.value) { updateListContainer(filteredListContainer2, filtered2.profileItem) } else { updateListContainer(filteredListContainer2, fullList) } })
.list-container { display: grid; grid-template-columns: repeat(3, 1fr); }
<div> <label for="filter-input"> Filter: <input type="text" id="filter-input" /> </label> <hr> <div class="list-container"> <div> FULL LIST: <ul id="full-list"></ul> </div> <div> FILTERED WITH JOIN: <ul id="filtered-list-1"></ul> </div> <div> FILTERED WITH SOME: <ul id="filtered-list-2"></ul> </div> </div> </div>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.