简体   繁体   中英

Swift Filter Nested Array

I have a NSMutableArray which it self has many arrays inside it. Inside each array at all index they further had custom objects of class given bellow.

Class User:NSObject{
    var name = ""
    var userName = ""
    var email = ""
    var age = 0
    var gender = ""
    var 
}

I want to filter this nested array with respect to two objects. For example if user type some text in searchBar and check that text in that nested array if that text matches with the name or the userName or both.

let nestedArray: [[User]] = [[user1, user2], [user3], [user4, user5]]

let searchName = "foo"
let filteredArray = nestedArray.map({
        return $0.filter({ $0.name == searchName })
    }).filter({ $0.count > 0 })

This is a purely functional way that results in a new nested array that only contains arrays with matching users and these arrays also will only contain the matching users.

A few suggestions

A struct is a better fit for a model value

struct User {
    let name: String
    let userName: String
    let email: String
    let age: Int
    let gender: String
}

Don't use NSMutableArray

NSMutableArray is an Objective-C class. You can access it via Swift but you should use the Swift array struct. It is faster and, being a value type, prevents bugs related to multiple parts of your code accessing a shared object.

Filtering your data

Now given an array of arrays of User

let data: [[User]] = ...

and a keyword

let keyword: String = ...

your can filter your data writing

let matchingUsers = data
    .flatMap { $0 }
    .filter { $0.name.range(of: keyword) != nil || $0.userName.range(of: keyword) != nil }

Now matchingUsers is an array of User(s) where the name or the username contains the keyword .

let textToSearch:String = "some text"

for nestedArray in myArray {

    for item:User in nestedArray
    {
        if user.name.contains(textToSearch) || user.userName.contains(textToSearch)
        {
            print("found")
        }
    }

}

Here is a more "swiftly" way to go about doing this using the filter(_:) method provided by Array .

Method:

func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> [Element]

Implementation:

    // Create some generic objects to test on
    let object1 = User()
    object1.name = "John"
    object1.userName = "jdoe"

    let object2 = User()
    object2.name = "Jane"
    object2.userName = "jdoe"

    let object3 = User()
    object3.name = "Bob"
    object3.userName = "bjones"

    // Add them to a test array
    var objects = [[]]
    objects.append([object1])
    objects.append([object2])
    objects.append([object3])

    // What we want to search on
    let searchString = "j"

    // Filter the array
    for array in objects {
        let searchedSubArray = array.filter {
            return $0.name.rangeOfString(searchString, options: .CaseInsensitiveSearch)     != nil ||
                   $0.userName.rangeOfString(searchString, options: .CaseInsensitiveSearch) != nil

        }
        if (searchedSubArray.count > 0) {
            print("we found a match!")
        }
    }

A slightly improved version of what matteok suggested:

It makes sense to ignore register in such filtering, and instead of checking equality check if lookup target contains search query

Also using !$0.isEmpty is more swifty than checking for $0.count > 0

let nestedArray: [[User]] = [[user1, user2], [user3], [user4, user5]]

let searchName = "foo".lowercased()
let filteredArray = nestedArray
  .map { $0.filter { $0.name.lowercased().contains(searchName) }}
  .filter { !$0.isEmpty }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM