Как использовать searchBar с файлом JSON?

Я использую этот код для успешного анализа файла JSON в tableView:

struct DetailSections: Decodable {
    var detailSections: [DetailSection]
}

struct DetailSection: Decodable {
    let title, translation, transcription : String
}

Далее я пытаюсь создать searchBar для поиска по title и translation:

var detailSection = [DetailSection]()
var filteredData = [String]()
var isSearching = false

class SearchViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        search.searchBar.delegate = self
        
        table.dataSource = self
        table.delegate = self
        
        view.addSubview(table)
        
        navigationItem.searchController = search
    }
    
    func decoder() -> Int {
        let url = Bundle.main.url(forResource: "detailSections", withExtension: "json")!
        let data = try! Data(contentsOf: url)
        let result = try! JSONDecoder().decode(DetailSections.self, from: data)
        self.detailSection = result.detailSections
        
        return detailSection.count
    }
    
}

Но я не знаю, что мне делать в этой строке: "???"

extension SearchViewController: UISearchBarDelegate {
    
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        self.filteredData.removeAll()
        guard searchText != "" || searchText != " " else {
            print("empty search")
            return
        }
        
        for item in "???" {
            let text = searchText.lowercased()
            let isArrayContain = item.lowercased().range(of: text)
                
            if isArrayContain != nil {
                print("Search complete")
                filteredData.append(item)
            }
        }
            
        print(filteredData)
            
        if searchBar.text == "" {
            isSearching = false
            table.reloadData()
        } else {
            isSearching = true
            filteredData = data.filter({$0.contains(searchBar.text ?? "")})
            table.reloadData()
        }
    }
    
}

Моя таблица:

extension SearchViewController: UITableViewDelegate, UITableViewDataSource {
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        if isSearching {
            return filteredData.count
        } else {
            return decoder()
        }
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "DetailCell", for: indexPath) as! DetailCell
        
        cell.title.text = detailSection[indexPath.row].title
        cell.translation.text = detailSection[indexPath.row].translation
        cell.transcription.text = detailSection[indexPath.row].transcription
        
        cell.selectionStyle = .none
        
        return cell
    }
    
}

В каких свойствах вы хотите искать текст? Все три title, translation и transcription?

Sweeper 12.08.2024 11:23

@Sweeper только название, перевод

user 12.08.2024 11:40
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это зависит от того, чего вы хотите, но обычно filteredData должно быть [DetailSection].

Без проверки случая для упрощения логики:

for item in detailsSection { 
    if item.title.contains(searchText) { 
        filteredData.append(item) 
    } else if item.translation.contains(searchText) {
        filteredData.append(item)
    } else { 
        print("\(searchText) not found title \(item.title) nor in translation \(item.translation)) 
    }
}

Конечно, это можно упростить, но явный код должен объяснять логику.

Боковое примечание: У вас есть print("empty search") с возвратом, если поиск пуст, но если вы искали раньше, а затем удалили текст поиска, в вашем случае вы не перезагрузите данные.

Поэтому я бы сделал это вот так в псевдокоде:: Всегда используйте filterData (в методах DataSource), а detailSection — это полные данные.

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    if search.empty() {
        filteredData = detailSection //Full data
    } else {
         filteredData = filterAccordingToSearchText()
    }
    tableView.reloadData()

}

Теперь пойдем немного дальше:

Вам следует использовать value.range(of: searchText, options: .caseInsensitive) != nil вместо lowerCase() для каждого значения.

Так что item.title.contains(searchText) может быть item.title.range(of: searchText, options: .caseInsensitive) != nil.

С помощью более высокого метода filter (но нет ничего плохого в том, чтобы выполнить руководство по циклу, это базовый навык, который вам все равно нужно освоить):

filteredData = detailsSection.filter { item in 
    if item.title.range(of: searchText, options: .caseInsensitive) != nil { 
        return true
    } else if item.translation.range(of: searchText, options: .caseInsensitive) != nil { 
        return true
    } 
    return false 
}

Полный код должен быть: var DetailSection = DetailSection var filteredData = DetailSection

//Here don't decode EACH time to know the count
func decodeSection() {
    let url = Bundle.main.url(forResource: "detailSections", withExtension: "json")!
    let data = try! Data(contentsOf: url)
    let result = try! JSONDecoder().decode(DetailSections.self, from: data)
    self.detailSection = result.detailSections
}

override func viewDidLoad() {
    ...
    decodeSection()
    table.reloadData()
}

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    
    guard !searchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
        filteredData = detailSection //search is empty, the "filtered" is equal to put everything
        table.reloadData()
        return
    }
    
    filteredData.removeAll()
    //Search Text is NOT empty, we filter
    for item in detailSection {
        if item.title.contains(searchText) {
            filteredData.append(item)
        } else if item.translation.contains(searchText) {
            filteredData.append(item)
        } else {
            print("\(searchText) not found title \(item.title) nor in translation \(item.translation) -> Ignored")
        }
    }
    
    table.reloadData()
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return filteredData.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "DetailCell", for: indexPath) as! DetailCell
    let item = filteredData[indexPath.row]
    cell.title.text = item.title
    cell.translation.text = item.translation
    cell.transcription.text = item.transcription
    cell.selectionStyle = .none
    return cell
}

Я хочу искать только title и translation. В этой строке filteredData.append(item) я получаю следующую ошибку: Cannot convert value of type 'DetailSection' to expected argument type 'String'

user 12.08.2024 11:46

Это первая строка моего ответа. В TableView вы хотите, чтобы заголовок и перевод отображались только после фильтрации, или вы хотите показывать DetailSection (с названием и переводом), соответствующие запросу?

Larme 12.08.2024 11:49

Я добавил новый код из tableVIew в вопрос. В моем tableView есть DetailCell это вид с title, translation, transcription и несколькими кнопками. Я хочу показать результат поиска, как мое представление, в ячейке с полными элементами (заголовок, перевод, транскрипция и некоторые кнопки). Но я хочу искать в этих ячейках только по названию и переводу.

user 12.08.2024 17:36

В вашем ответе filteredData это массив detailsSection, но мне нужен какой-то способ фильтрации только для title и translation внутри detailsSection. И я получаю ошибку Cannot convert value of type 'DetailSection' to expected argument type 'String'

user 12.08.2024 17:38

Я обновил ответ

Larme 12.08.2024 20:28

Те же проблемы filteredData = detailSection — невозможно присвоить значение типа «[DetailSection]» типу «[String]».

user 12.08.2024 20:40

«filteredData должен быть [DetailSection]»: var filteredData = [DetailSection](), а не var filteredData = [String]().

Larme 12.08.2024 20:43

Другие вопросы по теме