Filtering Array but keeping the indexPath position - arrays

I am using a UISearchController in my updateSearchResultsForSearchController
I am filtering an array of usernames with a NSPredicate:
self.filteredChats.removeAll(keepCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (self.gruppenNamen as NSArray).filteredArrayUsingPredicate(searchPredicate)
self.filteredChats = array as! [String]
Is there a way to store the indexPath of each filtered element?
I need to know that in order to display my cells correctly, since I am also using arrays for images and other data.

Let's says that you have gruppenNamen and gruppenImages. You want to keep them synchronized.
I would strongly suggest that you create a custom class with a property name, and a property image.
Since you seem to not want that, you could use indexesOfObjectsPassingTest: and objectsAtIndexes:. I don't use Swift, so I'll code in Objective-C, but it should be easily translated.
NSIndexSet *indexes = [self.gruppenNamen indexesOfObjectsPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
return [(NSString *)obj rangeOfString:searchController.searchBar.text options:NSCaseInsensitiveSearch].location != NSNotFound;
}];
self.filteredChat = [self. gruppenNamen objectsAtIndexes:indexes];
self.filteredImages = [self.gruppenImages objectsAtIndexes:indexes];
I used rangeOfString:options for the equivalent of contains[c] of your predicate. You could use a NSPredicate here too.

Related

I want to filter array

