How to search filter an array of object based on the dynamic value of object key on another array?
Problem Description:
So I have a problem while doing the search filtering features for searching through all columns in the Datagrid table; I’ve implemented the search based on the selected column field, which is working just fine, and there is an option for the search input to search through the All columns
.
I get all the column data dynamically from the MUI Grid API and the valueTwo
array is the representation of the column data that I got from the MUI Grid API.
const valueOne = [
{
name: 'Jacqueline',
reference: 'PRD-143',
active: true,
},
{
name: 'Jacqueline',
reference: 'PRD-143',
active: true,
},
{
name: 'Jacqueline',
reference: 'PRD-143',
active: true,
}
]
const valueTwo = [
{
id: '01',
field: 'name',
headerName: 'Name'
},
{
id: '02',
field: 'reference',
headerName: 'Reference'
},
{
id: '01',
field: 'active',
headerName: 'Status'
},
]
This is the application, image
This the hooks that I write
export function useSearchTable<T>(
tableData: T[],
setTableItems: Dispatch<SetStateAction<T[]>>,
searchState: string,
) {
const [selectedColumnHeader, setSelectedColumnHeader] = useState<
ColumnHeaderInterface | any
>({
field: `allColumns`,
headerName: `All Columns`,
});
const [filteredColumnHeaders, setFilteredColumnHeaders] = useState<
GridStateColDef[] | any
>();
const debouncedSearchState = useDebounce(searchState, 500) as string;
const requestSearch = (dataItems: T[]) => {
switch (selectedColumnHeader.field) {
case `allColumns`: {
let allColumnFilter: T[] = [];
const columnTempData = [] as any[];
if (filteredColumnHeaders) {
for (const columnHeader of filteredColumnHeaders) {
columnTempData.push(columnHeader.field);
}
}
// TODO: something is broken here
columnTempData.forEach((columnName) => {
allColumnFilter = dataItems.filter((itemData: any) => {
if (
typeof itemData[columnName] === `boolean` ||
typeof itemData[columnName] === `number`
) {
String(itemData[columnName])
.toLowerCase()
.includes(debouncedSearchState.toLowerCase());
}
if (typeof itemData[columnName] === `object`) {
itemData[columnName]?.name
.toLowerCase()
.includes(debouncedSearchState.toLowerCase());
}
return itemData;
});
});
console.log(columnTempData, `itemTempData`);
console.log(allColumnFilter, `columnFilterSearch`);
setTableItems(allColumnFilter);
break;
}
default: {
const filteredSearchData = dataItems.filter((itemData: any) => {
let data = itemData[selectedColumnHeader.field];
if (typeof data === `boolean` || typeof data === `number`) {
data = String(data);
}
// TODO: quick fix, but need to research on other API data regarding the resources: vehicles, machines, standalones
if (typeof data === `object`) {
data = data?.name;
}
console.log(
data?.toLowerCase().includes(debouncedSearchState.toLowerCase()),
`data`,
);
return data
?.toLowerCase()
.includes(debouncedSearchState.toLowerCase());
});
setTableItems(filteredSearchData);
break;
}
}
};
useEffect(() => {
if (tableData) {
requestSearch(tableData);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
debouncedSearchState,
tableData,
filteredColumnHeaders,
selectedColumnHeader,
]);
return {
setFilteredColumnHeaders,
setSelectedColumnHeader,
selectedColumnHeader,
};
}
columnTempData
is basically an array of string containing the field name from the column header data that I set in filteredColumnHeaders
. And things that needs to be fix is on the // TODO
comment
The code that I expect is to get all the filtered data based on the search through all the columns with the column data that comes from the MUI Grid API.
I’m a one-man engineer and I really need your assistance here 🙂
I’ve tried to loop through the column field data and then looping the table list data and access each key based on the column field data, something like this:
columnTempData.forEach((columnName) => {
allColumnFilter = dataItems.filter((itemData: any) => {
if (
typeof itemData[columnName] === `boolean` ||
typeof itemData[columnName] === `number`
) {
String(itemData[columnName])
.toLowerCase()
.includes(debouncedSearchState.toLowerCase());
}
if (typeof itemData[columnName] === `object`) {
itemData[columnName]?.name
.toLowerCase()
.includes(debouncedSearchState.toLowerCase());
}
return itemData;
});
});
It should have been returning the all filtered data based on the input given. so no matter what column is being search the data is going to filtered based on the input.
Solution – 1
Use map
instead of forEach
as forEach
doesn’t return anything
columnTempData.map((columnName) => {
allColumnFilter = dataItems.filter((itemData: any) => {
if (
typeof itemData[columnName] === `boolean` ||
typeof itemData[columnName] === `number`
) {
String(itemData[columnName])
.toLowerCase()
.includes(debouncedSearchState.toLowerCase());
}
if (typeof itemData[columnName] === `object`) {
itemData[columnName]?.name
.toLowerCase()
.includes(debouncedSearchState.toLowerCase());
}
return itemData;
});
return allColumnFilter
});
Solution – 2
I’ve found the solution! Basically, I was wrong in choosing the right array method for my case.
allColumnFilter = dataItems.filter((itemData: any) => {
return columnTempData.find((columnName) => {
let itemDataTemp = itemData[columnName];
if (
typeof itemDataTemp === `boolean` ||
typeof itemDataTemp === `number`
) {
itemDataTemp = String(itemDataTemp);
}
if (typeof itemDataTemp === `object`) {
itemDataTemp = itemDataTemp?.name;
}
return itemDataTemp
?.toLowerCase()
.includes(debouncedSearchState.toLowerCase());
});
});