0

I'm trying to build a search module with two filters. Basically, I want to have two input fields. The first input field will filter for people (tall or short), the other input field will be for things they like to do:

Here's my people.json file that is called through AJAX.

    [
        {
            "id": 1,
            "name": "Tom",
            "height": "short",
            "recreations": {
                "rec1": "football",
                "rec2": "hiking",
                "rec3": "swimming"
            }
        },
        {
            "id": 2,
            "name": "Tanya",
            "height": "tall",
            "recreations": {
                "rec1": "soccer",
                "rec2": "softball",
                "rec3": "hiking"
            }
        },
        {
            "id": 3,
            "name": "Patrick",
            "height": "short",
            "recreations": {
                "rec1": "skiing",
                "rec2": "hiking",
                "rec3": "running"
            }
        },
        {
            "id": 4,
            "name": "Michelle",
            "height": "tall",
            "recreations": {
                "rec1": "wrestling",
                "rec2": "boxing",
                "rec3": "hiking"
            }
        },
        {
            "id": 5,
            "name": "Eric",
            "height": "tall",
            "recreations": {
                "rec1": "football",
                "rec2": "tennis",
                "rec3": "swimming"
            }
        }
    ]

=========================

Here's my HTML:


    <form id="search-companions" action="#">
        <label for="s-recreation"></label>
        <input type="text" id="s-recreation" placeholder="Search recreation">
        
        <label for="s-height"></label>
        <input type="text" id="s-height" placeholder="Enter short or tall">
        
        <input type="submit" id="search-companions-btn" name="searchCompanions" value="Search">
    </form>
    <div id="match-list"></div>

================================

Here's my script:


    <script>
        //SEARCH FILTER
        const s_recreation = document.getElementById('s-recreation');
        const s_height = document.getElementById('s-height');
        const match_list = document.getElementById('match-list');
        const search_btn = document.getElementById('search-companions');
        
        // Search json file and filter
        const searchCompanions = async searchText => {
            const res = await fetch('./_inc/people.json');
            const companions = await res.json();
            const height = s_height.value;
            
            // Filter by search field from matches
            let matches = companions.filter(companion => {
                const regex = new RegExp(`^${searchText}`, 'gi');
                return companion.height.match(regex);
        
            });
            // clears DOM if input fields are empty
            if(searchText.length === 0) {
                matches = [];
                matchList.innerHTML = '';
            }
            outputHtml(matches);
        };
        
        // Show results in HTML
        const outputHtml = matches => {
            if (matches.length > 0) {
                const html = matches.map(
                    match => `
                    <div class="companion">
                        <div class="companion-info">
                            <header><h2 class="c_name">${match.name}</h2></header>
                            <p class="c_height">${match.height}</p>
                            <p class="c_recreation">${match.recreations}</p>
                        </div>
                    </div>
                    `).join();
                    
                    match_list.innerHTML = html;
            }
        }
        search_btn.addEventListener('submit', () => searchCompanions(s_recreation.value));
    </script>

I've spent two days searching, watching video tutorials and online articles and I cannot, for the life of me, figure it out. What I would like it to do is:

  1. Enter the search criteria of recreations and height
  2. filter through the criteria, recreations and height
  3. output matches that meet the recreations and height criteria

I can filter out the height because it's pretty straightforward. Where I'm having problems is getting the recreations array, filtering it to match the recreation input value and then filtering THAT through the height input value.

Any help/guidance would be greatly appreciated. Thank you.

2
  • the two filters are logical AND operations. Why not .filter(heightComparator).filter(recreationComparator) or .filter(heightComparator && recreationComparitor) where recreationComparator is rec=>recreations.some(...)? Commented Dec 24, 2021 at 15:42
  • I didn't know you could do it that way. I'll try it and thank you.
    – Lexiriam
    Commented Dec 25, 2021 at 22:45

1 Answer 1

0

I personally find pressing buttons fairly boring, especially after I spent time filling out input fields. For this reason I wrote my snippet so it reacts directly on any input changes in the two input fields:

const data=[
    {
        "id": 1,
        "name": "Tom",
        "height": "short",
        "recreations": {
            "rec1": "football",
            "rec2": "hiking",
            "rec3": "swimming"
        }
    },
    {
        "id": 2,
        "name": "Tanya",
        "height": "tall",
        "recreations": {
            "rec1": "soccer",
            "rec2": "softball",
            "rec3": "hiking"
        }
    },
    {
        "id": 3,
        "name": "Patrick",
        "height": "short",
        "recreations": {
            "rec1": "skiing",
            "rec2": "hiking",
            "rec3": "running"
        }
    },
    {
        "id": 4,
        "name": "Michelle",
        "height": "tall",
        "recreations": {
            "rec1": "wrestling",
            "rec2": "boxing",
            "rec3": "hiking"
        }
    },
    {
        "id": 5,
        "name": "Eric",
        "height": "tall",
        "recreations": {
            "rec1": "football",
            "rec2": "tennis",
            "rec3": "swimming"
        }
    }
];

const flt = "recreation,height".split(",").map(f=>document.getElementById('s-'+f)),
      lst = document.getElementById('match-list');

document.querySelector("form").addEventListener("input",ev=>{
  const [r,h]=flt.map(f=>f.value.toLowerCase());
  lst.innerHTML=data.filter(d=>Object.values(d.recreations).join("|").indexOf(r)>-1 && d.height.indexOf(h)>-1)
    .map(d=>
  
   `<div class="companion-info">
<header><h2 class="c_name">${d.name}</h2></header>
<p class="c_height">${d.height}</p>
<p class="c_recreation">${Object.values(d.recreations).join(', ')}</p>
</div>`).join("\n");
});
   
<form id="search-companions" action="#">
    <label for="s-recreation">preferred recreational activities:</label><br>
    <input type="text" id="s-recreation" placeholder="Search recreation">
    
    <label for="s-height">body height:</label><br>
    <input type="text" id="s-height" placeholder="Enter short or tall">
</form>
<div id="match-list"></div>

I left out the AJAX part but I would recommend that you load the data only once and store it, like in my example into the variable data, and then work on it repeatedly to filter out different results lists.

Not the answer you're looking for? Browse other questions tagged or ask your own question.