Использование плоской карты для функции поиска

У меня есть массив, который загружается в TableView.

var sections = [

        Section(sec: "One",
                subSec: ["A", "B", "C", "D", "E"],
                expanded: false),

        Section(sec: "Two",
                subSec: ["A", "B", "C", "D", "E"],
                expanded: false),

        Section(sec: "Three",
                subSec: ["A", "B", "C", "D", "E"],
                expanded: false),

Вот определение разделов:

struct Section {
    var sec: String!
    var subSec: [String]! // [ ] = Array of Strings
    var expanded: Bool!

    init(sec: String, subSec: [String], expanded: Bool) {
        self.sec = sec
        self.subSec = subSec
        self.expanded = expanded
    }
}

Я пытаюсь создать функцию поиска. Он работает для стандартного массива, но не для моей переменной, поскольку он более сложный. Я (с помощью) скорректировал код, используя плоскую карту, но я не знаю, как сопоставить его со строками subSec.

func filterContentForSearchText(_ searchText: String, scope: String = "All") {
        filtered = sections.flatMap{ return $0.subSec.lowercased().contains(searchText.lowercased()) ? $0.subSec : nil }

        tableView.reloadData()
    }

В настоящее время я получаю следующую ошибку: Value type [String] has no value lowercased

Дополнительный код:

var filtered = [Section]()
let searchController = UISearchController(searchResultsController: nil)

person 128K    schedule 26.08.2017    source источник
comment
Вы хотите искать разделы, в которых есть подразделы с точно таким же текстом? Или он просто должен содержать поисковый текст?   -  person Sweeper    schedule 26.08.2017
comment
Содержать. то есть, если я буду искать 'A', он вернет все subSec, которые соответствуют A в TableView   -  person 128K    schedule 26.08.2017
comment
Если вы ищете A, соответствует ли он ABC? Кроме того, вы хотите получить в результате весь массив подразделов или только строку "A"?   -  person Sweeper    schedule 26.08.2017
comment
Только строка A. Пример: Если выполняется поиск по A, то у него будет табличное представление с тремя полями A.   -  person 128K    schedule 26.08.2017


Ответы (3)


Проверить, содержит ли строка другую строку, игнорируя регистр

baseText.range(
    of: searchText, options: .caseInsensitive, range: nil, locale: nil
) != nil

Код

Если вы хотите получить весь раздел, вам не нужно использовать flatMap, просто используйте filter

var filtered: [Section] = []

func filterContentForSearchText(_ searchText: String, scope: String = "All") {
    filtered = sections.filter { section in
        section.subSec.contains { 
            $0.range(
                of: searchText, options: .caseInsensitive, range: nil, locale: nil
            ) != nil 
        }
    }
}

Но если в разделе вам нужны только те подразделы, которые содержат искомый текст, тогда

var filtered: [Section] = []

func filterContentForSearchText(_ searchText: String, scope: String = "All") {
    filtered = sections.map { section in
        var section = section
        section.subSec = section.subSec.filter {
            $0.range(
                of: searchText, options: .caseInsensitive, range: nil, locale: nil
            ) != nil
        }
        return section
    }
}

Но если вам нужны только подразделы, тогда

var filtered: [String] = []

func filterContentForSearchText(_ searchText: String, scope: String = "All") {
    filtered = sections.flatMap { section in
        section.subSec.filter {
            $0.range(
                of: searchText, options: .caseInsensitive, range: nil, locale: nil
            ) != nil
        }
    }
}
person Adrian Bobrowski    schedule 26.08.2017
comment
Я думаю, что OP использует flatMap, потому что он хотел [String] в качестве результата вместо [Section]. - person Sweeper; 26.08.2017
comment
да. Мне нужно вернуть строку, чтобы заполнить отфильтрованные результаты в виде таблицы - person 128K; 26.08.2017

Вашу ошибку можно решить, map переместив все подразделы в нижний регистр.

Однако это даст вам [[String]] вместо [String], и результаты будут не такими, как вы хотели.

Опишем словами то, что вы хотите сделать:

сгладить sections, чтобы я получил одномерный массив всех подразделов в виде строк

сохраняйте только те элементы в подразделах, которые соответствуют строке поиска.

Чтобы сделать первое, вам понадобится flatMap. Для второго вам понадобится filter.

Вы не можете использовать для этого одну плоскую карту, потому что вы не можете вернуть nil в закрытии плоской карты, чтобы сказать избавиться от этого.

let filtered: [String] = 
    sections.flatMap { $0.subSec }
             .filter { $0.lowercased().contains(searchText.lowercased()) }
person Sweeper    schedule 26.08.2017
comment
Спасибо. Так что я использую это вместо filtered = sections.flatMap{ return $0.subSec.lowercased().contains(searchText.lowercased()) ? $0.subSec : nil } - person 128K; 26.08.2017
comment
@ 128K Да. filtered в вашем коде должно быть [String] правильно? - person Sweeper; 26.08.2017
comment
@Sweeper Под вопросом - дополнительный код: var filtered = [Section](), но в комментарии к моему ответу он сказал [String] - person Adrian Bobrowski; 26.08.2017
comment
@ 128K Почему filtered [Section]? Это не имеет никакого смысла. Должно быть [String], не так ли? - person Sweeper; 26.08.2017
comment
Я изменил его на String, вы правы. Спасибо - person 128K; 26.08.2017
comment
Заменяет ли let filter мой filter = section.flapMap? - person 128K; 26.08.2017
comment
@ 128K Нет, вам нужно присвоить результат вашему свойству filtered, не новой константе, созданной let. - person Sweeper; 26.08.2017
comment
OK. Не думаю, что я правильно понял. См. Править - person 128K; 26.08.2017
comment
@ 128K извините, если я недостаточно хорошо объяснил. По сути, удалите let и : [String] из моего кода. - person Sweeper; 26.08.2017
comment
Спасибо за вашу помощь! Это устранило ошибку, но теперь возникла фатальная ошибка: фатальная ошибка: неожиданно обнаружен nil при разворачивании необязательного значения. Это в следующей строке: tableView.tableHeaderView = searchController.searchBar - person 128K; 26.08.2017
comment
@ 128K Я думаю, что это выходит за рамки этого вопроса. Предлагаю вам проверить, равно ли tableView или searchController.searchBar нулю. Я думаю, проблема здесь в том, что вы помещаете эту строку в место, где одна или несколько из вышеупомянутых переменных не были инициализированы. Если вы все еще застряли, задайте другой вопрос. - person Sweeper; 26.08.2017

Вместо того

$0.subSec.lowercased().contains(searchText.lowercased())

ты мог бы попробовать

$0.subSec.contains({$0.lowercased() == searchText.lowercased()})
person algrid    schedule 26.08.2017