obj c -get list of indexes in NSArray from NSPredicate - objective-c

I have an array of car and I am filtering it based on objects containing the letter i.
NSMutableArray *cars = [NSMutableArray arrayWithObjects:#"Maruthi",#"Hyundai", #"Ford", #"Benz", #"BMW",#"Toyota",nil];
NSString *stringToSearch = #"i";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF contains[c] %#",stringToSearch]; // if you need case sensitive search avoid '[c]' in the predicate
NSArray *results = [cars filteredArrayUsingPredicate:predicate];
results contains Maruthi,Hyundai. Instead of the elements, I want results to contain the indexes of the elements i.e 0,1.

NSMutableArray *cars = [NSMutableArray arrayWithObjects:#"Maruthi",#"BMW", #"Ford", #"Benz", #"Hyundai",#"Toyota",nil];
NSMutableArray * results = [[NSMutableArray alloc]init];
for(int i = 0;i<cars.count;i++)
{
NSString * obj = [cars objectAtIndex:i];
if([obj rangeOfString:#"i"].location == NSNotFound)
{
NSLog(#"Not Found");
}
else
{
int index = [cars indexOfObject:obj];
[results addObject:[NSNumber numberWithInt:index]];
}
}

Why not use
- (NSIndexSet *)indexesOfObjectsPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
Or similar?
Depending on your search criteria, something like this perhaps?
NSArray *array = #[#"Maruthi",#"Hyundai", #"Ford", #"Benz", #"BMW", #"Toyota"];
NSString *stringToSearch = #"i";
NSIndexSet *set = [array indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
NSString *string = obj;
if (([string rangeOfString:stringToSearch].location == NSNotFound))
{
return NO;
}
return YES;
}];

Related

Filter array of custom objects with a dictionary property

I have an array that holds MyCustomObject objects.
MyCustomObject has 3 properties:
NSString *id;
NSString *name;
NSDictionary *phones;
How do I filter that array by the content of the "phones" property?
All I saw online is:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"phones CONTAINS[c] %#",textField.text];
self.filteredArray = [self.unfilteredArray filteredArrayUsingPredicate:predicate];
But it doesn't help me much...
Thanks
MyCustomObject *value = [[MyCustomObject alloc] init];
for(value in arrayname)
{
NSString *str = [value.phones objectForKey:#"key"];
NSRange r = [str rangeOfString:textField.text options:NSCaseInsensitiveSearch];
if(r.location != NSNotFound)
{
NSLog(#"Match found");
}
}
self.filterArray = [self.unfilteredArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
MyCustomObject *obj = (MyCustomObject*)evaluatedObject;
return ([[[obj.phones objectForKey:#"key"] lowercaseString] rangeOfString:[textField.text lowercaseString]].location != NSNotFound );
}]];

Objective-C: How to find the most common string in an array?

I have an array of strings from an online database that I trying to determine the most commonly used word. The values inside the arrays will vary but I want to check the most common words of whatever collection or words I'm using. If theoretically I had an array of the following...
NSArray *stringArray = [NSArray arrayWithObjects:#"Duck", #"Duck", #"Duck", #"Duck", #"Goose"];
How do I iterate through this array to determine the most common string, which would obviously be "Duck"?
Simplest way is probably NSCountedSet:
NSCountedSet* stringSet = [[NSCountedSet alloc] initWithArray:strings];
NSString* mostCommon = nil;
NSUInteger highestCount = 0;
for(NSString* string in stringSet) {
NSUInteger count = [stringSet countForObject:string];
if(count > highestCount) {
highestCount = count;
mostCommon = string;
}
}
You can use the word as a key into a dictionary.
NSMutableDictionary *words = [NSMutableDictionary dictionary];
for (NSString *word in stringArray) {
if (!words[word]) {
[words setValue:[NSDecimalNumber zero] forKey:word];
}
words[word] = [words[word] decimalNumberByAdding:[NSDecimalNumber one]];
}
Now iterate through words and find the key with the highest value.
NSString *mostCommon;
NSDecimalNumber *curMax = [NSDecimalNumber zero];
for (NSString *key in [words allKeys]) {
if ([words[key] compare:curMax] == NSOrderedDescending) {
mostCommon = key;
curMax = word[key];
}
}
NSLog(#"Most Common Word: %#", mostCommon);
EDIT: Rather than looping through the array once then looping separately through the sorted dictionary, I think we can do better and do it all in a single loop.
NSString *mostCommon;
NSDecimalNumber *curMax = [NSDecimalNumber zero];
NSMutableDictionary *words = [NSMutableDictionary dictionary];
for (NSString *word in stringArray) {
if (!words[word]) {
[words setValue:[NSDecimalNumber zero] forKey:word];
}
words[word] = [words[word] decimalNumberByAdding:[NSDecimalNumber one]];
if ([words[word] compare:curMax] == NSOrderedDescending) {
mostCommon = word;
curMax = words[word];
}
}
NSLog(#"Most Common Word: %#", mostCommon);
This should be significantly faster than my answer pre-edit, though I don't know how it compares to using the NSCountedSet answer.
Try using NSPredicate.
NSUInteger count=0;
NSString *mostCommonStr;
for(NSString *strValue in stringArray) {
NSUInteger countStr=[[stringArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"self MATCHES[CD] %#, strValue]]count];
if(countStr > count) {
count=countStr;
mostCommonStr=strValue;
}
}
NSLog(#"The most commonstr is %#",mostCommonStr);

Creating NSDictionary with keys that have multiple values

I have a mutable array that contains NSDictionary dic1 objects,
each dictionary has a key called contactId, more than one dictionary can have the same value for contactId.
What I want to do is to create an NSDictionary with unique contactIds as the keys and an array value that contains a list of all NSDictionary dic1 objects that have the value contactId equal to the key.
How can I do this?
My data looks like this:
**myArray**:[ **dic1** {contactId = x1 , name = name1 }, **dic2**{contactId = x2, name =
name2 }, **dic3**{contactId = x1, name = name3} ]
I want it to become like this:
**NSDictionary**: { **x1**:[dic1, dic3], **x2**:[dic2] }
Use fast enumeration:
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (id obj in myArray)
{
NSString *contactId = [obj objectForKey:#"contactId"];
NSMutableSet *contacts = [result objectForKey:contactId];
if (!contacts)
{
contacts = [NSMutableSet set]
[result setObject:contacts forKey:contactId];
}
[contacts addObject:obj];
}
You could use blocks for no real added benefit:
__block NSMutableDictionary *result = [NSMutableDictionary dictionary];
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
NSString *contactId = [obj objectForKey:#"contactId"];
NSMutableSet *contacts = [result objectForKey:contactId];
if (!contacts)
{
contacts = [NSMutableSet set]
[result setObject:contacts forKey:contactId];
}
[contacts addObject:obj];
}];
How about the classic way?
NSMutableDictionary* Result;
NSEnumerator* Enumerator;
NSDictionary* Dict;
Result=[[NSMutableDictionary alloc] init];
Enumerator=[YourArray objectEnumerator];
while ((Dict=[Enumerator nextObject])!=nil)
{
NSString* ContactID;
NSMutableSet* Contacts;
ContactID=[Dict objectForKey:#"contactID"];
Contacts=[Result objectForKey:ContactID];
if (Contacts==nil)
{
Contacts=[[NSMutableSet alloc] init];
[Result setObject:Contacts forKey:ContactID];
[Contacts release];
}
[Contacts addObject:Dict];
}
This should create a Result dictionary. I haven't tested (or even compiled) this, though.

How to extract unique objects from multiple arrays

EDIT:
I have two different arrays with some repeated strings and i want to create a new array with the only the unique strings.
For instance, take these two arrays:
NSArray *array1 = [[NSArray alloc] initWithObjects:#"a",#"b",#"c",nil];
NSArray *array2 = [[NSArray alloc] initWithObjects:#"a",#"d",#"c",nil];
// Result should be an array with objects "b", and "d"
// since they are the only two that are not repeated in the other array.
EDIT:
// Your starting arrays
NSArray *array1 = [[NSArray alloc] initWithObjects:#"a",#"b",#"c",nil];
NSArray *array2 = [[NSArray alloc] initWithObjects:#"a",#"d",#"c",nil];
// Create two new arrays that only contain the objects
// which are not in the other array:
NSMutableArray *uniqueElementsInArray1 = [array1 mutableCopy];
[uniqueElementsInArray1 removeObjectsInArray:array2];
NSMutableArray *uniqueElementsInArray2 = [array2 mutableCopy];
[uniqueElementsInArray2 removeObjectsInArray:array1];
// Combine the two arrays.
// Result contains objects #"b" and #"d":
NSArray *result = [uniqueElementsInArray1 arrayByAddingObjectsFromArray:uniqueElementsInArray2];
For this you just declare one another temp NSMutableArray . Retrieve whatever data u have from your original array say objectArray. Check whether the temp array have that or not and put it into the temp array. Just refer following code:
for(NSString *str in objectArray)
{
if(![tempArray containsObject:str])
{
[tempArray addObject:str];
}
}
After this u can continue to use tempArray or put tempArray into objectArray if you want to use objectArray further.I think this should work for you.
You can use NSSet as a filter (think of Venn Diagrams in your head):
NSArray *array1 = #[#1,#2,#3,#4,#2,#3];
NSArray *array2 = #[#3,#4,#5,#6,#4,#6];
NSSet *set1 = [NSSet setWithArray:array1]; // [1,2,3,4]
NSSet *set2 = [NSSet setWithArray:array2]; // [3,4,5,6]
METHOD 1 (my favorite):
NSMutableSet *mSet1 = [set1 mutableCopy];
NSMutableSet *mSet2 = [set2 mutableCopy];
[mSet1 minusSet:set2]; // mSet1 = [1,2]
[mSet2 minusSet:set1]; // mSet2 = [5,6]
[mSet1 unionSet:mSet2]; // mSet1 = [1,2,5,6], only the unique elements.
// Now just put it in an immutable collections with a self-docu name...
NSArray *arrayOfUniqueness = [setOfUniqueElementsOnly allObjects];
METHOD 2 (more explicit test, no need for Venn Diagrams):
NSSet *setOfObjsUniqueTo1 = [set1 objectsPassingTest:^BOOL(id _Nonnull obj, BOOL * _Nonnull stop) {
return ![set2 containsObject:obj];
}]; // [1,2]
NSSet *setOfObjsUniqueTo2 = [set2 objectsPassingTest:^BOOL(id _Nonnull obj, BOOL * _Nonnull stop) {
return ![set1 containsObject:obj];
}]; // [5,6]
NSMutableSet *oneSetToRuleThemAll = [NSMutableSet setWithSet:setOfObjsUniqueTo1];
// [1,2]
[oneSetToRuleThemAll unionSet:setOfObjsUniqueTo2]; // [1,2,5,6]
// Or as an array:
NSArray *anotherArrayOfUniqueness = [oneSetToRuleThemAll allObjects];
METHOD 3 (eschews NSSet, but I would not seat this code opposite the Queen of England at a formal dinner -- it is inelegant):
NSMutableArray *mArray1 = [NSMutableArray new];
NSMutableArray *mArray2 = [NSMutableArray new];
NSIndexSet *uniqueIndexes1 = [array1 indexesOfObjectsPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
return ![array2 containsObject:obj];
}]; // [0,1,4] (b/c #1 and #2 are unique to array1)
[uniqueIndexes1 enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
[mArray1 addObject:array1[idx]];
}]; // #[#1,#2,#2]
NSIndexSet *uniqueIndexes2 = [array2 indexesOfObjectsPassingTest:^BOOL(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
return ![array1 containsObject:obj];
}]; // [2,3,5] (b/c #5 and #6 are unique to array2)
[uniqueIndexes2 enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
[mArray2 addObject:array2[idx]];
}]; // #[#5,#6,#6]
NSArray *unionArray = [array1 arrayByAddingObjectsFromArray:array2];
// #[#1,#2,#2,#5,#6,#6]
NSArray *yetAnotherArrayOfUniqueness = [[NSSet setWithArray:unionArray] allObjects];
// #[#1,#2,#5,#6]
Not the questioner's question, but to get an array with duplicates removed (i.e., where each element is unique), similar magic can be done:
//given...
NSArray *arr1 = #[#"a", #"b", #"c"];
NSArray *arr2 = #[#"b", #"c", #"d"];
//...make a single array to rule them all:
NSArray *temp = [arr1 arrayByAddingObjectsFromArray:arr2];
//[a,b,c,b,c,d]
//Make an NSSet from the two:
NSSet *filterSet = [NSSet setWithArray:temp]; // Set has: a,b,c,d
//Finally, transmogrify that NSSet into an NSArray:
NSArray *arrayOfUniqueness = [filterSet allObjects]; // [a,b,c,d]
As per the Apple Docs (emphasis added):
+setWithArray:
Creates and returns a set containing a uniqued collection of the objects contained in a given array.
UPDATE: And see here for a similar question: Remove all strings with duplicates in an NSArray
use Set as a filter, example:
String[] arr = {"a","a","b"};
Object[] uniqueArr = (Object[])new HashSet<String>(Arrays.asList(arr)).toArray();

Get matched string from two NSArrays

How can I save the string that match from one NSArray with one index difference in NSMutableArray?
For example, there are three "apple", four "pineapple", six "banana", two "cocoa" and the rest of words dont have duplicate(s) in the nsarray, i would like to know if the nsarray has at least two same words. If yes, I would like to save "apple", "pineapple, "banana" and "cocoa" once in nsmutablearray. If there are other alike words, I would like to add them to namutablearray too.
My code (which still doesn't work properly);
NSArray *noWords = [[NSArray alloc] initWithArray:
[[NSString stringWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:#"words" ofType:#"txt"]
encoding:NSUTF8StringEncoding error:NULL]
componentsSeparatedByString:#"\n"]];
NSUInteger scount = [noWords count];
int ii = 0;
NSString *stringline;
for (ii; ii < scount; ii++)
{
stringline = [noWords objectAtIndex:ii];
NSLog(#"stringline : %# ", stringline);
}
int i = 1;
NSString *line;
for (i ; i < 10; i++)
{
line = [noWords objectAtIndex:i];
NSLog (#"line : %# ", line);
NSMutableArray *douwords = [NSMutableArray array];
if ([stringline isEqualToString:line])
{
NSString *newword;
for (newword in douwords)
{
[douwords addObject:newword];
NSLog (#"detected! %# ", douwords);
}
}
}
Here's a solution using two sets:
- (NSArray *)getDuplicates:(NSArray *)words
{
NSMutableSet *dups = [NSMutableSet set],
*seen = [NSMutableSet set];
for (NSString *word in words) {
if ([seen containsObject:word]) {
[dups addObject:word];
}
[seen addObject:word];
}
return [dups allObjects];
}
Assuming NSSet uses hash tables behind the scenes (which I'm betting it does), this is going to be faster than the previously suggested O(n^2) solution.
Here's something off the top of my head:
NSMutableSet* duplicates = [NSMutableSet set];
NSArray* words = [NSArray arrayWithObjects:#"Apple", #"Apple", #"Orange", #"Apple", #"Orange", #"Pear", nil];
[words enumerateObjectsUsingBlock:^(NSString* str, NSUInteger idx, BOOL *stop) {
for (int i = idx + 1; i < words.count; i++) {
if ([str isEqualToString:[words objectAtIndex:i]]) {
[duplicates addObject:str];
break;
}
}
}];
NSLog(#"Dups: %#", [duplicates allObjects]); // Prints "Apple" and "Orange"
The use of an NSSet, as opposed to an NSArray, ensures strings are not added more than once. Obviously, there are optimizations that could be done, but it should be a good starting point.
I assume that you want to count appearances of words in your array and output those with a count of more than one. A basic and verbose way to do that would be:
// Make an array of words - some duplicates
NSArray *wordList = [[NSArray alloc] initWithObjects:
#"Apple", #"Banana", #"Pencil",
#"Steve Jobs", #"Kandahar",
#"Apple", #"Banana", #"Apple",
#"Pear", #"Pear", nil];
// Make an mutable dictionary - the key will be a word from the list
// and the value will be a number representing the number of times the
// word appears in the original array. It starts off empty.
NSMutableDictionary *wordCount = [[NSMutableDictionary alloc] init];
// In turn, take each word in the word list...
for (NSString *s in wordList) {
int count = 1;
// If the word is already in the dictionary
if([wordCount objectForKey:s]) {
// Increse the count by one
count = [[wordCount objectForKey:s] intValue] + 1;
}
// Save the word count in the dictionary
[wordCount setObject:[NSNumber numberWithInt:count] forKey:s];
}
// For each word...
for (NSString *s in [wordCount keysOfEntriesPassingTest:
^(id key, id obj, BOOL *stop) {
if ([obj intValue] > 1) return YES; else return NO;
}]) {
// print the word and the final count
NSLog(#"%2d %#", [[wordCount objectForKey:s] intValue], s);
}
The output would be:
3 Apple
2 Pear
2 Banana