Search NSArray of NSDictionary. How to find using NSPredicate? - ios

I have an NSArray. It has one or more NSDictionary in each index. Based on the search input, I want to check whether it contain the value in contact_Label inside contact_detail dictionary. It will look like this:
(
{
"contact_detail" = {
"contact_is_in_phone" = 1;
"contact_Label" = "Tyler Globussoft";
"contact_displayname" = "Suzan Arohh";
},
"last_msg_details" = {
.....
};
},
{
}
);
I have tired like this. But not getting the result.
NSArray *contacts = self.dataArray; //your array of NSDictionary objects
NSPredicate *filter = [NSPredicate predicateWithFormat:#"contact_Label = %#",stringValue];
NSArray *filteredContacts = [contacts filteredArrayUsingPredicate:filter];

You can use
NSArray *contacts = self.dataArray; //your array of NSDictionary objects
NSPredicate *filter = [NSPredicate predicateWithFormat:#"contact_detail.contact_Label = %#",stringValue];
NSArray *filteredContacts = [contacts filteredArrayUsingPredicate:filter];
Happy coding...

Related

Searching an Array of Strings in Array of Dictionaries

I have an array of combinations which needs to be searched in another array of dictionaries
Array Of Dictionaries is as follows:
self.listOfAllContacts
({
name = "William";
recordId = 541;
},
{
name = "Soan";
recordId = 541;
},
{
name = "kamal";
recordId = 541;
},
{
name = "Elisia";
recordId = 541;
},
{
name = "Ben";
recordId = 541;
},
{
name = "Loki";
recordId = 541;
},
{
name = "Fraser";
recordId = 541;
});
Array Of Combinations are as follows : array named as
self.arrayOfSearchCombinationsFormed
<__NSArrayM 0x1702518b0>(
ABCD,
JK,
AND,
MIKE,
ELI,
STEV,
FRASE,
WIILIA
)
Present Code in work:
self.filteredContacts = [[NSMutableArray alloc] init];
NSArray *arrayToTraversed = [[NSArray alloc] initWithArray:self.arrayOfSearchCombinationsFormed];
for(NSString *combination in arrayToTraversed){
NSPredicate *predicateInsideLoop = [NSPredicate predicateWithFormat:#"name CONTAINS[cd] %#", combination];
NSArray *filteredContactByName = [self.listOfAllContacts filteredArrayUsingPredicate:predicateInsideLoop];
if([filteredContactByName count]>0){
[self.filteredContacts addObjectsFromArray:filteredContactByName];
}
else{
[self.arrayOfSearchCombinationsFormed removeObject:combination];
}
}
Presently this solution is inefficient and consuming a lot of memory.
Any help would be appreciated.
Also note that any combination not found in the dictionary needs to be removed from the combinations array.
So my question is that i want the most efficient way of searching the names in terms of memory allocation. So that it uses minimum memory.
It might be helpful to use (NSPredicate*)predicateWithBlock: method to speed up searching.
Suppose you have a keys array and a source array, you want to filter the source array with the keys array.
NSArray *keysArray = #[#"1",#"2",#"3"];
NSArray *sourceArray = #[#"12",#"2",#"3",#"1",#"2"];
For the first object #"12" in sourceArray, looking at the keysArray, since #"12" contains #"1", you can stop filtering and keep the first object of both arrays. But original code uses #"1" to filter the sourceArray, result is #"12" and #"1", each element needs to be checked.
You can refer to the below code:
- (void)searchWithBlock:(NSArray*)keysArray
{
NSDate *beginDate = [NSDate date];
NSMutableSet *keySet = [NSMutableSet set];
NSPredicate *intersectPredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
for (NSString *str in keysArray) {
NSString *name = evaluatedObject[#"name"];
NSRange r = [name rangeOfString:str options:NSCaseInsensitiveSearch];
if (r.location != NSNotFound) {
[keySet addObject:str];
return true;
}
}
return false;
}];
NSArray *intersect = [self.listOfAllContacts filteredArrayUsingPredicate:intersectPredicate];
self.filteredContacts = [[NSMutableArray alloc] initWithArray:intersect];
self.arrayOfSearchCombinationsFormed = [NSMutableArray arrayWithArray:[keySet allObjects]];
NSDate *endDate = [NSDate date];
NSTimeInterval interval = [endDate timeIntervalSinceDate:beginDate];
NSLog(#"interval is %f",interval);
NSLog(#"intersect %#\n, filtered key array is %#\n", intersect,keySet);
}
It needs about 1/3 of the original time for filtering, memory allocation is a little bit less. I suggest you split the larger data source to smaller chunks to use less memory.
This should do the trick:
NSString *sourceRegexp =
[NSString stringWithFormat:#".*%#.*",
[combinations componentsJoinedByString:#".*|.*"]];
NSPredicate *sourcePredicate =
[NSPredicate predicateWithFormat:#"name MATCHES[c] %#", sourceRegexp];
NSArray *filteredSource =
[source filteredArrayUsingPredicate:sourcePredicate];
NSPredicate *combinationsPredicate =
[NSPredicate predicateWithFormat:
#"SUBQUERY(%#, $s, $s.name CONTAINS[c] SELF).#count > 0",
filteredSource];
NSArray *filteredCombinations =
[combinations filteredArrayUsingPredicate:combinationsPredicate];
I may have misunderstood the question, but wouldn't using an NSPredicate with a set work?
NSSet *contactsToSearchFor = [NSSet setWithArray:self.arrayOfSearchCombinationsFormed];
NSPredicate *prediate = [NSPredicate predicateWithFormat:#"name IN[cd] %#", contactsToSearchFor];
NSArray *results = [self.listOfAllContacts filteredArrayUsingPredicate:predicate];
I haven't tested this in XCode, but it should work.
Why not implement a binary search algorithm to search array.
The link provided below gives you full details on how to implement binary search.
See: http://oleb.net/blog/2013/07/nsarray-binary-search/
I would recommend you to use swift for this purposes: it is much faster and allocates much less memory. Here is a solution in Swift:
func filterContacts(contacts: [Dictionary<String, String>], searchCombinations: [String]) -> [Dictionary<String, String>]{
return contacts.filter { dict in
let name = dict["name"]!
for string in searchCombinations{
if name.rangeOfString(string) != nil { return true }
}
return false
}
}
Another much more complex solution would involve using Suffix Tree for storing your contacts data if duration of searching is important.

How can I extract only the Twitter id's from this json data?

I am trying to store only the Twitter id's from the following json data:
(
{
id = chuckschumer;
type = Facebook;
},
{
id = SenSchumer;
type = Twitter;
},
{
id = SenatorSchumer;
type = YouTube;
}
),
I am working in objective-C. How would I pull only the id's that have a type = Twitter? Any advice is appreciated.
I then attempted to use NSPredicate but my filtered array continues to return as nil. Here is my code:
NSPredicate *filter = [NSPredicate predicateWithFormat:#"(type = 'Twitter')"];
NSArray *filteredItems = [channels filteredArrayUsingPredicate:filter];
NSLog(#"%#", filteredItems);
filteredItems continues to return nil, not sure why
To start, get just the items that have type=Twitter:
NSPredicate *filter = [NSPredicate predicateWithFormat:#"(type = 'Twitter')"];
NSArray *filteredItems = [items filteredArrayUsingPredicate:filter];
Then you can iterate through the items to pull the id...
(I just saw this as a related answer, haven't tried it though)
NSArray *idArray = [filteredItems valueForKey: #"id"];

NSPredicate for array of dictionaries, and the dict has a value of array

I have an array with dictionaries. The dictionary has a value of array, like:
{ #"categories" : [#"equipment", #"weapon", #"sword"] }
I want to find out all the dictionaries in that array whose categories contains weapon, I tried:
NSString *category = #"weapon";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.categories contains %#", category];
NSArray *resultArray = [dictionaryArray filteredArrayUsingPredicate:predicate];
But resultArray is empty.
How can I do it right?
You can try this,
NSString *category = #"weapon";
NSArray *items = #[#{ #"categories" : #[#"equipment", #"weapon", #"sword"] },
#{ #"categories" : #[#"toys", #"weapon", #"sword"] },
#{ #"categories" : #[#"arrow", #"bow", #"sword"] }];
NSPredicate*predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
NSArray *categories = [evaluatedObject objectForKey:#"categories"];
return [categories containsObject:category];
}];
NSArray *filteredArray = [items filteredArrayUsingPredicate:predicate];
In my case it outputs like,
(
{
categories = (
equipment,
weapon,
sword
);
},
{
categories = (
toys,
weapon,
sword
);
}

Merge an array into a dictionary

I am trying to take an array and merge it into an array of dictionaries but unsure as to how to do it.
I have an array of dictionaries that looks like this:
(
{
caption = a;
urlRep = "12";
},
{
caption = b;
urlRep = "34";
},
{
caption = c;
urlRep = "56";
}
)
and given an array like this:
(12,34,56,78)
I want to merge it into my dictionaries to make it look like this:
(
{
caption = a;
urlRep = "12";
},
{
caption = b;
urlRep = "34";
},
{
caption = c;
urlRep = "56";
},
{
caption = "";
urlRep = "78";
}
)
edit:
I need to also consider removing from the array of dicts if the given array does not contain one of the urlReps.
Any help would be greatly appreciated as I've been stuck trying to figure this out for some time.
Here's a simple, efficient and elegant solution using NSSets to handle unique keys:
NSMutableArray *arrayOfDicts; // your input array of dictionaries
NSArray *urlRepArray; // the new array with string elements
// create a set of potentially new keys (urlReps)
NSMutableSet *urlReps = [NSMutableSet setWithArray:urlRepArray];
// remove existing keys from your original array
[urlReps minusSet:[NSSet setWithArray:[arrayOfDicts valueForKey:#"urlRep"]]];
// merge new dicts to the original array
for (id urlRep in urlReps)
[arrayOfDicts addObject:#{ #"urlRep" : urlRep, #"caption" : #"" }];
Easiest way AFAIK, Filter using valueForKeyPath
//Your array of dictionary I created here for debugging purpose.
NSArray *tmpArray = #[ #{#"caption":#"a",#"urlRep":#"12"},
#{#"caption":#"b",#"urlRep":#"34"},
#{#"caption":#"c",#"urlRep":#"56"}];
//This will give you 12,34,56 in your case
NSArray *existingURLRep = [tmpArray valueForKeyPath:#"urlRep"];
NSMutableArray *targetArray = [[NSMutableArray alloc] initWithObjects:#12, #34,#56, #78, nil]; //Assuming you have your array as you said
[targetArray removeObjectsInArray:existingURLRep];
//remove existing items you will have 78 here now loop through
//this targetArray and add it to your array of dictionary.
(void)filterArray{
NSLog(#"Array before filtering = %#",initialArray);
NSLog(#"given Array = %#",givenArray);
NSMutableSet *urlReps = [NSMutableSet setWithArray:givenArray];
// remove existing records
[urlReps minusSet:[NSSet setWithArray:[initialArray valueForKey:#"urlRep"]]];
// adding new objects
for (id obj in urlReps) {
[initialArray addObject:#{#"caption":#"", #"urlRep" : obj}];
}
// removing objects
NSMutableSet *set = [[NSMutableSet alloc] init];
for (id obj in initialArray) {
NSDictionary *dict = (NSDictionary *)obj;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"self = %#", dict[#"urlRep"]];
NSArray *filteredArray = [givenArray filteredArrayUsingPredicate:predicate];
if(filteredArray.count == 0) {
[set addObject:dict];
}
}
[initialArray removeObjectsInArray:[set allObjects]];
NSLog(#"Array after filtering = %#",initialArray);
}
NSMutableArray *yourArray;//This will be your original array of dictionary.
NSArray *newArray;//This is your new array which you want to add.
for(id obj in newArray) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"urlRep = %#", id];
NSArray *filteredArray = [locationsArray filteredArrayUsingPredicate:predicate];
if(filteredArray.count == 0) {
[yourArray addObject:#{#"caption":#"", #"urlRep" : id}];
}
}
/*
NSArray *inputArray;//(12,34,56,78)- I assumes you are having array which contains strings. If you are having number then modify the code as you needed
NSMutableArray *colloectionArray;// your total collection
NSMutableArray *tobeMerged;
*/
// Extract the dictionary set only to be merged
for (NSString* aNumber in inputArray) {
for (NSDictionary *aItem in colloectionArray) {
NSString *urlRep= [aItem valueForKey:#"urlRep"];
if (![urlRep isEqualToString:aNumber]) {
[tobeMerged addObject:urlRep];
}
}
}
// Add missed items in collection
for (NSString *aNumber in tobeMerged) {
NSMutableDictionary *newset = [[NSMutableDictionary alloc]init];
[newset setObject:#"" forKey:#"caption"];
[newset setObject:aNumber forKey:#"urlRep"];
[colloectionArray addObject:newset];
}

Filter NSArray to make new NSArray for use on tableview methods

I have a NSArray that looks like this:
{"result":
[
{
"epoch":"1371333600"
},
{
"epoch":"1371420000"
},
{
"epoch":"1371333600"
}
]
}
I want to sort the NSArray and make a new one so i can use it easier with the tableview methods to count the sections and rows.
All the dates that are the same need to be in one section.
The array should look like the example below but i don’t know how to get there. I have tried NSPredicate and used a loop but it won’t work.
What i want:
{"result":
[
{"data":
[
{
"epoch":"1371333600"
},
{
"epoch":"1371333600"
}
]
},
{"data":
[
{
"epoch":"1371420000"
}
]
}
]
}
My NSPredicate looks like this, but does not give me the result.
_finalArray = [[NSMutableArray alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"epoch IN %#", [_resultArray valueForKey:#"epoch"]];
_predicateDate = [NSMutableArray arrayWithArray:[dataSortArray filteredArrayUsingPredicate:predicate]];
if ([_predicateDate count] != 0)
{
NSDictionary *itemsArrayDict = [NSDictionary dictionaryWithObject:_predicateDate forKey:#"data"];
[_finalArray addObject:itemsArrayDict];
}
NSOrderedSet is awesome for this occasion as it allows you to get the unique strings.
NSDictionary *dict1 = [NSDictionary dictionaryWithObject:#"2222222" forKey:#"epoch"];
NSDictionary *dict2 = [NSDictionary dictionaryWithObject:#"2222222" forKey:#"epoch"];
NSDictionary *dict3 = [NSDictionary dictionaryWithObject:#"1111111" forKey:#"epoch"];
NSArray *dictArray = #[dict1, dict2, dict3];
NSMutableArray *finalArray = [[NSMutableArray alloc]init];
NSArray *epoches = [dictArray valueForKey:#"epoch"];
NSOrderedSet *orderedSet = [NSOrderedSet orderedSetWithArray:epoches];
for (NSString *string in orderedSet) {
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"epoch == %#", string];
NSArray *resultsArray = [dictArray filteredArrayUsingPredicate:predicate];
[finalArray addObject:resultsArray];
}
Hi you can use the NSPredicate to filter an array like:
//NSPredicate to filter an array
NSArray *data = [NSArray arrayWithObject:[NSMutableDictionary dictionaryWithObject:#"hello" forKey:#"Test"]];
NSArray *filtered = [data filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(Test == %#)", #"hello"]];
Thanks
It appears that you are using dictionaries along with arrays. Here's what I've achieved:
(result
(data
{
epoch = 1371333600;
},
{
epoch = 1371333600;
}
),
(data
{
epoch = 1371420000;
}
)
)
I'm not using predicates, but it looks as it's working :). Here is the code:
// Initial input
NSArray *result = #[#{#"epoch":#"1371333600"},#{#"epoch":#"1371420000"},#{#"epoch":#"1371333600"}];
// Get unique values for the input
NSSet *resultSet = [NSSet setWithArray:result];
// Here we are going to store the final result
NSMutableArray *newResult = [[NSMutableArray alloc] init];
// Loop over the unique items
for (NSDictionary *uniqueItem in resultSet) {
// Here we are going to store the grouped data
NSMutableArray *dataResult = [[NSMutableArray alloc] init];
// Loop over the initial input
for (NSDictionary *resultItem in result) {
// Search for all the items that are equal to the uniqe
// I would rather include a count instead of repeating values :)
if([uniqueItem isEqual:resultItem]) {
[dataResult addObject:resultItem];
}
}
[newResult addObject:dataResult];
}
NSLog(#"%#", newResult);
Cheers!

Resources