#javascript #json #vue.js
Вопрос:
Я пытаюсь отфильтровать список магазинов v-для цикла. Код Vue , который у меня есть, работает при фильтрации "store"
, но я изо всех сил пытаюсь понять, как заставить фильтр поиска работать с вложенным json, как "section_name"
и даже глубже "items"
.
примеры:
поиск «молоко» -> Store A dairy section only (match daily/almond milk in item)
поиск «фрукты» -> Store B fruit section only (match fruit sectio_name)
поиск «зеленый» -> 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"
]
}
]
}
]
Комментарии:
1. Как только вы начнете пытаться сопоставлять вложенные термины, вы, возможно, захотите рассмотреть возможность передачи поиска в выделенный индекс, такой как elasticsearch, вместо создания собственной сложной фильтрации во время цикла
2. Полные данные-это файл json объемом 5 кб (256 строк), который меняется раз в год. Я не знаком с elasticsearch, не будет ли это немного излишним для данного варианта использования?
3. Даже для такого объема данных вам все равно потребуется создать множество регулярных выражений / фильтров, чтобы кто-то, вводящий
apple
текст, все равно получалapples
совпадение. Так что это зависит от того, насколько «хорошей» должна быть поисковая способность, но большинство людей, вероятно, оценят предсказуемость поиска, к которой они больше привыкли. Когда я учился использовать elastic с приложением node, эта статья очень быстро привела меня туда markpenovich.com/posts/…
Ответ №1:
У меня возникли некоторые проблемы с определением того, какой именно вывод вы хотите получить от функции, поэтому давайте рассмотрим это с самого простого и станем более сложными, вот так:
- Перечислите объект хранилища, если какой-либо из его разделов содержит совпадение
- Перечислите копию объекта хранилища, содержащую только соответствующие разделы
Кроме того, мы будем использовать includes() из прототипа строки для выполнения фактического сопоставления. Вы можете заменить метод сравнения сопоставлением регулярных выражений или каким-либо другим решением, но ваше текущее прямое сравнение не найдет «молоко» из «миндального молока», потому 'milk' !== 'almond milk'
что, но 'almond milk'.includes('milk')
.
Функция toLowerCase() будет опущена для краткости, потому что вы уже используете ее.
Наконец, {}
и return
будет опущено, так как каждая функция возвращает результат однострочного.
Магазины, содержащие спички
Поэтому мы хотим вернуть каждому store
по крайней мере по одному data
с items
массивом, содержащим по крайней мере один элемент, включающий ТЕРМИН.
Поскольку нам нужно найти только по крайней мере один экземпляр соответствия для этого случая, мы отфильтруем хранилище, а затем используем функцию find() из прототипа массива. find принимает функцию, а не значение, и возвращает неопределенное (ложное значение), а не -1, если ничего не найдено.
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
)
)
)
)
Разделы, содержащие совпадения
Основываясь на ваших примерах, похоже, что вы хотите вернуть только соответствующие разделы, а не соответствующие магазины. Для этого мы будем использовать функцию map() из прототипа массива, чтобы заменить результаты из нашего отфильтрованного списка объектом, в котором data
массив содержит только соответствующие хранилища.
Мы разрушим параметры нашей функции сопоставления, чтобы нам было легче структурировать возвращаемый объект.
const sections = computed(
() => filtered.value.map(
({ store, data }) => ({
store, data: data.filter(
d => d.items.find(
item => item.includes(TERM)
)
)
})
)
)
Вы можете изменить вывод функции карты в соответствии с точной формой данных, которые вы хотите вернуть.