javascripttypescriptsearchfilter

How to loop through a nested JSON object which contains objects and and arrays of objects


I am trying to recreate the search functionality of outlook. My entire data set is contained in the following interfaces:

export interface Folder{
    id: string | null;
    name: string;
    folders: Folder[] | null;
    emails: Email[]
}

export interface Email {
    id : string;
    conversation_id: string;
    from: Contact;
    to: Contact[];
    cc: Contact[];
    bcc: Contact[];
    subject: string;
    body: string;
    body_preview: string;
    has_attachments: boolean;
    file_attachments: FileAttachment[];
    received_date: Date
}

export interface Contact
{
    email_address : number;
    name : string | null;
}

export interface FileAttachment
{
    id: string;
    file_name: string;
    content_type: string;
    content_bytes: string;
}

As you can see it is a complex object with multiple layers and unknown depth as the folders can contain more folders and more emails. I have the following v-text-field which I will be using to search for strings in my nested object:

            <v-text-field
        v-model="search"
        append-inner-icon="mdi-magnify"
        label="Dynamic Search"
        density="compact"
        variant="outlined"
        clearable
        rounded
        hide-details
        single-line
        flat
        color="primaryDarken4"/> 

I am looking to see an answer which shows how to maintain this structure of the objects but filters down the emails to those containing atleast 1 matching string in one of its properties.


Solution

  • This functions will help

     function filterEmails(emailData: Email, searchTerm: string): boolean {
        const searchTermLwCase = searchTerm.toLowerCase();
        return (
            emailData.subject.toLowerCase().includes(searchTermLwCase) ||
            emailData.body.toLowerCase().includes(searchTermLwCase) ||
            emailData.body_preview.toLowerCase().includes(searchTermLwCase) ||
            emailData.from.name?.toLowerCase().includes(searchTermLwCase) ||
            emailData.to.some(contact => contact.name?.toLowerCase().includes(searchTermLwCase)) ||
            emailData.cc.some(contact => contact.name?.toLowerCase().includes(searchTermLwCase)) ||
            emailData.bcc.some(contact => contact.name?.toLowerCase().includes(searchTermLwCase))
        );
    }
    
    function searchForEmails(folder: Folder, searchTerm: string): Folder {
        // emails filtered from current folder
        const filteredEmails = folder.emails.filter(email => filterEmails(email, searchTerm));
    
        // subfolder filter recursively
        const filteredFolders = folder.folders ? folder.folders.map(subfolder => searchFolders(subfolder, searchTerm)).filter(subfolder => subfolder.emails.length > 0 || (subfolder.folders && subfolder.folders.length > 0)) : [];
    
       
        return {
            ...folder,
            emails: filteredEmails,
            folders: filteredFolders
        };
    }