0

I'm trying to add a search bar to the top of a grouped table. However I'm unsure how to filter through my data. The data is stored in a nested array with objects held in this format;

struct Group {
    var id: String
    var type: String
    var desc: String
    var avatar: String
    var name: String

    init() {
        id = ""
        type = ""
        desc = ""
        avatar = ""
        name = ""
    }
}

Because I get data from two sources, two arrays are nested together, this also makes it simpler to create the two sections of the grouped table. I'll note, they both use the same Group struct.

self.masterArray = [self.clientArray, self.departmentArray]

This "masterArray" is then used to populate the table. Filtering/searching a single array isn't too difficult, but how do I search through a nested array?

func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {

}

EDIT: I've finally got things working, courtesy of @appzYourLife.

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
    print("Searching for:" + searchText)
    if searchText.isEmpty {
        filterArray = masterArray
    } else {
        filterArray = [ clientArray.filter { $0.name.range(of: searchText) != nil }] + [ departmentArray.filter { $0.name.range(of: searchText) != nil } ]
    }
    tableView.reloadData()
}
9
  • 1
    Oh okay, it was unclear to me that the nested arrays were an intentional design decision Commented Jun 27, 2016 at 16:23
  • I've edited the question, sorry about the confusion. Commented Jun 27, 2016 at 16:24
  • @Aloogy Regarding edit: your .filter{ ... } closure in its current form contains an incorrect argument type: the closure expects elements that are arrays themselves ([Group] elements). Try exchanging the above to filterArray = masterArray.flatten().filter(..... (Or, just: filterArray = masterArray.flatten().filter { $0.id.lowercaseString.containsString(searchText.lowercaseString) }). Also, have a look at this tutorial. Commented Jun 27, 2016 at 17:52
  • I actually get the exact same error when using flatten() Commented Jun 27, 2016 at 17:57
  • @Aloogy Ah, I suspect the type of filterArray is [[Group]], in which case the filtering operation must yield an object of type [[Group]]. Try filterArray = masterArray.map { $0.filter { $0.id.lowercaseString.containsString(searchText.lowercaseString) } }. Commented Jun 27, 2016 at 18:00

3 Answers 3

3

You can make use of .flatten() to flatten your array prior to filtering it for whatever search criteria you want to use. E.g.

struct Group {
    var id: String
    var name: String
    init(_ id: String, _ name: String) { self.id = id; self.name = name }
    /* .... */
}

let clientArray = [Group("a", "John"), Group("b", "Jane"), Group("c", "Phil")]
let departmentArray = [Group("a", "Foo"), Group("b", "Bar"),
                       Group("c", "Baz"), Group("d", "Bax")]

let arr = [clientArray, departmentArray]

// find some id
let searchForId = "c"
let hits = arr.flatten()
    .filter { $0.id.lowercaseString.containsString(searchText.lowercaseString) }

print(hits)
    // [Group(id: "c", name: "Phil"), Group(id: "c", name: "Baz")]

From the edits of your questions, it seems, however, that you want the resulting filtered array to be of the same nested array type as the "master" array. In such case, the following is a more appropriate approach:

/* ... */

// find some id but keep [[Group]] type
let searchText = "c"
let hits = arr.map { $0.filter { $0.id.lowercaseString.containsString(searchText.lowercaseString) } }

print(hits)
    // [[Group(id: "c", name: "Phil")], [Group(id: "cc", name: "Baz")]]
Sign up to request clarification or add additional context in comments.

Comments

2

Given

struct Group {
    let id: String = ""
    let type: String = ""
    let desc: String = ""
    let avatar: String = ""
    let name: String = ""
}

let clients = [Group(), Group(), Group(), Group()]
let departmens = [Group(), Group(), Group(), Group()]

let clientsAndDepartments = [clients, departmens]

You can search inside clients and department writing

let results = (clients + departmens).filter { $0.id == "123" }

enter image description here

Update #1

Now understand that you want to filter both arrays but as result you still want something like this [[Group]].

So here's the code

var filterArray = [clients.filter { $0.name == "White" }] + [departmens.filter { $0.name == "White" }]

Update #2

If you want to search for string inclusione the use this code

var filterArray = [ clients.filter { $0.name.rangeOfString("White") != nil }] + [ departmens.filter { $0.name.rangeOfString("White") != nil } ]

10 Comments

When I try and use this I get Cannot invoke 'filter' with an argument list of type '(@noescape (Group) throws -> Bool)'
@Aloogy: I added a screenshot of my playground, as you can see it does work. You should check the differences with your code. Can you add a screenshot of your code in Xcode showing the code I suggested and the error message you received?
@Aloogy: Please take a look at my Update #2
I'd just like to add for anyone using Swift-3, the syntax has changed, and in @appzYourLife's second example, .rangeOfString("White") is now .range(of: "White").
|
1

You can map over each of the arrays, and filter them independently:

self.masterArray.map{ subarray in 
    subarray.filter { element in
        trueWhenElementShouldStay(element)
    }
}

P.S. I suspect masterArray should NOT be an instance variable, it would be more appropriate as a local variable instead.

1 Comment

Do you have an idea why something like this won't work? func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { if(searchText.isEmpty){ filteredArray = masterArray } else { filteredArray = masterArray.filter { subarray in subarray.filter { element in return element.name.lowercaseString.rangeOfString(searchText.lowercaseString) } } tableView.reloadData() } } Thank you very much for your help.

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.