简体   繁体   中英

Filter array of array with keyword in Swift

let searchResultItem1 = SearchResult()
searchResultItem1.type = "contact"
searchResultItem1.typeTitle = "CONTACTS"
searchResultItem1.results = ["Joe" , "Smith" , "Alan" , "Nick" , "Jason"]

let searchResultItem2 = SearchResult()
searchResultItem2.type = "address"
searchResultItem2.typeTitle = "ADDRESS"
searchResultItem2.results = ["829 6th Street North Fullerton" , "669   Windsor Drive Randallstown" , "423 Front Street Lacey"]
searchResults.append(searchResultItem1)
searchResults.append(searchResultItem2)

When i search for "al" i am expecting to return one SearchResult with its results "[Alan]" and another SearchResult with its result "["669 Windsor Drive Randallstown"]"

I tried the below filter , but returns empty array.

let results = searchResults.filter({$0.results.contains("al")})

Within your filter , $0.results is, in each example:

["Joe" , "Smith" , "Alan" , "Nick" , "Jason"]
["829 6th Street North Fullerton" , "669   Windsor Drive Randallstown" , "423 Front Street Lacey"]

The Array.contains(_) function searches for an object exactly matching it's argument, not a substring of the argument

To get each entire array that contains a string element with the substring "al", use:

let results = searchResults.filter({$0.results.filter({$0.rangeOfString("al") != nil}).count > 0})

Alternatively, to get just the strings with the substring "al", use:

let results = searchResults.map{ $0.results }.reduce([], combine: {$0 + $1}).filter { $0.rangeOfString("al") != nil }

Note that this won't match "Alan" because rangeOfString is case-sensitive.

As both other answers point out, you're searching your array of results for an item with a value of "al". What you're wanting to is actually return an array of results, narrowed down to only those that match:

struct SearchResult {
    let type:String
    let typeTitle:String
    let results:[String]
}

let searchResultItem1 = SearchResult(
    type: "contact",
    typeTitle: "CONTACTS",
    results: ["Joe" , "Smith" , "Alan" , "Nick" , "Jason"]
)

let searchResultItem2 = SearchResult(
    type:"address",
    typeTitle: "ADDRESS",
    results:["829 6th Street North Fullerton" , "669   Windsor Drive Randallstown" , "423 Front Street Lacey"]
)

var searchResults = [ searchResultItem1, searchResultItem2 ]

Now then, again, for convenience, define a case insensitive contains function for String:

extension String {
    func containsIgnoreCase(substring:String) -> Bool {
        return rangeOfString(
            substring,
            options: .CaseInsensitiveSearch,
            range: startIndex..<endIndex,
            locale: nil)?.startIndex != nil
    }
}

Note that String already has a contains function, it's just case sensitive, but if that's sufficient, you don't even need to define your own.

Now, you can use map to get rid of results that don't contain your search string:

searchResults = searchResults.map({
    return SearchResult(
        type: $0.type,
        typeTitle: $0.typeTitle,
        results: $0.results.filter({
            $0.containsIgnoreCase("al")
        })
    )
})

And, presumably, you also want to eliminate any SearchResult with no actual results, so use filter:

searchResults = searchResults.filter { $0.results.count > 0 }

Of course, the whole thing can be strung into one expression:

searchResults = searchResults.map({
    return SearchResult(
        type: $0.type,
        typeTitle: $0.typeTitle,
        results: $0.results.filter({
            $0.contains("al")
        })
    )
}).filter { $0.results.count > 0 }

And, you can possibly further reduce some of the iteration by using flatMap , which is like map , but eliminates any nil values:

searchResults = searchResults.flatMap {
    let results = $0.results.filter { $0.containsIgnoreCase("al") }
    if results.count > 0 {
        return SearchResult(type: $0.type, typeTitle: $0.typeTitle, results: results)
    } else {
        return nil
    }
}

Your filter checks if the results array contains an entry of "al", not if any of the strings in results contains "al". You need to filter the results array and construct a new SearchResult with the filtered results array.

I would probably flat map a function that filtered the results array of the input SearchResult and return an optional depending on whether or not any matches were found.

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