I got two arrays from API call,
One is nameArray - which contains recipe names ( menuNameArray = ["pohe","bread","cheese chilli toast"]
And another array - which contains prices of those recipes (menuPriceArray = ["10", "40", "120"].
I have got theses two arrays from API call.
I am displaying both the arrays on the table view & I am searching through the menuNamesArray because I want to search by recipe names.
I am getting recipe names by searching those from menuNameArray. Now I want menuPriceArray to get updated also according to searched menuName Array.
means if I search for "bread" then I must get price value as "40" and accordingly for other names also.
How should I perform such filtering on the second array?
My code Snippet -
//MARK: UISearch result update delegate
func updateSearchResults(for searchController: UISearchController) {
// here arrFilter is the resulting array to sotre searched items from menuNamesArray
arrFilter.removeAll(keepingCapacity: false)
let searchPredicate = NSPredicate(format: "SELF CONTAINS[c] %#", searchController.searchBar.text!)
let array = (menuNamesArray as NSArray).filtered(using: searchPredicate)
let result = menuPriceArray.firstIndex(of: array.startIndex)
arrFilter = array as! [String]
self.tblSearch.reloadData()
//here now I got the searched menu names, Now I want prices for searched menu names from menuPrice Array..
}
Never use multiple arrays as data source.
Swift is an object oriented language. Take advantage of it.
Create a struct
struct Menu {
let name : String
let price : Double
}
and a data source array
var menues = [Menu]()
Filter the array by name and get the price, pretty easy and straightforward
if let foundMenu = menues.filter(where: {$0.name == "bread"}) {
print(foundMenu.price)
}
You can merge the two arrays into one array of dictionary elements.

Is it possible to turn an array to a Result<> in Realm?

I use the following query in my app to filter Folders where the categories (array) id equals the one the user selected (categoryId)
func getFoldersForCategory(_ categoryId:Int, sorting:String) -> Results<Folder> {
let realm = try! Realm()
let realmObjects = realm.objects(Folder.self).sorted(byKeyPath: "producerName", ascending: true).filter("toDate >= %# AND language == %#", getCurrentLocalDateWithoutTimeStamp(), ApplicationSettingsRealm().getCurrentLanguage())
return realmObjects.filter({ $0.categories.filter({ $0.id == categoryId }) != [] })
}
The above code used to work when I returned an array of [Folder] (hence getFoldersForCategory(_ categoryId:Int, sorting:String) -> [Folder].
I recently wanted to change the return to the results from the Realm query.
Now my code isn't working anymore, because I can't filter this way with Results<Folder>. The error I receive is
Unable to infer closure type in the current context
So I was wondering if there is a way that I can convert the last line of code from an array to a Realm Results<>
Thanks!
No, you cannot. However, assuming that categories is a Realm List, you can simply include that filter in the query rather than converting it to an array:
func getFoldersForCategory(_ categoryId:Int, sorting:String) -> Results<Folder> {
let realm = try! Realm()
return realm.objects(Folder.self)
.sorted(byKeyPath: "producerName", ascending: true)
.filter("toDate >= %# AND language == %# AND ANY categories.id == %#",
getCurrentLocalDateWithoutTimeStamp(),
ApplicationSettingsRealm().getCurrentLanguage(),
categoryID)
}

Swift: Accessing array value in array of dictionaries

I am currently struggling with obtaining a value from an array inside an array of dictionaries. Basically I want to grab the first "[0]" from an array stored inside an array of dictionaries. This is basically what I have:
var array = [[String:Any]]()
var hobbies:[String] = []
var dict = [String:Any]()
viewDidLoad Code:
dict["Name"] = "Andreas"
hobbies.append("Football", "Programming")
dict["Hobbies"] = hobbies
array.append(dict)
/// - However, I can only display the name, with the following code:
var name = array[0]["Name"] as! String
But I want to be able to display the first value in the array stored with the name, as well. How is this possible?
And yes; I know there's other options for this approach, but these values are coming from Firebase (child paths) - but I just need to find a way to display the array inside the array of dictionaries.
Thanks in advance.
If you know "Hobbies" is a valid key and its dictionary value is an array of String, then you can directly access the first item in that array with:
let hobby = (array[0]["Hobbies"] as! [String])[0]
but this will crash if "Hobbies" isn't a valid key or if the value isn't [String].
A safer way to access the array would be:
if let hobbies = array[0]["Hobbies"] as? [String] {
print(hobbies[0])
}
If you use a model class/struct things get easier
Given this model struct
struct Person {
let name: String
var hobbies: [String]
}
And this dictionary
var persons = [String:Person]()
This is how you put a person into the dictionary
let andreas = Person(name: "Andreas", hobbies: ["Football", "Programming"])
persons[andreas.name] = Andreas
And this is how you do retrieve it
let aPerson = persons["Andreas"]

PFObject Array Sort

I'm using Parse and I have an array of PFObjects called "scorecardData". Each PFObject has a "score" property that is of type Int. I'm trying to sort my array by "score" but I'm getting the following error: "Binary operator '<' cannot be applied to two 'AnyObject?' operands". I'm not sure what I'm doing wrong here. I also tried down casting the objectForKey("score") as! Int but its not letting me do this. Any suggestions? Thanks in advance.
var scorecardData = [PFObject]()
scorecardData.sortInPlace({$0.objectForKey("score") < $1.objectForKey("score")})
You declared scorecardData variable as Array of PFObject. Why are you trying access PFObject property using objectForKey: reserved? Anyway I am not parse expert. But if you declared your array as [PFObject] you can use:
scorecardData.sortInPlace({$0.score < $1.score})
But this won't work unless you subclass PFObject for a more native object-oriented class structure. If you do that remember also to specify:
var scorecardData = [YOUR_NEW_CLASS]()
I strongly recommend subclassing PFObject to make use of all swift type-safe goodies.
But if you want to keep your data structure you can use:
scorecardData.sortInPlace({($0["score"] as! Int) < ($1["score"] as! Int)})
Keep in mind that it's dangerous, and in future avoid it.
If you want to Sort your array of PFOject... You can do this
extension Array where Element:PFObject {
func sort() -> [PFObject] {
return sort { (first, second) -> Bool in
let firstDate = first.objectForKey("time") as! NSDate//objectForKey(Constants.Parse.Fields.User.fullName) as? String
let secondDate = second.objectForKey("time") as! NSDate//objectForKey(Constants.Parse.Fields.User.fullName) as? String
return firstDate.compare(secondDate) == .OrderedAscending
}
}
}
Have you tried doing this?
var query = PFQuery(className:"ScoreCard")
// Sorts the results in ascending order by the score field
query.orderByDescending("score")
query.findObjectsInBackgroundWithBlock {

Persist Array of Images Swift

I want to be able to save an array, cardImages, that contains UIImages via SwiftyUserDefaults.
Desired Behavior
Here is the exact desired behavior:
Save an array of UIImages to NSUserDefaults via the SwiftyUserDefault library
Retrieve the images later
Code This is stripped down to very little code
var newPhotoKey = DefaultsKey<NSArray>("image")//Setting up the SwiftyUserDefaults Persisted Array
cardImages = [(UIImage(named: "MyImageName.jpg")!)] //This array contains the default value, and will fill up with more
Defaults[theKeyForStoringThisArray] = cardImages //This is the persisted array in which the array full of images should be stored. WHERE THE ERROR HAPPENS
var arrayToRetreiveWith = Defaults[theKeyForStoringThisArray] as! [UIImage] //To Retreive
Error
I get the following error:
Attempt to set a non-property-list object (
", {300, 300}"
) as an NSUserDefaults/CFPreferences value for key image
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object (
", {300, 300}"
) for key image'
Thanks!
The error message is clear actually. UIImage is not a propertylist so you need to change it to a row data first. I'll put example below but FYI saving big data like images using NSUserDefaults is really not recommended. I'd use NSFileManager and put it in the user documents directory. anyway
var newPhotoKey = DefaultsKey<NSArray>("image")
cardImages = [(UIImage(named: "MyImageName.jpg")!)]
var cardImagesRowdataArray: NSData = []
for image in cardImages {
let imageData = UIImageJPEGRepresentation(image, 1.0)
cardImagesRowdataArray.append(imageData)
}
Defaults[theKeyForStoringThisArray] = cardImagesRowdataArray
var arrayToRetreiveWith = Defaults[theKeyForStoringThisArray] as! [NSData]
// here you can use UIImage(data: data) to get it back
If you don't insist on using SwiftyUserDefaults, you can save it in the user documents directory, here is how to do it
func saveImage(image: UIImage){
if let imageData = UIImageJPEGRepresentation(image, 1.0) {
let manager = NSFileManager()
if let docUrl = manager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first{
let uniqueName = NSDate.timeIntervalSinceReferenceDate()
let url = docUrl.URLByAppendingPathComponent("\(uniqueName).jpg")
imageData.writeToURL(url, atomically: true)
}
}
}
The value of a user default must be a property list. A property list is
a string (String or NSString),
an NSData,
a date (NSDate),
a number (NSNumber),
a boolean (also NSNumber),
an array of property lists,
or a dictionary whose keys are strings and whose values are property lists.
A UIImage is none of those, so a UIImage is not a property list and cannot be part of a property list.
You need to convert your image to an NSData to store it in a user default. Since a UIImage contains some properties (like scale and imageOrientation) in addition to raw pixel data, the easiest way to convert a UIImage to an NSData with no loss is by creating an archive:
let cardImage: UIImage? = someImage()
let cardImageArchive: NSData = NSKeyedArchiver.archivedDataWithRootObject(cardImage!)
Now you can store cardImageArchive in a larger property list that you can store as a user default.
Later, when you need to recreate the image from the data, do this:
let cardImageArchive: NSData = dataFromUserDefaults()
let cardImage: UIImage = NSKeyedUnarchiver.unarchiveObjectWithData(cardImageArchive) as! UIImage

Resources