Creating a search function

Hi there I am working on the marvel Api, i have managed to extract data but now i want to create a search function whereby only those characters from the api are returned that include my search term, i have been playing around with it for a bit but not been successful yet, any ideas please help.

import './style.css'

import javascriptLogo from './javascript.svg'

import viteLogo from '/vite.svg'

import { setupCounter } from './counter.js'

import md5 from 'md5'

const publicKey = 'd73f66f08c7d56dde7aaf6ded4a26718';

const privateKey = '9d331bbe217eebffed1c66f8e3fe0a6818845b87';

let timestamp = new Date().getTime()

let hashVal = md5(timestamp+privateKey+publicKey);

const url = `http://gateway.marvel.com/v1/public/characters?ts=${timestamp}&apikey=${publicKey}&hash=${hashVal}&limit=100`;

const button = document.getElementById('submit-search');

const searchInput = document.getElementById('search-input')

let characters = []
button.addEventListener('click', ()=>{
   fetch(url).then(response=> response.json().then(data=>{
    let characters = data.data.results;
    console.log(characters)
     searchQuery()
    //  displayData()
   }))

})

function searchQuery(){
  const searchTerm =  searchInput.value.trim().toLowerCase()
   const filteredResult = characters.filter((character)=>{
    const characterName  = character.name.toLowerCase()
    if(characterName.includes(searchTerm)){
      return characterName
    }else 'no match found'
   })
  //  console.log(filteredResult)
}



// function displayData(){
//   const resultsContainer = document.getElementById('results-container')

  
//     characters.forEach(character =>{
//       console.log(character)
//       const characterDiv = document.createElement('div')
//       resultsContainer.appendChild(characterDiv)
//       characterDiv.classList.add('character')

//       const charName = document.createElement('h2')
//       charName.textContent = character.name
//       characterDiv.appendChild(charName)
//       let image = document.createElement('img')
//       image.classList.add('results-image')
//       image.src = `${character.thumbnail.path + '.'}${character.thumbnail.extension}`
//       characterDiv.appendChild(image)
      
//     })

  // }else {
  //   const noResultsMessage = document.createElement('p');
  //   noResultsMessage.textContent = 'No results found';
  //   resultsContainer.appendChild(noResultsMessage);
  // }
  
// }

some of the code has been commented out as i am just focussing on the searchQuery function dont think i can get it to do what i want

You should use indexOf instead of include. Then it should work

1 Like

this is returning all the api results and then an empty array, i want to show only api results that contain my my search input so for exapmple if i search ‘men’ then i want a list of all characters that contain men

Opinionated I know, but the first thing I would do would be rename that function and call it something like getMatchingCharacters or getMatchingChars. ‘searchQuery’ is a bit vague to me, and doesn’t tell me what the function does.

I would change it so that it took two arguments, a search term and the characters array. That way you aren’t relying on globals, and hunting through your code to find where these variables originate from.

The other benefit is that you are able to test it as a standalone function.

function getMatchingChars(searchTerm, characters) {
  const searchToLower = searchTerm.trim().toLowerCase();
  // flatMap might be a better option here
  // this will return an array of only the matching characters
  const filteredResult = characters.flatMap((character) => {
    const characterName = character.name.toLowerCase();

    if (characterName.includes(searchTerm)) {
      return characterName;
    }

    return []; // this will be discarded by flatMap
  });
  //  console.log(filteredResult)
}

It looks like you want a mixture of filter and map to me, so I have swapped out what you have for flatMap

Example of flatMap.

const fruit = [ 'apple', 'banana', 'cherry', 'date', 'fig', 'grape' ];

fruit.flatMap((fruit) => {
  if (fruit.length === 5) {
    return fruit.toUpperCase();
  }
  return [];
})
// [ 'APPLE', 'GRAPE' ]

It might be helpful though if you could give us some sample JSON data, just to see what you are searching through, and some sample search terms.

the flatMap didnt work, this is the data i am getting from fetch

if i search zero i want to return any characters that contain zero

I managed to get things working abit but i think this code is messy

button.addEventListener('click', ()=>{
  let characters = []
  fetch(url).then(response=> response.json().then(data=>{
    characters = data.data.results;
    let filteredResult = searchQuery(characters)
     displayData(filteredResult)
  }))
  
})

function searchQuery(characters){
  const searchTerm =  searchInput.value.trim().toLowerCase()
  let filteredResult = characters.filter((character)=>{
    const characterName  = character.name.toLowerCase()
    if(characterName.includes(searchTerm)){
      return characterName
    }else 'no match found'
  })
  // console.log(filteredResult)
  return filteredResult.length > 0 ? filteredResult: 'no matching results'
}

function displayData(filteredResult){
  const resultsContainer = document.getElementById('results-container')

  if(filteredResult){
    filteredResult.forEach(character =>{
      console.log(character.thumbnail.extension)
      const characterDiv = document.createElement('div')
      resultsContainer.appendChild(characterDiv)
      characterDiv.classList.add('character')

      const charName = document.createElement('h2')
      charName.textContent = character.name
      characterDiv.appendChild(charName)
      let image = document.createElement('img')
      image.classList.add('results-image')
      image.src = `${character.thumbnail.path + '.'}${character.thumbnail.extension}`
      characterDiv.appendChild(image)
      
    });

  }else {
    const noResultsMessage = document.createElement('p');
    noResultsMessage.textContent = 'No results found';
    resultsContainer.appendChild(noResultsMessage);
  }
  
}

Here is my attempt at simplifying:

Alternatively, to search for words in the character names:

thanks for that i have another issue in that when ever i close my visual studio code then open my project up it does not run, i have to keep creating a vite project each time which cant be correct way of doing it, what am i doing wrong anyone know? im sure i figured this issue out a few months back but iv had a break from coding now forgot

I suggest you start a new topic for that.

1 Like

You have a misunderatnding of how filter works. Filter relies on a function (predicate) returning true or false. If the function returns true the item in the array is kept, if the function returns false the item in the array is discarded.

MDN

callbackFn
A function to execute for each element in the array. It should return a truthy value to keep the element in the resulting array, and a falsy value otherwise.

e.g.

const numbers  = [1,2,3,4,5,6,7,8,9,10]
const largerThanFour = numbers.filter((num) => {
    if (num > 4) {
        return true // keep these
    }
    return false // otherwise discard
})

console.log(largerThanFour) // [5,6,7,8,9,10]

Why your function is possibly filtering is that if characterName is a string with anything in it, it is evaluating to true.
e.g.

'superman' || false // 'superman'
'' || false // false 'empty string evaluates to false'

and you have an else statement that isn’t really doing anything, so what is being returned otherwise is undefined
undefined || false // false

It might be worth reading the documentation.

MDN - truthy

MDN - falsy