How to search through struct data using UISearchBar swift 4.2 - arrays

I have struct datatype which is use for decoding data
struct OtherCountry : Decodable {
let name : String
let dial_code : String
let code : String
}
struct FrequentCountry:Decodable{
let name : String
let dial_code : String
let code : String
}
I want to search based on name & code and this is stored in array of type struct
var OtherDataCountry = [OtherCountry]()
var FrequentDataCountry = [FrequentCountry]()
I have also implemented search function that looks something like this
func searchBar(searchText: String) {
searchCountry1 = OtherDataCountry.filter({ (OtherCountry) -> Bool in
return OtherCountry.name.range(of: searchText , options:[.caseInsensitive]) != nil
searchActive = !searchCountry1.isEmpty
self.mTableView.reloadData()
}
)}
}
can anyone help me to convert that struct data to String array , that would be helpful because i can search through array and use that result to show entire data .
Thank You for Help !

Please try below code
func searchBar(searchText: String) {
searchCountry1 = OtherDataCountry.filter {
$0.name.range(of: searchText , options:[.caseInsensitive]) != nil
}.map {$0.name}
searchActive = !searchCountry1.isEmpty
self.mTableView.reloadData()
}
)
}
searchCountry1 will be an array after map containing the name value

Related

How to acces array of structures in structure Swift

I have the following stucture:
struct Points : Codable {
var name : String?
var interests : [Interests]
///How do i get string or array of string that is equal to all interests
var allInterestText : String ///???
}
struct Interests : Codable {
var interest : Interest?
}
struct Interest : Codable{
var name : String?
}
I've been trying to achieve that, but all my attempts have failed.
Try this for a single String:
struct Points {
// ...
var allInterestText: String {
interests.compactMap { $0.interest?.name }.joined(separator: " ")
}
}
if you decide you want an Array instead, simple change the type and remove the .joined()

Swift 3: Getting error while joining the reversed() array

