Swift filter array using NSPredicate - ios

I have an application written in Swift that is pulling in the users contacts from their address book.
I want to filter out the contact that only contain a company name (so that you get your "assumed" real person contact and not businesses)
Here is how this is being accomplish in the Objective-C version of my app:
NSArray *allContacts = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id person, NSDictionary *bindings) {
NSString *firstName = CFBridgingRelease(ABRecordCopyValue((__bridge ABRecordRef)person, kABPersonFirstNameProperty));
NSString *lastName = CFBridgingRelease(ABRecordCopyValue((__bridge ABRecordRef)person, kABPersonLastNameProperty));
return (firstName || lastName);
}];
NSArray *peopleNotCompanies = [allContacts filteredArrayUsingPredicate:predicate];
This works perfectly, so here is my attempt to do this in Swift:
var contactList: NSArray = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue()
var predicate: NSPredicate = NSPredicate { (AnyObject person, NSDictionary bindings) -> Bool in
var firstName: String = ABRecordCopyValue(person as ABRecordRef, kABPersonFirstNameProperty).takeRetainedValue() as String
var lastName: String = ABRecordCopyValue(person as ABRecordRef, kABPersonLastNameProperty).takeRetainedValue() as String
return firstName || lastName
})
Now this has a couple problems. I am getting these errors on the return statement and the end of the predicate call:
How can I provide similar functionality found in my ObjC code in Swift? Or is there a better way in swift to check if a contact has ONLY a company name and then omit it from the final array?
Thanks!

If you have firstName and lastName be optional strings, you can compare them against nil and use them in a boolean expression.
Your second error is due to the extra paren after your closure. This code should work.
var predicate: NSPredicate = NSPredicate { (AnyObject person, NSDictionary bindings) -> Bool in
var firstName: String? = ABRecordCopyValue(person as ABRecordRef, kABPersonFirstNameProperty).takeRetainedValue() as? String
var lastName: String? = ABRecordCopyValue(person as ABRecordRef, kABPersonLastNameProperty).takeRetainedValue() as? String
return firstName != nil || lastName != nil
}

If you convert the NSArray into a Swift Array, you can use Swift's Array.filter method. Here's an example with simpler objects for clarity:
let arrObjc: NSArray = ["aaa", "bab", "bbb", "baa", "cbc"]
let arr: [AnyObject] = arrObjc //create a swift array with same data
// filter takes a block that returns a boolean. True: keep the item, False: drop it.
arr.filter{
if let s = $0 as? String { // first try to cast AnyObject to our expected type.
return contains(s, "a") // return true if we want this item, false otherwise.
} else {
return false // this object is of the wrong type, return false.
}
}
// returns: ["aaa", "bab", "baa"]

Your test at the last line in Objective-C will return if firstName is nil or if lastName is nil. In your swift code, you are just trying to compare two strings as if they were Bools. Instead, you want to return if firstName exists, so you should instead do this in both your objective-c and Swift code:
return firstName != nil

Related

Replace an Dictionary object in Array using NSPredicate

