Coming from C++, here's my question :
I have created objects of this type :
Size *one = [[Size alloc] initWithX: 3 andY: 1];
Size *two = [[Size alloc] initWithX: 4 andY: 7];
// etc...
Size *thirtythree = [[Size alloc] initWithX: 5 andY: 9];
( with a #property int x; & #property int y; for each object.. )
that I have stored in an array as follows :
NSArray *arrayOfSizes;
arrayOfSizes = [NSArray arrayWithObjects:one,two,three,four,five,six,
seven,eight,nine,ten,eleven,twelve,thirteen,
fourteen,fifteen,sixteen,seventeen,eighteen,
nineteen,twenty,twentyone,twentytwo,
twentythree,twentyfour,twentyfive,twentysix,
twentyseven,twentyeight,twentynine,thirty,
thirtyone,thirtytwo,thirtythree nil];
now I have a single object of type :
Myobject *myObject = [[Myobject alloc] initWithX: 5 andY: 3];
that also has a #property int x; & #property int y; ...
and I want to compare its values to the values of the objects found in the array, until I find an array object of similar values.. But I don't know how to do that in Obj-C. (in c++ I would simply use a vector v; with v.size(); and v[x]; ..etc... I suppose..)
here's what I'm looking for.. :)
while( !wholeOfArrayOfSizesChecked && !found)
{
if ( // x & y of object in array is equal to x & y of myObject )
{
found = YES;
}
else if( // whole of array checked)
{
wholeOfArrayOfSizesChecked = YES;
}
else
{
//move on to the next object of the array..
}
}
Thanks in advance for any help!
Well, you could just use fast enumeration on the array. Something like this:
Myobject *myObject = [[Myobject alloc] initWithX: 5 andY: 3];
for (Size *s in arrayOfSizes)
{
if (s.x == myObject.x && s.y == myObject.y)
{
// Found one
// Do something useful...
break;
}
}
Another one:
NSUInteger index = [arrayOfSizes indexOfObjectPassingTest:
^BOOL(Size *s, NSUInteger idx, BOOL *stop)
{
return (s.x == myObject.x) && (s.y == myObject.y);
}
];
if (index != NSNotFound) {
id object = [arrayOfSizes objectAtIndex:index];
}
Just to use your given structure. There are smarter ways of doing it though :)
wholeOfArrayOfSizesChecked = NO;
int currObj = 0
while( !wholeOfArrayOfSizesChecked && !found)
{
Size *current = (Size *)[arrayOfSizes objectAtIndex:i];
if (myObject.x == current.x && myObject.y == current.y)
{
found = YES;
}
else if(currObj == [arrayOfSizes count] -1 )
{
wholeOfArrayOfSizesChecked = YES;
}
else
{
currObj++;
}
}
Try something like this:
for (int i = 0; i < [arrayOfSizes size]; i++)
{
Size *current = (Size *)[arrayOfSizes objectAtIndex:i];
if (myObject.x == current.x && myObject.y == current.y)
{
// found
break;
}
}
How'bout a for-in loop?
for (Size *item in array) {
// compare 'item' to myObject
if (/* equal condition here */) break;
}
-(BOOL) isSize:(Size*)size equalToMyObject:(MyObject*)object{
return (size.x == object.x) && (size.y == object.y);
}
//In some method where you are checking it:
for (Size* size in arrayOfSizes){
if ([self isSize:size equalToMyObject:myObject]){
//You found it! They're equal!
break;
}
}
Related
How can I check if an object in the last object on an NSArray?
I've tried:
if ([currentStore isEqual:[Stores lastObject]])
{
//Code
}
but it didn't work.
Any idea?
Thanks!
or try this
BOOL lastElement = false;
NSUInteger index = [stores indexOfObject:currentStore];
if (index != NSNotFound)
{
lastElement = (index == [stores count] - 1);
}
Bit modified try this:
NSUInteger index = [stores indexOfObject:currentStore];
if (index == ([stores count]-1))
{
NSLog(#"Yes its Last");
}
If you didn't override isEqual method, the base class implementation of NSObject::isEqual only check if both pointers points to the same address.
This excellent article http://nshipster.com/equality/ explain objc equality principles
The below sample logs - Testing Stores - works fine
#interface Stores : NSObject
#property (strong, nonatomic) NSString* name;
- (instancetype) initWithName:(NSString*) name;
#end
#implementation Stores
- (instancetype) initWithName:(NSString*) name;
{
_name = name;
return self;
}
- (BOOL)isEqualToStores:(Stores*) Stores
{
if (!Stores)
return NO;
if (![_name isEqualToString:Stores.name] )
return NO;
return YES;
}
- (BOOL)isEqual:(id)object
{
if (self == object)
{
return YES;
}
if (![object isKindOfClass:[Stores class]])
{
return NO;
}
return [self isEqualToStores:(Stores *)object];
}
#end
-(void) testStores
{
Stores* last = [[Stores alloc] initWithName:#"5"];
NSArray* arr = #[
[[Stores alloc] initWithName:#"1"],
[[Stores alloc] initWithName:#"2"],
[[Stores alloc] initWithName:#"3"],
[[Stores alloc] initWithName:#"4"],
[[Stores alloc] initWithName:#"5"]
//last
];
if ([last isEqual:[arr lastObject]])
{
NSLog(#"Testing Stores - works fine");
}
else
{
NSLog(#"Testing Stores - opps!?1?!?");
}
}
I want to mimic NSDirectoryEnumerator with a simple recursive function, but my attempts so far have had my loops stopping prematurely.
The method needs to stop when there is no longer a directory or file present at an nth level.
How can I rearrange the method below to be a simple recursive function that will terminate accordingly?
Any help would be much appreciated. Thanks.
#import "MenuFromPathData.h"
#interface MenuFromPathData ()
#property (nonatomic,strong) NSFileManager *myFileManager;
#end
#implementation MenuFromPathData
#synthesize myFileManager=_myFileManager;
#define ROOT_DIRECTORY #"TextData"
//
////
//
- (void) simpleMenuArrayBuilder
{
NSArray *fileNamesLevel1 = [self fullArrayReturn:ROOT_DIRECTORY];
for(int i = 0; i < [fileNamesLevel1 count]; i++)
{
NSString *pathNameLevel2 = [NSString stringWithFormat:#"%#/%#",ROOT_DIRECTORY,[fileNamesLevel1 objectAtIndex:i]];
NSArray *fileNamesLevel2 = [self fullArrayReturn:pathNameLevel2];
for(int j = 0; j < [fileNamesLevel2 count]; j++)
{
NSString *pathNameLevel3 = [NSString stringWithFormat:#"%#/%#/%#",ROOT_DIRECTORY,[fileNamesLevel1 objectAtIndex:i],[fileNamesLevel2 objectAtIndex:j] ];
NSArray *fileNamesLevel3 = [self fullArrayReturn:pathNameLevel3];
for(int k = 0; k < [fileNamesLevel3 count]; k++)
{
NSString *pathNameLevel4 = [NSString stringWithFormat:#"%#/%#/%#/%#",ROOT_DIRECTORY,[fileNamesLevel1 objectAtIndex:i],[fileNamesLevel2 objectAtIndex:j],[fileNamesLevel3 objectAtIndex:k] ];
NSArray *fileNamesLevel4 = [self fullArrayReturn:pathNameLevel4];
for(int l = 0; l < [fileNamesLevel4 count]; l++)
{
NSString *pathNameLevel5 = [NSString stringWithFormat:#"%#/%#/%#/%#/%#",ROOT_DIRECTORY,[fileNamesLevel1 objectAtIndex:i],[fileNamesLevel2 objectAtIndex:j],[fileNamesLevel3 objectAtIndex:k],[fileNamesLevel4 objectAtIndex:l] ];
NSArray *fileNamesLevel5 = [self fullArrayReturn:pathNameLevel5];
for(int m = 0; m < [fileNamesLevel5 count]; m++)
{
NSString *pathNameLevel6 = [NSString stringWithFormat:#"%#/%#/%#/%#/%#/%#",ROOT_DIRECTORY,[fileNamesLevel1 objectAtIndex:i],[fileNamesLevel2 objectAtIndex:j],[fileNamesLevel3 objectAtIndex:k],[fileNamesLevel4 objectAtIndex:l], [fileNamesLevel5 objectAtIndex:m] ];
NSArray *fileNamesLevel6 = [self fullArrayReturn:pathNameLevel6];
NSLog(#"-- LVL 6.0 %# -- ",pathNameLevel6);
for(int n = 0; n < [fileNamesLevel6 count]; n++)
{
NSString *pathNameLevel7 = [NSString stringWithFormat:#"%#/%#/%#/%#/%#/%#%#",ROOT_DIRECTORY,[fileNamesLevel1 objectAtIndex:i],[fileNamesLevel2 objectAtIndex:j],[fileNamesLevel3 objectAtIndex:k],[fileNamesLevel4 objectAtIndex:l], [fileNamesLevel5 objectAtIndex:m],[fileNamesLevel6 objectAtIndex:n] ];
NSArray *fileNamesLevel7 = [self fullArrayReturn:pathNameLevel7];
NSLog(#"-- LVL 7.0 %# -- ",pathNameLevel7);
}
}
}
}
}
}
}
//
////
//
- (NSString*) resourcePath : (NSString*) pathName {
return [[NSBundle mainBundle] pathForResource:pathName ofType:nil];
}
- (NSArray*) bundleArrayReturn : (NSString*)pathForResource {
NSError* error;
return [self.myFileManager contentsOfDirectoryAtPath:pathForResource error:&error];
}
- (NSArray*) fullArrayReturn : (NSString*) pathName {
return [self bundleArrayReturn:[self resourcePath: pathName]];
}
//
////
//
- (NSFileManager*) myFileManager {
if(!_myFileManager) {
_myFileManager = [NSFileManager defaultManager];
}
return _myFileManager;
}
#end
Something like this should work. Only trick is to add another parameter to your method to track current depth during the recursion.
- (void) simpleMenuArrayBuilderForPath:(NSString*)pathToDecend allowedDepth:(int)depth
{
// if we've defended far enough, then stop
if(depth == 0) return;
// otherwise, get the next directory listing at this level
NSArray *fileNamesNextLevel = [self fullArrayReturn:pathToDecend];
for(int i = 0; i < [fileNamesNextLevel count]; i++)
{
// find each path inside the directory we're looking at
NSString *nextLevelPathName = [pathToDecend stringByAppendingPathComponent:[fileNamesNextLevel objectAtIndex:i]];
// process it however you want...
NSLog(#"Looking at %#", nextLevelPathName);
// and recur into it
[self simpleMenuArrayBuilderForPath:nextLevelPathName allowedDepth:depth-1];
}
}
then, call it by:
[self simpleMenuArrayBuilderForPath:ROOT_DIRECTORY allowedDepth:5];
I know that print line is NSLog. How do I print a line 10 times in Objective-C?
I haven't had a chance to refactor yet, but something like this should work!
for(int i = 0; i < 10; i++)
{
if(i == 0)
NSLog(#"A line");
else if (i == 1)
NSLog(#"A line");
else if (i == 2)
NSLog(#"A line");
else if (i == 3)
NSLog(#"A line");
else if (i == 4)
NSLog(#"A line");
else if (i == 5)
NSLog(#"A line");
else if (i == 6)
NSLog(#"A line");
else if (i == 7)
NSLog(#"A line");
else if (i == 8)
NSLog(#"A line");
else if (i == 9)
NSLog(#"A line");
}
Another while loop option.
//
// main.m
// Pritner
//
// Created by Joshua Caswell on 7/19/12.
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
// A Pritner instance holds and displays a passed-in string. The string is publicly
// unchangeable.
#interface Pritner : NSObject
+ (id)pritnerWithLine: (NSString *)newLine;
- (void)printLine;
#property (readonly, copy, nonatomic) NSString * line;
#end
// Extension to manage "class variable" for counting number of created instances
#interface Pritner ()
+ (NSUInteger)numPritnersCreated;
+ (void)setNumPritnersCreated:(NSUInteger)n;
+ (NSUInteger)maxNumPritners;
#property (readwrite, copy, nonatomic) NSString * line;
- (id)initWithLine: (NSString *)line;
#end
#implementation Pritner
#synthesize line;
+ (id)pritnerWithLine: (NSString *)newLine {
id newInstance = [[self alloc] initWithLine:newLine];
if( newInstance ){
NSUInteger createdSoFar = [self numPritnersCreated];
// Only allow maxNumPritners to ever be created; keeping track of them
// is the client's problem.
if( createdSoFar >= [self maxNumPritners] ){
abort();
}
[self setNumPritnersCreated:createdSoFar + 1];
}
return newInstance;
}
// Fake class variable using associated objects; keep count of created instances
char numPritnerKey;
+ (NSUInteger)numPritnersCreated {
NSNumber * n = objc_getAssociatedObject(self, &numPritnerKey);
if( !n ){
n = [NSNumber numberWithUnsignedInteger:0];
[self setNumPritnersCreated:0];
}
return [n unsignedIntegerValue];
}
+ (void)setNumPritnersCreated:(NSUInteger)n {
objc_setAssociatedObject(self,
&numPritnerKey,
[NSNumber numberWithUnsignedInteger:n],
OBJC_ASSOCIATION_RETAIN);
}
// Maximum number of instances ever allowed to be created
+ (NSUInteger)maxNumPritners {
return 10;
}
- (id)initWithLine: (NSString *)newLine {
self = [super init];
if( !self ) return nil;
line = [newLine copy];
return self;
}
- (void)printLine {
NSLog(#"%#", [self line]);
}
#end
int main(int argc, const char * argv[])
{
#autoreleasepool {
while( YES ){
Pritner * p = [Pritner pritnerWithLine:#"I figure, if you're going to build a time machine out of a car, why not do it with some style?"];
[p printLine];
}
}
return 0;
}
Please don't use this in real life.
Don't forget the "object" in "Objective-C".
NSArray *lines = [#"123456789" componentsSeparatedByCharactersInSet:[NSCharacterSet alphanumericCharacterSet]];
[lines enumerateObjectsUsingBlock:^(id obj, NSUInteger i, BOOL* stop) {
NSLog(#"a line");
}];
If you think Objective-C isn't true enough to its Smalltalk roots, you can do something like the following.
typedef void (^LoopBlock)(NSNumber*);
#interface NSNumber (loop)
-(void)to:(NSNumber*)upTo do:(LoopBlock)block;
-(void)timesRepeat:(LoopBlock)block;
#end
#implementation NSNumber (loop)
static NSString *loopSeparator = #"_";
-(void)to:(NSNumber*)upTo do:(LoopBlock)block {
[ [ [#"" stringByPaddingToLength:[upTo unsignedIntegerValue]-[self unsignedIntegerValue]
withString:loopSeparator
startingAtIndex:0
] componentsSeparatedByString:loopSeparator
] enumerateObjectsUsingBlock:^(id obj, NSUInteger i, BOOL* stop) {
block([NSNumber numberWithUnsignedInteger:i+[self unsignedIntegerValue]]);
}
];
}
-(void)timesRepeat:(LoopBlock)block {
[[NSNumber numberWithUnsignedInteger: 1] to:self do:block];
}
#end
int main() {
#autoreleasepool {
[[NSNumber numberWithInt:10]
timesRepeat:^(NSNumber* i){
NSLog(#"a line");
}];
}
}
Use recursion:
print(10);
void print(int i){
if(i == 0)
return;
NSLog(#"print this line");
print(i - 1);
}
Use Grand Central Dispatch another way:
dispatch_apply(10, dispatch_get_main_queue(), ^(size_t curr_iteration){
NSLog(#"a line");
});
Use a goto statement:
int i = 0;
print:
NSLog(#"print this line");
if (i++ < 10) goto print;
Use a for loop:
for (int i = 0; i < 10; i++) {
NSLog(#"print this line");
}
Use Grand Central Dispatch:
__block int i = 0;
__block dispatch_block_t print_block = ^() {
NSLog(#"print this line");
i += 1;
if (i < 10) dispatch_sync(dispatch_get_main_queue(), print_block);
}
dispatch_sync(dispatch_get_main_queue(), print_block);
Use a while loop:
int i = 0;
while (i < 10) {
NSLog(#"print this line");
i += 1;
}
I want to move a string item to the top of the list.
NSMutableArray animal = "cat", "lion", "dog", "tiger";
How do I move dog to top of the list?
You would remove the item and insert it at the correct space:
id tmp=[[animals objectAtIndex:2] retain];
[animals removeObjectAtIndex:2];
[animals insertObject:tmp atIndex:0];
[tmp release];
You have to retain the object or when you tell the array to remove it, it will release the object.
If you don't know the index you could do something like this:
NSMutableArray* animals = [NSMutableArray arrayWithObjects:#"cat", #"lion", #"dog", #"tiger",nil];
for (NSString* obj in [[animals copy] autorelease]) {
if ([obj isEqualToString:#"dog"]) {
NSString* tmp = [obj retain];
[animals removeObject:tmp];
[animals insertObject:tmp atIndex:0];
break;
}
}
This method will go over all your list and search for "dog" and if it finds it will remove it from the original list and move it to index 0.
I would like to point out an inefficiency in your method. By removing/inserting an object from a NSMutableArray you potentially affect every row after the deletion/insertion. I say ‘potentially’ because it’s not clear what internal method Apple uses to maintain their mutable arrays. However, assuming it’s a simple c-array, then every row after that deletion/insertion index will be need to be moved down/up. In a very large array, this could be inefficient if the items moved are at the beginning. However, replacing items in an array are not inefficient at all. Thus the following is a category on NSMutableArray (note this code is under ARC, so no memory management):
- (void) moveObjectAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex{
if (fromIndex == toIndex) return;
if (fromIndex >= self.count) return; //there is no object to move, return
if (toIndex >= self.count) toIndex = self.count - 1; //toIndex too large, assume a move to end
id movingObject = [self objectAtIndex:fromIndex];
if (fromIndex < toIndex){
for (int i = fromIndex; i <= toIndex; i++){
[self replaceObjectAtIndex:i withObject:(i == toIndex) ? movingObject : [self objectAtIndex:i + 1]];
}
} else {
id cObject;
id prevObject;
for (int i = toIndex; i <= fromIndex; i++){
cObject = [self objectAtIndex:i];
[self replaceObjectAtIndex:i withObject:(i == toIndex) ? movingObject : prevObject];
prevObject = cObject;
}
}
}
Also, a small bonus to further increase functionality, if you're performing operations on the items moved (like updating a db or something), the following code has been very useful to me:
- (void) moveObjectAtIndex:(NSUInteger)fromIndex toIndex:(NSUInteger)toIndex withBlock:(void (^)(id, NSUInteger))block{
if (fromIndex == toIndex) return;
if (fromIndex >= self.count) return; //there is no object to move, return
if (toIndex >= self.count) toIndex = self.count - 1; //toIndex too large, assume a move to end
id movingObject = [self objectAtIndex:fromIndex];
id replacementObject;
if (fromIndex < toIndex){
for (int i = fromIndex; i <= toIndex; i++){
replacementObject = (i == toIndex) ? movingObject : [self objectAtIndex:i + 1];
[self replaceObjectAtIndex:i withObject:replacementObject];
if (block) block(replacementObject, i);
}
} else {
id cObject;
id prevObject;
for (int i = toIndex; i <= fromIndex; i++){
cObject = [self objectAtIndex:i];
replacementObject = (i == toIndex) ? movingObject : prevObject;
[self replaceObjectAtIndex:i withObject:replacementObject];
prevObject = cObject;
if (block) block(replacementObject, i);
}
}
}
You can remove an existing element, e.g dog and then reinsert it at the beginning of the array.
NSMutableArray *animals = [NSMutableArray arrayWithObjects:#"cat", #"lion", #"dog", #"tiger",nil];
NSString *dog = #"dog";
// Check to see if dog is in animals
if ( [animals containsObject:dog] ) {
// Remove dog from animals and reinsert
// at the beginning of animals
[animals removeObject:dog];
[animals insertObject:dog atIndex:0];
}
I have a large NSArray of names, I need to get random 4 records (names) from that array, how can I do that?
#include <stdlib.h>
NSArray* names = ...;
NSMutableArray* pickedNames = [NSMutableArray new];
int remaining = 4;
if (names.count >= remaining) {
while (remaining > 0) {
id name = names[arc4random_uniform(names.count)];
if (![pickedNames containsObject:name]) {
[pickedNames addObject:name];
remaining--;
}
}
}
I made a caregory called NSArray+RandomSelection. Just import this category into a project, and then just use
NSArray *things = ...
...
NSArray *randomThings = [things randomSelectionWithCount:4];
Here's the implementation:
NSArray+RandomSelection.h
#interface NSArray (RandomSelection)
- (NSArray *)randomSelectionWithCount:(NSUInteger)count;
#end
NSArray+RandomSelection.m
#implementation NSArray (RandomSelection)
- (NSArray *)randomSelectionWithCount:(NSUInteger)count {
if ([self count] < count) {
return nil;
} else if ([self count] == count) {
return self;
}
NSMutableSet* selection = [[NSMutableSet alloc] init];
while ([selection count] < count) {
id randomObject = [self objectAtIndex: arc4random() % [self count]];
[selection addObject:randomObject];
}
return [selection allObjects];
}
#end
If you prefer a Swift Framework that also has some more handy features feel free to checkout HandySwift. You can add it to your project via Carthage then use it like this:
import HandySwift
let names = ["Harry", "Hermione", "Ron", "Albus", "Severus"]
names.sample() // => "Hermione"
There is also an option to get multiple random elements at once:
names.sample(size: 3) // => ["Ron", "Albus", "Harry"]
I hope this helps!