0

I am trying to filter a v-for loop list of stores. The Vue code I have works if filtering by "store", but I'm struggling to find out how to have the search filter work with nested json like "section_name" and even deeper "items".

examples:
search "milk" -> Store A dairy section only (match daily/almond milk in item)
search "fruit" -> Store B fruit section only (match fruit sectio_name)
search "green" -> Store A veggie section only (match green beans item)

<script setup>
import { computed, reactive, ref } from "vue";

const stores = reactive([]);
const mySearch = ref("");

fetch("http://localhost:8081/sample.json")
  .then((res) => res.json())  
  .then((data) => {
    data.forEach(el => {
      stores.push(el)
    });
  })

const filterStores = computed(() => {
   return stores.filter((store) => {
      return store.store.toLowerCase().indexOf(mySearch.value.toLowerCase()) != -1
   });
})

</script>


<template>
  <main>
    <div class="container-fluid">
      <div class="row header">
        <div class="d-flex justify-content-center mt-0 p-4">
            <input type="text" placeholder="select store" class="form-control form-control-lg" v-model="mySearch"/>
        </div>
      </div>
      <div class="row">
        <div class="container">
          <div v-for="(store, index) in filterStores" :key="index">
              <li>{{ store.store }}</li>
          </div>
        </div>
      </div>
    </div>
  </main>
</template>
sample.json file
[
    {
        "store": "Store A",
        "data": [
            {
                "section_name": "veggies",
                "section_num": "301",
                "items": [
                    "carrots",
                    "green beans"
                ]
            },
            {
                "section_name": "dairy",
                "section_num": "302",
                "items": [
                    "dairy milk",
                    "almond milk"
                ]
            }
        ]
    },
    {
        "store": "Store B",
        "data": [
            {
                "section_name": "fruit",
                "section_num": "336",
                "items": [
                    "apples",
                    "oranges"
                ]
            },
            {
                "section_name": "bread",
                "items": [
                    "wheat bread"
                ]
            }
        ]
    }
]
3
  • Once starting to try to match nested terms, you may want to look at handing off search to a dedicated index like elasticsearch, rather than creating your own complex filtering while looping Commented Oct 8, 2021 at 2:38
  • The full data is 5kb json file (256 lines) that changes once a year. I'm not familiar with elasticsearch, would it be a bit overkill for this use case? Commented Oct 8, 2021 at 2:51
  • Even for that size of data, you will still need to create a lot of regex / filtering so that someone typing apple still gets the apples to match. So it depends how 'good' the search ability needs to be but most humans will probably appreciate search predicatbility they are more used to. When I was learning to use elastic with a node app this article got me there really quickly markpenovich.com/posts/… Commented Oct 8, 2021 at 5:37

1 Answer 1

1

I'm having a bit of trouble figuring out specifically what output you want from the function, so let's tackle this from the simplest, and get more complex from there, like so:

  1. List the store object if any of its sections contain a match
  2. List a copy of the store object containing only the relevant sections

Also, we'll use includes() from the String Prototype to do the actual matching. You can replace the comparison method with a regex match or some other solution, but your current direct comparison won't find 'milk' out of 'almond milk' because 'milk' !== 'almond milk' but 'almond milk'.includes('milk').

toLowerCase() will be left out for brevity, because you're already making use of it.

Finally, {} and return will be left out since each function is returning the result of a one-liner.

Stores containing matches

So we want to return each store with at least one data with an items array containing at least one item that includes TERM.

Since we only need to find at least one instance of a match for this case, we'll filter on the store, then use find() from the Array prototype. find takes a function rather than a value, and returns undefined (a falsy value), rather than -1, if nothing is found.

const filtered = computed(
  () => stores.filter( //return each store with
    store => store.data.find( //a data item that contains at least one
      d => d.items.find( //items array that contains at least one
        item => item.includes(TERM) //item that includes TERM
      )
    )
  )
)

Sections containing matches

Based on your examples, it looks like you only want to return the matching sections, rather than the matching stores. For that, we'll use map() from the Array prototype to replace the results from our filtered list with an object where the data array contains only the relevant stores.

We'll destructure the parameters of our mapping function so that we can more easily structure our returned object.

const sections = computed(
  () => filtered.value.map(
    ({ store, data }) => ({ 
      store, data: data.filter(
        d => d.items.find(
          item => item.includes(TERM)
        )
      )
    })
  )
)

You can modify the output of the map function to match the exact shape of the data you want to return.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.