This question already has answers here:
Finding out NSArray/NSMutableArray changes' indices
(3 answers)
Closed 8 years ago.
Here is what i need it to do.
NSArray has 10 objects
NSArray2 has the same 10 objects but in different indexes.
I need to compare if NSArray1 index:5 matches NSArray2 index:5 if not tell me if the object has moved up or down in the NSArray2, same for every other object inside that array.
The objects have the following properties: id and name.
Any suggestion on how i can accomplish this?
If you have RAM to spare you could build a map from the object's id to its index in array 1, then scan array 2 and compare, like:
NSMutableDictionary *map = [[NSMutableDictionary alloc] init];
for (NSUInteger j = 0; j < array1.count; j++) {
id object = array1[j];
map[object.id] = #(j);
}
for (NSUInteger j = 0; j < array2.count; j++) {
id object = array2[j];
id identifier = object.id;
NSUInteger array1Index = [map[identifier] unsignedIntegerValue];
// Compare array1Index to j here.
}
That'll let you compare with a running time that grows like the number of objects in the arrays, but note that you have to spend some extra RAM to make that map. You could compare with only constant RAM costs if you're willing to spend more time:
for (NSUInteger j = 0; j < array1.count; j++) {
id object = array1[j];
NSUInteger k = [array2 indexOfObject:object];
// Compare j and k, note that k could be NSNotFound.
}
And that should have a running time that grows like the product of the array counts.
Related
I have three mutable array:
wkdatearray values like this: date from 2016-01-10 to 2016-01-16.
spentonarray values like this: 2016-01-10 to 2016-01-13.
hoursarray values like this: 7,3,4,5,1.
So, I compare the index objects of wkdatearray and spentarray like this:
for (int i = 0; i < self.wkDateArray.count; i++) {
for (int j = 0; j < self.spentonArray.count; j++) {
if ([
[self.wkDateArray objectAtIndex: i] isEqualToString: [self.spentonArray objectAtIndex: j]
]) {
NSLog(# "Matched Indexes %d %#", i, [self.wkDateArray objectAtIndex: i]);
} else {
}
}
Now I want to get the result like :
If the index value of self.wkdatearray and self.spentonarray are matched or equals then I have to set the hours array value as object for self.wkdatearray[0],[1]…[6]. like this in newsheet dictionary. Now I manually put string #"".
newSheet = [NSDictionarydictionaryWithObjectsAndKeys:strEntryID,entryID, proj,project,projID,projectId, strIssue,issue,strIssueID,issueId, strActivity,activity,strActivityId,activityId,#"",comment,#"",self.wkDateArray[0],#"",self.wkDateArray[1],#"",self.wkDateArray[2],#"",self.wkDateArray[3],#"",self.wkDateArray[4],#"",self.wkDateArray[5],#"",self.wkDateArray[6], nil];
If the index value of self.wkdatearray and self.spentonarray are not matched and not equals then the hours value like this #“‘ as object for self.wkdatearray[0].like this in newsheet dictionary. Now, I put by default object values as #""for self.wkdatearray[0...6].
Here the hours array having 5 values only.
How to do both the conditions inside the for loop? Or is there any other way to do this?
First of all make the newSheet dictionary mutable. You do not need 2 for loops.
int minIndexCount = MIN(self.wkdatearray.count, self. spentonArray.count)
we first take the min of wkdatearray.count and spentonArray.count since for example if there are only 2 items in spentonArray then if we try to use index 4 of wkdatearray then we do not have index 4 in spentonArray, meaning the primary condition that the indexes of both wkdatearray and spentonArray should be equal fails. Hence by taking min we ensure that we are only going to compare those indexes whixha re available in both wkdatearray and spentonArray.
for (int i = 0; i < minIndexCount; i++) {
if ([[self.wkDateArray objectAtIndex: i] isEqualToString: [self.spentonArray objectAtIndex: i]])
{
NSLog(# "Matched Indexes %d %#", i, [self.wkDateArray objectAt Index: i]);
[newSheet setValue:self.hoursarray[i] forKey:self.weekArray[i]]
} else {
}
}
If by "matched or equals" you mean that if the index's are the same AND the strings are matched.
for (int i = 0; i < self.wkDateArray.count; i++) {
for (int j = 0; j < self.spentonArray.count; j++) {
if (i==j && [
[self.wkDateArray objectAtIndex: i] isEqualToString: [self.spentonArray objectAtIndex: j]
]) {
NSLog(# "Matched Indexes %d %#", i, [self.wkDateArray objectAtIndex: i]);
} else {
}
}
Take note of the beginning of the if statement that has if (i==j && ...
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)!
I am trying to return the lowest number in an array.
Parameter: arrayOfNumbers - An array of NSNumbers.
Return: The lowest number in the array as an NSInteger.
The code I have thus far doesn't give me any errors, but does not pass the unit tests. What am I doing wrong?
- (NSInteger) lowestNumberInArray:(NSArray *)arrayOfNumbers {
NSNumber* smallest = [arrayOfNumbers valueForKeyPath:#"#min.self"];
for (NSInteger i = 0; i < arrayOfNumbers.count; i++) {
if (arrayOfNumbers[i] < smallest) {
smallest = arrayOfNumbers[i];
}
}
NSInteger smallestValue = [smallest integerValue];
return smallestValue;
}
This is the unit test:
- (void) testThatLowestNumberIsReturned {
NSInteger lowestNumber = [self.handler lowestNumberInArray:#[#3, #8, #-4, #0]];
XCTAssertEqual(lowestNumber, -4, #"Lowest number should be -4.");
lowestNumber = [self.handler lowestNumberInArray:#[#83, #124, #422, #953, #1004, #9532, #-1000]];
XCTAssertEqual(lowestNumber, -1000, #"Lowest number should be -1000.");
}
This method
NSNumber* smallest = [arrayOfNumbers valueForKeyPath:#"#min.self"];
will already determine the smallest number in the array, so the loop inside the method is superfluous (on top of being plain wrong, as #vikingosegundo notices).
you are comparing objects with c types, resulting im pointer addresses being compared with an int.
Beside the fact your smallest is already the smallest, as you used the KVC collection operator #min.self (see Glorfindel answer), the following code shows you correct comparison
if (arrayOfNumbers[i] < smallest)
should be
if ([arrayOfNumbers[i] compare:smallest] == NSOrderingAscending)
or
if ([arrayOfNumbers[i] integerValue] < [smallest integerValue])
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.
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];
}