How to compare every pair of elements inside a NSArray? - ios

I have an NSArray filled with only NSStrings
I understand that to iterate over a NSArray of n elements, all I have to do is use for (NSString *element in arrayOfElements). However, I was wondering if there is specific function that will perform a comparison between every string element in the array with each other. For example, if I have the array:
[#"apple", #"banana", #"peach", #"kiwi"],
how would I do the comparison so apple is compared to banana, peach and then kiwi; and then banana is against peach and wiki, and finally peach is against kiwi?

Try using nested for loops, ex:
for (int i = 0 ; i < array.count ; i ++) {
for (int j = i + 1 ; j < array.count ; j ++) {
// compare array[i] to array [j]
}
}
Edit: And although wottle's suggestion would work, I'd recommend mine in this case, since it won't waste iterations going over the same comparisons multiple times. What I've done in this algorithm by setting j = i + 1 is compare each element in the array only to the ones after it.

Given "the array will not have any duplicates, and every NSString will be unique" this sounds like a great case for using NSSet classes instead of NSArray. NSMutableSet provides:
minusSet:
Removes each object in another given set from the receiving
set, if present.
and
intersectSet:
Removes from the receiving set each object that isn’t a
member of another given set.
I'm not sure which operation you're looking for but it sounds like one of those should cover your exact use case.

What you're trying to do is a bit beyond what custom comparators were meant to do. Typically when you have a list and you want to run a custom comparator, you're doing it to sort the list. You seem to want to do some specific action when certain items in the list compare to others, and for that, I think a loop within a loop is your best bet. It won't be very good performance, so hopefully you are not expecting a large array:
-(void) compareArrayToSelf
{
NSArray *array=#[#"apple", #"bananna", #"peach", #"kiwi"];
for( NSString *value1 in array)
{
for( NSString *value2 in array)
{
if( ![value1 isEqualToString:value2] && [self compareArrayValue:value1 toOtherValue:value2])
{
//Do something with either value1 or value2
}
}
}
}

Related

iOS: Remove NSArray in NSMutableArray in For Loop

This is a pretty simple concept, but I'm not getting the results I'm wanting. I have an NSMutableArray that is populated with NSArrays, I want to loop through that NSMutableArray and remove certain NSArrays based on a key-value pair. My results have many of the NSArrays that I should be removing and I think it has something to do with the count of the NSMutableArray and the int I declare in the For Loop.
Here is my code: (restArray is the NSMutableArray)
for (int i=0; i<restArray.count; i++) {
NSArray *array = restArray[i];
if ([[array valueForKey:#"restaurant_status"] isEqualToString:#"0"]) {
[restArray removeObjectAtIndex:i];
}
}
Does someone know what I am doing wrong here?
It is not recommended to modify an array on what are you currently iterating.
Lets create a tmp array, and reverse your logic.
NSMutableArray * tmpArray = [NSMutableArray array];
for (int i=0; i<restArray.count; i++) {
NSArray *array = restArray[i];
if (![[array valueForKey:#"restaurant_status"] isEqualToString:#"0"] {
[tmpArray addObject:array];
}
}
So at the end of the iteration, you should end up with tmpArray having the arrays you needed.
Use NSPredicate:
NSArray *testArray = #[#{#"restaurant_status" : #"1"}, #{#"restaurant_status" : #"0"}];
NSArray *result = [testArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(restaurant_status == %#)", #"1"]];
When you remove an element all the elements past it shift down by one, e.g. If you remove the element at index 3 then the element previously at index 4 moves to index 3.
Every iteration you increase the index by one.
Combine the above two and you see that when you remove an element your code skips examining the following element.
The simple solution is to reverse the order of the iteration:
for (int i = restArray.count - 1; i >= 0; i--)
and then your algorithm will work.
Addendum
You can safely ignore this addendum if your arrays contain < 2^32 elements and you use Clang or GCC (and most other C compilers).
It has been raised in the comments that this answer has a problem if the array has 0 elements in it. Well yes & no...
First note that the code in the question is technically incorrect: count returns an NSUInteger which on a 64-bit machine is a 64-bit unsigned integer, the loop variable i is declared as an int which is 32-bit signed. If the array has more than 2^31-1 elements in it then the loop is incorrect.
Most people don't worry too much about this for some reason ;-) But let's fix it:
for (NSInteger i = restArray.count - 1; i >= 0; i--)
Back to the problem with an empty array: in this case count returns unsigned 0, C standard arithmetic conversions convert the literal 1 to unsigned, the subtraction is done using modular arithmetic, and the result is unsigned 2^64-1.
Now that unsigned value is stored into the signed i. In C converting from signed to unsigned of the same type is defined to be a simple bit-copy. However converting from unsigned to signed is only defined if the value is within range, and implementation defined otherwise.
Now 2^64-1 is greater than the maximum signed integer, 2^32-1, so the result is implementation defined. In practice most compilers, including Clang and GCC, choose to use bit-copy, and the result is signed -1. With this the above code works fine, both the NSInteger and the int (if you've less than 2^32-1 elements in your array) versions.
What the comments raise is how to avoid this implementation-defined behaviour. If this concerns you the following will handle the empty array case correctly with ease:
for (NSUInteger i = restArray.count; i > 0; )
{
i--; // decrement the index
// loop body as before
}
If the array is empty the loop test, i > 0, will fail immediately. If the array is non-empty i, being initialised to the count, will start as one greater than the maximum index and the decrement in the loop will adjust it - effectively in the loop test i contains the number of elements left to process and in the loop body after the decrement contains the index of the next element to process.
Isn't C fun (and mathematically incorrect by definition)!

Call objects by their string name where the strings are built dynamically [duplicate]

This question already has answers here:
Objective C Equivalent of PHP's "Variable Variables" [duplicate]
(2 answers)
Closed 8 years ago.
For my iOS app, I created a class named Tile which is a subclass of UIImageView.
The tiles are displayed in a kind of an array of 6 rows and 5 column.
I previously created 30 instances of my Tile class. These instances are all named this way: RiCj where i is the row number and j is the column number.
I would like to create a for loop where I would apply a specific treatment to each of my tiles (basically, I want to display the tiles where displayTile is an instance method of the class Tile).
I would love to do something like (I know the code below is incorrect):
for (int i = 1; i <= numberOfRows ; j++) {
for (int j = 1; j <= numberOfColumns ; j++) {
[self.RiCj displayTile];
}
}
I don't know how to do a call to my tiles based on their dynamic string title.
Yes, technically, it is possible - you may use Key-Value Coding like this:
for (int i = 1; i <= numberOfRows; i++) {
for (int j = 1; j <= numberOfColumns; j++) {
NSString* tileName = [NSString stringWithFormat:#"R%dC%d", i, j];
[[self valueForKey:tileName] displayTile];
}
}
But you should not. It won't be a clean solution. Array is a more natural choice here.
Yes, you can actually access a property of a class dynamically by creating a string naming the property then using KVC like so:
NSString *propertyName = [NSString stringWithFormat:#"R%dC%d", i, j];
tile = [self valueForKey:propertyName];
But should you? No, not in this case. It's a horrible hack when the perfectly nice alternative of making an array (or array of arrays) is available.
Here's what array of array creation and access might look like (by using handy Objective C literals for arrays):
NSArray *tiles = #[
#[ tile0C0, tile0C1, tile0C2 ],
#[ tile1C0, tile1C1, tile1C2 ],
#[ tile2C0, tile2C1, tile2C2 ],
];
for (int i = 0; i < numberOfRows ; j++) {
for (int j = 0; j < numberOfColumns ; j++) {
tile = tiles[j][i];
// do stuff with tile
}
}
If I understand correctly, you're trying to access the instances by their variable names dynamically. You can't do that, as your variable name is designed for you, the programmer, and is not available at runtime.
What you can do, however, is to keep a list of your created instances in an array somewhere, and simply iterate over that array when you need to access them.
Alternatively, if you created the 30 tiles as 30 different properties, you could use some dynamic code to get them. At that point, however, I would strongly recommend to use the array technique.

how to include index number in a class name

I am making a program where I need to loop through an array with a list of letters. I want the letters to be shown on their specific label. I have therefore created an outlet of each (about 38) and given them the name "alif01", "alif02", etc.
for (int i = 0; i < [arabicLetters count]; i++) {
int num = i;
NSString *letterString = [arabicLetters objectAtIndex:i];
NSLog(#"alif0%d is %#", num, letterString);
alif0**[i]**.text = arabicLetters[i];
}
is it possible to use the index [i] instead of writing it all manually?
You should not have 38 IBOutlet properties for this. You should have an array (possibly an IBOutletCollection) so that you can loop over the array / index into the array.
While technically you can create a key name and use KVC valueForKey: (appending strings / string format), the array approach is a much better solution.
Indeed, as you already have a loop, you would be better served by creating the labels in the loop directly, then you know you have the correct number. This is particularly beneficial later, when you change the contents of arabicLetters (though that sounds like it isn't a concern in this particular case).
Try with below code:
for (int i = 0; i < [arabicLetters count]; i++) {
NSString *letterString = [arabicLetters objectAtIndex:i];
NSString *propertyName = [NSString stringWithFormat:#"alif0%d.text",i];
[self setValue:letterString forKeyPath:propertyName];
}

Calculating percent for 1,000s of objects slow in xcode

can anyone suggest a faster approach to the following:
I have an array of 5,000 managed objects (faulted) (an array of car.h objects)
Each object has a set of items (toCarParts.h). This set can have any number of objects.
Now i want to sort these out by the most matches in my search query carpart array.
I search for wheel, seat, window, mirror.
The method will go through each car and find the closest match, and calculate a percentage. So if car a has wheel, seat, window, mirror, mat, tire, wiper, pipe --> the % should be 50%. (Matched 4/8 parts.
This is simple enough, but the problem is with 5,000 items the search takes a long time (even using coredata).
The logic i am using goes something like: (Pseudocode)
For each Car*car in array.
NSMutableArray *x=[car tocarparts]allobjects];
For the count of objects in x.
Carpart*part=objectatindex...i.
If the name of this matches one of my parts
add a count to my counter.
At the end of the loop counter/[x count] =%.car.percent=%.
There has to be a better way, any suggestions? (I think its the dividing and checking each part that takes forever.
Thank you in advance.
Edited, added code below:.
-(NSMutableArray*)calculatePercentagePerFind:(NSMutableArray*)CarArray:(NSMutableArray*)partsArray{
NSArray*defaultParts =[NSArray arrayWithArray:[[[HelperMethods alloc]init]getObjectUserDefault:#"AvailableDefaultParts"]];
int lowestPercentMatchInt=[[[HelperMethods alloc]init]getIntegerUserDefault:#"lowestPercentageMatch"];
NSMutableArray*partsFromCarArray=[[NSMutableArray alloc]init];
NSMutableArray*returnArray=[[NSMutableArray alloc]init];
NSMutableArray *partsWithDefaultParts =[NSMutableArray arrayWithArray:partsArray];
[partsWithDefaultParts addObjectsFromArray:defaultParts];
for (int i=0; i<[CarArray count]; i++) {
double matchCount=0;
Car *CarResult =(Car*)[CarArray objectAtIndex:i];
//Check if it will at least be 30% match
double number1 = [partsWithDefaultParts count];
number1 =(number1/[CarResult.numberOfParts doubleValue])*100;
if (number1>lowestPercentMatchInt) {
partsFromCarArray =[NSMutableArray arrayWithArray:[[CarResult toParts]allObjects]];
NSMutableArray *faultedParts=[[NSMutableArray alloc]init];
for (int i =0; i<[partsFromCarArray count]; i++) {
CarPart*part = (CarPart*)[partsFromCarArray objectAtIndex:i];
[faultedParts addObject:part.name];
}
// for each part in the Car
for (NSString *partInCar in partsWithDefaultParts){
//if the search parts contain that part, add one to count
if ([faultedParts containsObject:partInCar]) {
matchCount++;
}
}
//Calculate percent match
double percentMatch = matchCount;
percentMatch =(percentMatch/[CarResult.numberOfParts doubleValue])*100;
//if at least 30%(user default) then add the percent match to Car result
if (percentMatch >lowestPercentMatchInt) {
if (percentMatch>100) {
CarResult.percentMatch = [NSNumber numberWithDouble:100.00];
}else{
CarResult.percentMatch = [NSNumber numberWithDouble:percentMatch];
}
[returnArray addObject:CarResult];
}
}
}
NSLog(#"Percent Matched Cars = %i",[returnArray count]);
return [self arrangeByHighestPercentMatch:returnArray];
}
Try this, which I believe will minimize the strain on core data.
NSSet *selectionSet; // contains the selected parts
NSPredicate *filter = [NSPredicate predicateWithFormat:
#"self IN %#", selectionSet];
float percentageSum = 0;
NSSet *parts;
for (Car *car in fetchedObjects) {
parts = car.parts; // so the relationship is retrieved only once
percentageSum +=
[parts filteredSetUsingPredicate:predicate].count*1.0f
/ (parts.count*1.0f);
}
return percentageSum/fetchedObjects.count;
This would average out the percentages across all cars. There are other methods to weigh the parts differently in the aggregate.
It is not clear from your question, but if you do not need a total percentage but one percentage for each car there would be no need to loop through all cars - you could just calculate the percentage on the fly when displaying it (e.g. with a transient property).

NSMutableArray or NSMutableDictionary : which is best for this scenario?

I need to scroll through several thousands of words to categorize them... to determine which words have the same pattern. (this part works)
For example, a four letter word that has two m's in 2nd & 4th position represent a pattern ("-m-m"). Once I have gone through all the words, I will know how many words there are for any given pattern. I am scrolling through now, but the problem I have is 'remembering' how many words I have in any given pattern.
I was thinking of using NSMutableDictionary and have the key be the pattern ('-m-m-') and the object represent the count of that pattern. This means every time I come across a pattern, I look up that pattern in the dictionary, get the key, increment the key, and put it back in the dictionary.
I need help with both the decision and syntax for performing this task.
Thank You
The answer to your question was this part of your (given) question "I will know how many words there are for any given pattern.". I would use an array of dictionary. You use the dictionary to store key value pair: a known pattern and the count. And you use the array to store those KVP records. So the next time you detect a pattern, search for the array for that record (dictionary), if found, increment the count. If not, create new record and set the count to 1.
Added sample code:
#define kPattern #"Pattern"
#define kPatternCount #"PatternCount"
-(NSMutableDictionary *)createANewDictionaryRecord:(NSString *) newPattern
{
int count = 1;
NSMutableDictionary *myDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:
newPattern, kPattern,
[NSString stringWithFormat:#"%i",count], kPatternCount,
nil];
return myDictionary;
}
-(void)addANewPatternToArray:(NSMutableDictionary *)newDictionary
{
// NSMutableArray *myArrayOfDictionary = [[NSMutableArray alloc]init]; // you need to define it somewhere else and use property etc.
[self.myArrayOfDictionary addObject:newDictionary]; //or [self.myArrayOfDictionary addObject:newDictionary]; if you follow the recommendation above.
}
-(BOOL)existingPatternLookup:(NSString *)pattern
{
for (NSMutableDictionary *obj in self.myArrayOfDictionary)
{
if ([[obj objectForKey:kPattern] isEqual:pattern])
{
int count = [[obj objectForKey:kPatternCount] intValue] + 1;
[obj setValue:[NSString stringWithFormat:#"%i",count] forKey:kPatternCount];
return YES;
}
}
[self.myArrayOfDictionary addObject:[self createANewDictionaryRecord:pattern]];
return NO;
}
-(void)testData
{
NSMutableDictionary *newDict = [self createANewDictionaryRecord:#"mmm"];
[self addANewPatternToArray:newDict];
}
-(void) printArray
{
for (NSMutableDictionary * obj in self.myArrayOfDictionary)
{
NSLog(#"mydictionary: %#", obj);
}
}
- (IBAction)buttonPressed:(id)sender
{
if ([self existingPatternLookup:#"abc"])
{
[self printArray];
} else
{
[self printArray];
}
}
Not being an objective C expert but solving this problem in java before, I would say a dictionary(I used a map when doing it in java) is the best way. Check if the key(pattern) already exist if so increment that count else put a new one in the dictionary.
EDIT
If you want to not just get the count of a pattern, but in fact tell which words fall under that pattern, I would use a dictionary of strings to mutable arrays. In the arrays you store the words and the key to the array is the pattern(as a string), similar code as above but instead of just incrementing the count, you have to add the new word to the array.
The only difference in NSDictionary and NSMutableDictionary is that one can have objects added to it. I think your implementation is good, but English is a complex language. It would be more efficient to parse out the string with regex than to set a key for it.
Why don't you use NSCountedSet instead:
NSCountedSet Class Reference
..which is available in iOS 2.0 and later?
Each distinct object inserted into an NSCountedSet object has a counter associated with it. NSCountedSetkeeps track of the number of times objects are inserted [...] Thus, there is only one instance of an object in an NSSet object even if the object has been added to the set multiple times. The count method defined by the superclass NSSet has special significance; it returns the number of distinct objects, not the total number of times objects are represented in the set.
Then use:
- (NSUInteger)countForObject:(id)anObject
Use a dictionary of NSMutableArrays, and check for the existence of each search key as you recommended. If the key doesn't exist, add an NSMutableSet or NSMutableArray (depending on your needs) for the searched key type "-m-m" for example, and then add to the set or array for that key.

Resources