I have one dictionary object in Array.
I want to replace this object with new dictionary.
Both have same order_id.
Currently I am doing it like, How can I do it with NSPredicate.
NSMutableArray *orderList=[[NSUserDefaults standardUserDefaults] objectForKey:#"Orders"];
//2. Find and replace the object/OrdeDetails.
for(int i=0;i<orderList.count;i++){
NSDictionary *dictionary=orderList[i];
if([dictionary[#"order_id"] isEqualToString:OrderDetails[#"order_id"]]){
[orderList replaceObjectAtIndex:i withObject:OrderDetails];
break;
}
}
Check this code:
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF.order_id contains[cd] %#",OrderDetails[#"order_id"]];
NSMutableArray *arrOrders = [[NSMutableArray alloc]init];
[arrOrders addObjectsFromArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"Orders"]];
NSArray *filteredOrder = [arrOrders filteredArrayUsingPredicate:resultPredicate];
if ([filteredOrder count] > 0) {
NSUInteger index = [arrOrders indexOfObject:[filteredOrder objectAtIndex:0]];
if (index != NSNotFound) {
[arrOrders replaceObjectAtIndex:index withObject:OrderDetails];
}
}
You cannot replace an object with NSPredicate you can however search for it then do the replacement afterwards.
Well just made this up without testing but I do think it has a valid syntax.
You can use this as the foundation or hope you get the logic in using the predicate.
// construct the predicate with the given condition
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"order_id = %#",dictionary[#"order_id"]];
// this will filter the array according to the given predicate. if there are more than 1 entries with the given condition then you should handle that this only handle unique entries
NSArray *array = [orderList filteredArrayUsingPredicate:predicate];
// assuming that order_id is unique
NSInteger index = [orderList indexOfObject:[array lastObject]];
if (index != NSNotFound) // check if index is existing
[orderList replaceObjectAtIndex:index withObject:orderDetails]; // replace the object with the desired object
If I want to match perfect then I used, LIKE. Although I am not sure.
Got from the linkcheat sheet
NSPredicate *predicateSearchOrder=[NSPredicate predicateWithFormat:#"SELF.order_id LIKE[cd] %#",[responsedDict valueForKey:#"order_id"]];
//
// //It will be only one object with order id.
NSArray *searchedArray=[orderList filteredArrayUsingPredicate:predicateSearchOrder];
if(searchedArray.count>0){
NSDictionary *toModiFyDictionary=searchedArray[0];
toModiFyDictionary=OrderDetails;
}
This is also working
If you were using Swift, It's very simple.
public class Test : NSObject {
class func test() -> Void {
var array : [Dictionary<String, String>] = []
let dic: Dictionary<String, String> = ["order_id" : "111", "name" : "good1"]
let dic2: Dictionary<String, String> = ["order_id" : "222", "name" : "good2"]
let dic3: Dictionary<String, String> = ["order_id" : "111", "name" : "good3"]
array.append(dic)
array.append(dic2)
let result = array.map { (elem) -> [String : String] in
if elem["order_id"] == "111" {
return dic3
}
return elem
}
print("result = \(result)")
}
}

What is the best way to print a log of object with nested NSDictionary or NSArray with decoded unicode symbols?

I look for the best way to print a log of nested objects of NSArray and NSDictionary with decoded unicode symbols.
I know about description property, but it prints unicode symbols in \Uxxxx format. Also I know, that it possible to enumerate object and print it's keys separately, but it's not comfortable for big nested objects.
For example:
NSArray *array = #[#{#"firstName":#"Марк"},#{#"lastName":#"Цекурберг"}];
NSLog(#"array %#", array);
===
array (
{
firstName = "\U041c\U0430\U0440\U043a";
},
{
lastName = "\U0426\U0435\U043a\U0443\U0440\U0431\U0435\U0440\U0433";
}
)
I want to get this:
array (
{
firstName = "Марк";
},
{
lastName = "Цукерберг";
}
)
I wrote for myself script, that takes description string of object and replaces all \Uxxxx (and %xx%xx) to approptiate symbols, but I think it's not the best way.
This is my Objective C script (NSObject extension), that do this: https://github.com/iOS-altima/dumpObject
Standard way:
NSArray *array = #[#{#"firstName":#"Марк%20%F0%9F%98%9C"},#{#"lastName":#"Цекурберг"}];
NSLog(#"array %#", array);
=== result ===
array (
{
firstName = "\U041c\U0430\U0440\U043a%20%F0%9F%98%9C";
},
{
lastName = "\U0426\U0435\U043a\U0443\U0440\U0431\U0435\U0440\U0433";
}
)
dumpObject way:
NSArray *array = #[#{#"firstName":#"Марк%20%F0%9F%98%9C"},#{#"lastName":#"Цекурберг"}];
[self dumpObject:array];
=== result ===
array (
{
firstName = "Марк 😜";
},
{
lastName = "Цукерберг";
}
)

IOS Sort NSDictionary with given array order

I have a dictionary with objects and keys. I want to sort that dictionary using the keys
dict = [name : abc, city: xyz, date :xys, location: mnq ...]
for example i want to pass like
array = [name, location, city, date]
i want to sort the dicionary like
dict = [name : abc, location: mnq, city: xyz,date: xys ...]
It should sort dicationay,as per my passing array values
not like alphabet or allkeys or allValues order
NSArray *newkey = [[NSArray alloc] initWithObjects:#"name",#"location", #"city", #"date", nil];
int index = 0;
for (id key in dictionary) {
NSLog(#"key: %#, value: %#", [newkey objectAtIndex:index], [dictionary objectForKey:[newkey objectAtIndex:index]]);
index++;
}
u can't sort dictionary ... make 2D array with key - value pairs and sort them afterwards with .sort() function
var dict : NSMutableDictionary = NSMutableDictionary()
dict.setObject("damu", forKey: "name")
dict.setObject("bng", forKey: "city")
dict.setObject("sakha", forKey: "office")
dict.setObject("apple", forKey: "technology")
// Searching array
keysArray = ["name","city","office","tech"]
// returning Touple
var dictTouples = returnTouppleWithDictionary(dict, sortWithArray: keysArray)
print("\n\n Soreted as keys: \(dictTouples.allKeys) Values \(dictTouples.allValues) ")
// Touple
func returnTouppleWithDictionary(dict : NSDictionary, sortWithArray: NSArray) ->(allKeys:NSArray, allValues: NSArray){
var arrayKeys : NSMutableArray = NSMutableArray()
var arrayValues : NSMutableArray = NSMutableArray()
for i in 0..<keysArray.count{
var key : NSString = keysArray[i] as! String
var name: NSString? = dict.objectForKey(key) as! String;
arrayKeys.addObject(key)
arrayValues.addObject(name!)
}
return(arrayKeys,arrayValues)
}

Access email in EKParticipant/EKAttendee

I want to get the email address of the attendee of an event in the EKEventKit.
I have the following code:
if ( event.attendees.count > 0)
{
NSArray *people = event.attendees;
for(EKParticipant *person in people)
{
if ( person.participantType == EKParticipantTypePerson && person.URL.resourceSpecifier.length > 0)
{
NSString *dataString = [NSString stringWithFormat:#"event_id=%ld&name=%#&is_me=%d&email=%#&role=%#",event_id,person.name, person.isCurrentUser,person.URL.resourceSpecifier, #"attendee"];
//<DO SOMETHING USEFUL WITH dataString>;
}
}
}
When I run the code person populates with the following data:
EKAttendee <0x17809acc0> {UUID = 4F657EA4-452A-412B-A9AA-FEC5551DC096; name = A. Real Person; email = realperson#therightdomain.com; status = 0; role = 0; type = 1}
How to I access the email field?
I tried (as above) to use URL.resourceSpecifier, but that frequently is some strange string that is definitely NOT an email address.
The "Description" of the EKParticipant object is a property list of sorts. I tried several different methods of parsing that list into something containing key:value pairs unsuccessfully. So I wrote the following:
// This is re-useable code that converts any class description field into a dictionary that can be parsed for info
NSMutableDictionary *descriptionData = [NSMutableDictionary dictionary];
for (NSString *pairString in [person.description componentsSeparatedByString:#";"])
{
NSArray *pair = [pairString componentsSeparatedByString:#"="];
if ( [pair count] != 2)
continue;
[descriptionData setObject:[[pair objectAtIndex:1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] forKey:[[pair objectAtIndex:0]stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
With this I simply get the email address with
[descriptionData valueForKey:#"email"]
I tried to answer this same question in "how to get ekevent EKparticipant email?" thread:
What you need to do is use EKPrincipal:ABRecordWithAddressBook and then extract email from there. Like this:
NSString *email = nil;
ABAddressBookRef book = ABAddressBookCreateWithOptions(nil, nil);
ABRecordRef record = [self.appleParticipant ABRecordWithAddressBook:book];
if (record) {
ABMultiValueRef value = ABRecordCopyValue(record, kABPersonEmailProperty);
if (value
&& ABMultiValueGetCount(value) > 0) {
email = (__bridge id)ABMultiValueCopyValueAtIndex(value, 0);
}
}
Note that calling ABAddressBookCreateWithOptions is expensive so you might want to do that only once per session.
If you can't access the record, then fall back on URL.resourceSpecifier.
In Swift 4:
private static func getParticipantDescriptionData(_ participant: EKParticipant) -> [String:String] {
var descriptionData = [String: String]()
for pairString in participant.description.components(separatedBy: ";") {
let pair = pairString.components(separatedBy: "=")
if pair.count != 2 {
continue
}
descriptionData[pair[0].trimmingCharacters(in: .whitespacesAndNewlines)] =
pair[1].trimmingCharacters(in: .whitespacesAndNewlines)
}
return descriptionData
}

iOS filter array of dictionary base on value and not the key

I have array with dictionaries like this:
Array: (
{
customerUS= {
DisplayName = "level";
InternalName = "Number 2";
NumberValue = 1;
},
customerCAN= {
DisplayName = "PurchaseAmount";
InternalName = "Number 1";
NumberValue = 3500;
};
}
)
I want to filter the dictionaries base on particular value and not the key. For example I want all the dictionaries with values on any key of 3500. Does any body knows how can I do this?
I'll really appreciate your help.
Try a predicate format like:
#"%# IN SELF", testValue
When you filter the array each will be run against the IN. When you pass IN a dictionary it uses the values of the dictionary.
you can also use
-keysOfEntriesPassingTest:
of NSDictionary. Just pass in a block like so:
for(NSDictionary *myDictionary in myArray){
NSSet *resultSet = [myDictionary keysOfEntriesPassingTest:^(id key, id object, BOOL *stop) {
//assuming that 3500 is an int, otherwise use appropriate condition.
if([[NSNumber numberWithInt:object] isEqual:[NSNumber numberWithInt:3500]]){
return YES;
}else{
return NO;
}
}];
if (resultSet.count>0){
//do whatever to myDictionary
}
}

Resources