I have code like this:
class Stack<T>: CustomStringConvertible {
fileprivate var array: [T] = []
func pop() -> T? {
let popItem = self.array.popLast()
print("POP ITEM : \(popItem)")
return popItem
}
func push(item: T) {
print("PUSH ITEM : \(item)")
array.append(item)
}
var isEmpty: Bool {
return self.array.isEmpty
}
var count: Int {
return self.array.count
}
var description: String {
let topDivider = "### STACK ###\n"
let bottomDivider = "\n############\n"
let stackElements = self.array.reversed().joined(separator: "\n") // GETTING ERROR
return topDivider + stackElements + bottomDivider
}
}
Error: Type of expression is ambiguous without more context
I'm unable to join that reverse array.
REF : https://www.raywenderlich.com/149213/swift-algorithm-club-swift-stack-data-structure
Here they have done in struct with String successfully.
Can i know How i can achieve same here?
The problem is that the joined function requires that the separator be the same data type as the values being joined. Since self.array is an array of T, you can't pass a String as the separator. This mismatch is causing the error.
Since this is your description property and your goal is to get a string representation of the array, one solution would be to map the array of T to an array of String. Then you can join that String array with your newline separator.
One way to convert something to a String is to call the description method on it. But to do that, you need to limit your T to only those types that provide the description method which comes from CustomStringConvertible.
Change your class declaration to:
class Stack<T:CustomStringConvertible>: CustomStringConvertible {
and then your description property to:
var description: String {
let topDivider = "### STACK ###\n"
let bottomDivider = "\n############\n"
let stackElements = self.array.reversed().map { $0.description }.joined(separator: "\n")
return topDivider + stackElements + bottomDivider
}
Map your generic type T to String and then apply joined() function.
joined is only for elements of type String and here compiler do not know the type of T.
let stackElements = self.array.map{"\($0)"}.reversed().joined(separator: "\n")

Modify an array element after finding it in swift does not work

I wrote a model like this as an exercise :
struct Store {
var name : String
var bills : Array<Bill>
var category : Category?
}
struct Bill {
var date : String
var amount : Float
}
struct Category {
var name : String
var tags : Array<String>
}
and when I'm searching if a store already exist to add a bill to it instead of creating a new store, my code doesn't work. It acts like if the result of the search is a copy of the Array element . I would like to have a reference.
var stores : Array <Store> = Array()
for billStatment in billStatements! {
let billParts = billStatment.split(separator: ",")
if billParts.count > 0 {
let bill : Bill = Bill(date:String(billParts[0]), amount: Float(billParts[2])!)
var store : Store = Store(name:String(billParts[1]), bills: [bill], category: nil)
if var alreadyAddedStore = stores.first(where: {$0.name == String(billParts[1])}) {
alreadyAddedStore.bills.append(bill)
print("yeah found it \(alreadyAddedStore)") // the debugger breaks here so I know the "first" method is working. If I print alreadyAddedStore here I have one more element, that's fine.
} else {
stores.append(store)
}
}
}
print("\(stores.count)") // If I break here for a given store that should contains multiple elements, I will see only the first one added in the else statement.
Can anyone tell me what I am doing wrong?
As already noted, you're confusing value (struct) semantics with reference (class) semantics.
One simple fix would be the change stores to a dictionary with the name as your key:
var stores : Dictionary<String, Store> = [:]
and use it like this:
if(stores[store.name] == nil) {
stores[store.name] = store
}
else {
stores[storeName].bills.append(bill)
}

Using Array.map to seach in array of dictionaries

I'm trying to search for item inside of array of dictionaries and just return the match if there is any. Here is my code:
let book = self.listOfBooks.map({ (Books) -> String in
var bookName = String()
if searchText == Books?. name{
airportName = (Books?.author)!
return airportName
}
return // Error: Non-void function should return a value
})
But my problem is the .map is expecting return for each item in the array of self.listOfBooks. My question to you guys is how can just return the dictionary with only the matching the if ?
I'll really appreciate your help.
What you're looking for is flatMap not map.
let book = self.listOfBooks.flatMap({ (Books) -> String? in
var bookName = String()
if searchText == Books?.name{
airportName = Books?.author
return airportName
}
return nil
})
Shorter version
let airportNames = self.listOfBooks.flatMap { ($0?.name == searchText) ? $0?.author : nil }
Edit: If you want whole object not only author then you need to use filter for that.
let airportNames = self.listOfBooks.filter { $0?.name == searchText }

Swift 1.2 Filter an Array of Structs by keyword

I need some help filtering an array of Structs.
This is what I am doing currently, it filters the array but not correctly.
For example lets say I search for an item in the array with "Mid" I have one item that should be shown however the item shown starts with "Bad".
var array = breweries.filter() { $0.name?.lowercaseString.rangeOfString(searchController.searchBar.text.lowercaseString) != nil }
results = array
here is my Struct
struct Breweries {
let name: String?
let breweryId: String?
let distance: Double?
let largeIconURL: String?
let streetAddress: String?
let locality: String?
let region: String?
let phone: String?
let website: String?
init(brewDictionary: [String: AnyObject]) {
name = brewDictionary["brewery"]?["name"] as? String
breweryId = brewDictionary["breweryId"] as? String
distance = brewDictionary["distance"] as? Double
largeIconURL = brewDictionary["brewery"]?["images"]??.objectForKey("large") as? String
streetAddress = brewDictionary["streetAddress"] as? String
locality = brewDictionary["locality"] as? String
region = brewDictionary["region"] as? String
phone = brewDictionary["phone"] as? String
website = brewDictionary["website"] as? String
}
}
Please point in the right direction!
Note: I am using Swift 1.2
Update:
I thought a video would be of help to better explain what I am trying to do.
Demo Of issue
What I want is to find the filter the array so only the item with a similar name is shown.
Update 2: As it turns out I forgot to handle the case when my UISearchController was active.
Assuming your Struct name is Breweries and it has a name property, try this:
let array = breweries.filter() {
($0.name!.lowercaseString as NSString).containsString(searchController.searchBar.text.lowercaseString)
}
Your usage of filter is correct, but your closure seem to be complicated with no clear goal. I suggest you to write an extension (or possibly use what I am using):
extension String {
func contains(search: String, ignoreCase: Bool = false, ignoreDiacritic: Bool = false) -> Bool {
var options = NSStringCompareOptions.allZeros
if ignoreCase { options |= NSStringCompareOptions.CaseInsensitiveSearch }
if ignoreDiacritic { options |= NSStringCompareOptions.DiacriticInsensitiveSearch }
return self.rangeOfString(search, options: options) != nil
}
}
This way you can use closure like this to search:
breweries.filter() {
$0.name?.contains("x") // Precise search
$0.name?.contains("x", ignoreCase: true, ignoreDiacritics: true) // Ignores diacritics and lower / upper case
}
of course, you can use | or & to search for multiple parameters
breweries.filter() {
$0.name?.contains("x") || $0.streetAddress?.contains("x")
}
Hope it helps!
Here is an example from an investing app with struct:
import Foundation
public struct SNStock {
public let ticker:NSString
public let name:NSString
init(ticker:NSString, name:NSString) {
self.ticker = ticker
self.name = name
}
}
Search on Main Thread:
public func searchStocksByKeyword(keyword:String) -> [SNStock] {
let lowercaseKeyword = keyword.lowercaseString
var searchResults:[SNStock] = []
searchResults = stocks.filter({ (stock:SNStock) -> Bool in
return stock.ticker.lowercaseString.hasPrefix(lowercaseKeyword)
})
if (searchResults.count == 0) {
searchResults = stocks.filter({ (stock:SNStock) -> Bool in
return stock.name.lowercaseString.hasPrefix(lowercaseKeyword)
})
}
searchResults.sortInPlace {
($0.ticker as String) < ($1.ticker as String)
}
return searchResults;
}
Search on Background Thread:
public func searchStocksByKeyword(keyword:String, completion:(stocks:[SNStock])->()) {
let qualityOfServiceClass = QOS_CLASS_USER_INTERACTIVE
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
let stocks:[SNStock] = self.searchStocksByKeyword(keyword)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completion(stocks: stocks)
})
})
}

Resources