skip to Main Content

I have a simple search bar and a somewhat large JSON file with name, x, y values for each entry. Given some (x, y) values (e.g.: 1.2 0.5) the JavaScript search bar shows cards that are within a fixed distance of 1 to that value.

This works but I need to:

  1. Limit the number of results to a maximum (say 10) so that there won’t be hundreds or more results shown at the same time
  2. Order the results by minimum distance to the (x, y) value searched

Any help will be much appreciated

const userCardTemplate = document.querySelector("[data-user-template]")
const userCardContainer = document.querySelector("[data-user-cards-container]")
const searchInput = document.querySelector("[data-search]")

let users = []

searchInput.addEventListener("input", e => {
  const value = e.target.value.toLowerCase()
  const xy = value.split(' ')
  users.forEach(user => {
  if (parseFloat(value.length) < 4) {
  user.element.classList.toggle("hide", true)
} else {
    var distance = Math.sqrt(
      Math.pow(parseFloat(xy[0]) - parseFloat(user.x), 2) +
      Math.pow(parseFloat(xy[1]) - parseFloat(user.y), 2))
    const isVisible = distance <= 1
  user.element.classList.toggle("hide", !isVisible)
}
  })
})

fetch("https://raw.githubusercontent.com/ucc23/ucc/main/test.json")
  .then(res => res.json())
  .then(data => {
users = data.map(user => {
  const card = userCardTemplate.content.cloneNode(true).children[0]
  const header = card.querySelector("[data-header]")
  const body = card.querySelector("[data-body]")
  header.textContent = user.name
  card.classList.add('hide')
  userCardContainer.append(card)
  return {
    name: user.name,
    x: user.x,
    y: user.y,
    element: card
  }
})
  })
.user-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: .25rem;
  margin-top: 1rem;
}

.card {
  border: 1px solid black;
  background-color: white;
  padding: .5rem;
  text-align: center;
}

.card > .header {
  margin-bottom: .25rem;
}

.hide {
  display: none;
}
  <div class="search-wrapper">
    <input type="search" id="search" data-search>
  </div>
  <div class="user-cards" data-user-cards-container></div>
  <template data-user-template>
    <div class="card">
      <div class="header" data-header></div>
    </div>
  </template>

2

Answers


  1. For my answer, I’m not appending the cards on the loading of the json file. This makes it load faster plus allows easy sorting later on.

    I created a new array called results, this will store the matching results. On input, I empty the results and empty the html of the card container, that way the results will always be up to date. Then I append the user with it’s distance to the results array.

    After that I sort the array based on the distance, then I slice the array to the first N results in this case I have it set to 10.

    After that I simply loop through the results and append them to the card container element.

    Also, I moved the input length if statement outside of the loop, no need to re run it for every user.

    const userCardTemplate = document.querySelector("[data-user-template]")
    const userCardContainer = document.querySelector("[data-user-cards-container]")
    const searchInput = document.querySelector("[data-search]")
    
    let users = []
    let results = []
    searchInput.addEventListener("input", e => {
      results = []; //quickly empty array
      userCardContainer.innerHTML = "";
      const value = e.target.value.toLowerCase()
      const xy = value.split(' ')
      if (parseFloat(value.length) >= 4) {
        users.forEach(user => {
    
          var distance = Math.sqrt(
            Math.pow(parseFloat(xy[0]) - parseFloat(user.x), 2) +
            Math.pow(parseFloat(xy[1]) - parseFloat(user.y), 2))
        if(distance <= 1){
          user.distance = distance
          results.push(
            user
          );
         }
    
        })
    
      }
    
      results.sort((a, b) => a.distance - b.distance)
      
      results = results.slice(0,10)
      
      results.forEach((u) => {
        userCardContainer.append(u.element);
      });
    
      
    })
    
    fetch("https://raw.githubusercontent.com/ucc23/ucc/main/test.json")
      .then(res => res.json())
      .then(data => {
        users = data.map(user => {
          const card = userCardTemplate.content.cloneNode(true).children[0]
          const header = card.querySelector("[data-header]")
          header.textContent = user.name
          return {
            name: user.name,
            x: user.x,
            y: user.y,
            element: card
          }
        })
      })
    .user-cards {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
      gap: .25rem;
      margin-top: 1rem;
    }
    
    .card {
      border: 1px solid black;
      background-color: white;
      padding: .5rem;
      text-align: center;
    }
    
    .card>.header {
      margin-bottom: .25rem;
    }
    
    .hide {
      display: none;
    }
    <div class="search-wrapper">
      <input type="search" id="search" data-search>
    </div>
    <div class="user-cards" data-user-cards-container></div>
    <template data-user-template>
        <div class="card">
          <div class="header" data-header></div>
        </div>
      </template>
    Login or Signup to reply.
  2. I shortened the input data a little to make it clearer:

    const data = [
      {
       "name": "mwsc4688",
       "x": 0.5809138064,
       "y": 0.3204712061
      },
      {
       "name": "mwsc5684",
       "x": 0.831380058,
       "y": 0.334242817
      },
      {
       "name": "mwsc5692",
       "x": 0.1744799133,
       "y": 0.2460810205
      },
      {
       "name": "mwsc4005",
       "x": 0.9892221203,
       "y": 0.8656343945
      },
      {
       "name": "mwsc4176",
       "x": 0.2958017735,
       "y": 0.6074523398
      },
      {
       "name": "eso00806",
       "x": 0.3700941722,
       "y": 0.7938580839
      },
      {
       "name": "mwsc4219",
       "x": 0.0444884369,
       "y": 0.6772385082
      },
      {
       "name": "mwsc5575",
       "x": 0.0239875802,
       "y": 0.5067098227
      },
    ];
    
    function search(x, y, distance = 1, count = 10){
      return data.filter(item => Math.abs(x - item.x) <= distance && Math.abs(y - item.y) <= distance)
        .sort((a, b) => (b.x + b.y - distance) - (a.x + a.y - distance))
        .slice(0, count);
    }
    
    console.log(search(1.2, .5, 1, 6));
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search