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!
Related
Is there a downside to dividing implementation of Objective-C classes into categories, for code organization reasons. Instead of using the conventional #pragma mark - SectionTitle way?
Below I've included contrasting samples of a portion of a single implementation file.
Category Approach
#implementation Gallery
+ (NSArray*)titles
{
return #[#"St. Augustine", #"Roanoke", #"Jamestown", #"Santa Fe"];
}
#end
#implementation Gallery (Overrides)
- (NSString*)description
{
return self.title;
}
- (NSString*)debugDescription
{
return [NSString stringWithFormat:#"%# - %u items",
self.title, (unsigned int)[self.items count]];
}
#end
#implementation Gallery (Debug)
+ (instancetype) randomGalleryWithTitle:(NSString*)title;
{
Gallery *gallery = [[Gallery alloc] init];
gallery.title = title;
gallery.iconImageName = title;
NSMutableArray *items = [NSMutableArray array];
for (int i = 0; i < 20; ++i) {
if(rand() % 2 == 0) {
ArtObject *randomArtObject = [ArtObject randomArtObject];
randomArtObject.galleryTitle = gallery.title;
[items addObject:randomArtObject];
} else {
Story *randomStory = [Story randomStory];
randomStory.galleryTitle = gallery.title;
[items addObject:randomStory];
}
}
gallery.items = items;
return gallery;
}
#end
Conventional Approach
#implementation Gallery
+ (NSArray*)titles
{
return #[#"St. Augustine", #"Roanoke", #"Jamestown", #"Santa Fe"];
}
#end
#pragma mark - Overrides
- (NSString*)description
{
return self.title;
}
- (NSString*)debugDescription
{
return [NSString stringWithFormat:#"%# - %u items",
self.title, (unsigned int)[self.items count]];
}
#end
#pragma mark - Debug
+ (instancetype) randomGalleryWithTitle:(NSString*)title;
{
Gallery *gallery = [[Gallery alloc] init];
gallery.title = title;
gallery.iconImageName = title;
NSMutableArray *items = [NSMutableArray array];
for (int i = 0; i < 20; ++i) {
if(rand() % 2 == 0) {
ArtObject *randomArtObject = [ArtObject randomArtObject];
randomArtObject.galleryTitle = gallery.title;
[items addObject:randomArtObject];
} else {
Story *randomStory = [Story randomStory];
randomStory.galleryTitle = gallery.title;
[items addObject:randomStory];
}
}
gallery.items = items;
return gallery;
}
#end
From "Customizing Existing Classes"
in the "Programming with Objective-C" guide:
At runtime, there’s no difference between a method added by a category
and one that is implemented by the original class.
So you can choose whatever you find more intuitive for managing your code. There will
be no difference at runtime.
I'm trying to use wildcard in KVC like this.
Is it possible?
Or Is there other ways to use a wildcard to indicate a member variable?
#interface MyClass : NSObject
#property(nonatomic, retain) NSNumber *test1;
#property(nonatomic, retain) NSNumber *test2;
#end
#implementation MyClass{
NSNumber * test1;
NSNumber * test2;
}
#synthesize test1;
#synthesize test2;
#end
using wildcard
MyClass *testClass = [[[MyClass alloc] init] autorelease];
testClass.test1 = #50;
NSLog(#"test value : %#", [testClass valueForKey:#"*1"]);
For detail codes.
A real reason i wanted is to indicate a member variable of instance by value of integer or nsnumber type.
If possible, it is easier to set values and read values of any instance.
For example of property part copy.
MyClass *testClass = [[[MyClass alloc] init] autorelease];
testClass.year_1 = #2012;
testClass.quarter_2 = #3;
testClass.month_3 = #8;
testClass.day_4 = #20;
testClass.week_5 = #4;
// copy propertys to other instance.
// Normal way
MyClass *testClassCopy = [[[MyClass alloc] init] autorelease];
testClassCopy.year_1 = testClass.year_1;
testClassCopy.quarter_2 = testClass.quarter_2;
testClassCopy.month_3 = testClass.month_3;
testClassCopy.day_4 = testClass.day_4;
// copy propertys by using wildcard
for (int j = 0; j < 4; j++) {
NSString *indicate = [NSString stringWithFormat:#"*%#", [NSNumber numberWithInteger:j + 1]];
NSNumber *sourceProperty = [testClass valueForKey:indicate];
[testClassCopy setValue:sourceProperty forKey:indicate];
}
I'll raise your wildcards by adding Regex, and by using categories:
To read about how regex works with this, please read the NSRegularExpression Class Reference.
Features:
Uses regex, for matching of a wide variety of keys
Uses a category that works on any instance
Caches key lists per class
Full KVC support (not just properties, but accessor methods & iVars too!)
Integrates flawlessly with current KVC methods (only uses the regex if the key wasn't found, improving performance)
Subclassing doesn't mess it up, like #JamesWebster's solution
Doesn't needlessly pollute the list of keys with NSObject's methods
Returns a NSDictionary of matched keys & values
Cons:
Uses regex, which is slower and more complex to understand
Slow initial lookup for a class (must iterate through all methods & iVars)
Automatically overwrites the -valueForUndefinedKey: method, so it's possible that this could break some existing code (move it to it's own method to fix).
Currently doesn't support setting of values (by design, that's a whole other bag of cats).
Can have duplicate keyPaths in the result (not the biggest of issues, but stems from the fact that KVC matching is complex, and I have to implement all of the rules)
Uses NSRegularExpression, which is only available in iOS 4 and later (not the largest of issues).
Version History:
1.0: Initial Release
So, here is the code:
NSObject+KVCRegex.h:
//
// NSObject+KVCRegex.h
// TestProj
//
// Created by Richard Ross on 8/20/12.
// Copyright (c) 2012 Ultimate Computer Services, Inc. All rights reserved.
//
#import <Foundation/Foundation.h>
#interface NSObject (KVCRegex)
// custom implemenation
-(id) valueForUndefinedKey:(NSString *)key;
#end
NSObject+KVCRegex.m:
//
// NSObject+KVCRegex.m
// TestProj
//
// Created by Richard Ross on 8/20/12.
// Copyright (c) 2012 Ultimate Computer Services, Inc. All rights reserved.
//
#import "NSObject+KVCRegex.h"
#import <objc/runtime.h>
#implementation NSObject (KVCRegex)
static NSSet *keyPathsForClass(Class cls)
{
NSMutableSet *keys = [NSMutableSet set];
do
{
if (cls == [NSObject class])
{
// nothing good can come from trying to use KVC on NSObject methods
break;
}
unsigned count = 0;
Method *methods = class_copyMethodList(cls, &count);
for (int i = 0; i < count; i++) {
// make sure that the method returns a value
const char *methodName = sel_getName(method_getName(methods[i]));
char returnType[64];
method_getReturnType(methods[i], returnType, 64);
if (strcmp(returnType, "v") == 0)
continue;
// make sure that the method takes no args (except for self & _cmd)
if (method_getNumberOfArguments(methods[i]) == 2)
{
// add a duplicate entry for ones matching 'is'
if (strstr(methodName, "is") == methodName)
{
char *newStr = strdup(methodName + 2);
newStr[0] = tolower(newStr[0]);
[keys addObject:[NSString stringWithUTF8String:newStr]];
free(newStr);
}
[keys addObject:[NSString stringWithUTF8String:methodName]];
}
}
free(methods);
// now copy iVars
count = 0;
Ivar *ivars = class_copyIvarList(cls, &count);
for (int i = 0; i < count; i++)
{
const char *ivarName = ivar_getName(ivars[i]);
if (strstr(ivarName, "_") == ivarName)
[keys addObject:[NSString stringWithUTF8String:ivarName + 1]]; // iVar name starting with _<key>
[keys addObject:[NSString stringWithUTF8String:ivarName]];
}
free(ivars);
} while ((cls = [cls superclass]));
return [NSSet setWithSet:keys];
}
// returns a dictionary based on 'key' as a regex
-(id) valueForUndefinedKey:(NSString *)key
{
// lookup for later use
static NSMutableDictionary *keyClassPairs;
if (!keyClassPairs)
keyClassPairs = [NSMutableDictionary dictionary];
if (!keyClassPairs[[self class]])
{
keyClassPairs[(id<NSCopying>)[self class]] = keyPathsForClass([self class]);
}
NSSet *keyPaths = keyClassPairs[[self class]];
// assume 'key' is a regex
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:key options:0 error:nil];
NSMutableArray *matches = [NSMutableArray array];
for (NSString *keyPath in keyPaths)
{
NSRange matchRange = [regex rangeOfFirstMatchInString:keyPath options:0 range:(NSRange) { 0, keyPath.length }];
if (matchRange.length == keyPath.length)
{
// we have a match
[matches addObject:keyPath];
}
}
if (matches.count)
return [self dictionaryWithValuesForKeys:matches];
else
[NSException raise:NSUndefinedKeyException format:#"Could not find a key that matches the regex in %#", key];
return nil;
}
#end
Example:
#interface MyObject : NSObject
{
#public
int normalIvar;
id _underscoreIvar;
}
#property id someProp;
#property BOOL isProperty;
#property int nativeProp;
-(void) notAKey;
-(id) aKey;
#end
#implementation MyObject
#synthesize someProp, isProperty, nativeProp;
-(void) notAKey
{
NSLog(#"Not a key!");
}
-(id) aKey
{
return #"Value";
}
#end
int main()
{
#autoreleasepool {
MyObject *obj = [MyObject new];
obj.someProp = #"a property";
obj.nativeProp = 15;
obj.isProperty = YES;
obj->normalIvar = 172;
obj->_underscoreIvar = #"Ivar";
NSString *regex = #"[a|s].*"; // match a key starting with 'a' or 's', then matching anything else after
NSLog(#"%#", [obj valueForKey:regex]); // prints "{ aKey = 'Value', someProp = 'a property' }"
regex = #"_.*"; // match a key starting with '_', and then match anything else after
NSLog(#"%#", [obj valueForKey:regex]); // prints "{ _underscoreIvar = 'Ivar' }"
regex = #".*"; // match any key declared for this object
NSLog(#"%#", [obj valueForKey:regex]); // prints "{ "_underscoreIvar" = Ivar; aKey = Value; isProperty = 1; nativeProp = 15; normalIvar = 172; property = 1; someProp = "a property"; underscoreIvar = Ivar; }"
regex = #"(?i)[A-J].*"; // match (case insensitive) a key starting with A - J
NSLog(#"%#", [obj valueForKey:regex]); // prints "{ aKey = value; isProperty = 1; }"
}
}
Though I couldn't find a way to support wildcards using the syntax you were attempting. I found this roundabout method using the Objective-C runtime!
First we get all of the properties of the class you'd like to use
#import <objc/runtime.h>
unsigned int outCount;
objc_property_t *properties = class_copyPropertyList([MyClass class], &outCount);
NSMutableArray *array = [NSMutableArray arrayWithCapacity:outCount];
for (int i = 0; i < outCount; i++)
{
objc_property_t property = properties[i];
const char *propName = property_getName(property);
if(propName)
{
NSString *propertyName = [NSString stringWithUTF8String:propName];
[array addObject:propertyName];
}
}
free(properties);
Then filter out the ones you actually want
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF ENDSWITH '1'"];
[array filterUsingPredicate:predicate];
Then actually use them
for (NSString *key in array)
NSLog(#"%#", [testClass valueForKey:key]);
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;
}
}
I'm creating UILabels dynamically in a for each loop. Every loop that is run creates 1-4 UILabels.
What I want is that I put these UILabels into my NSMutableArray and being able later to easy retrieve the data.
My original thought was to put these UILabels into a NSDictionary and use [dictGroupLabels setValue:uiLabel1 forKey:#"uiLabel1"] and then [dictGroupLabels setValue:uiLabel2 forKey:#"uiLabel2"] and so on. And then put this dictionary into my NSMutableArray for each loop. Later on I could access the values like UILabel *label = [[myArray objectAtIndex:0] valueForKey:#"uiLabel1"] BUT that unfortunately doesn't work since UILabels don't conform to the NSCopying protocol.
So with this in mind how would you solve this?
this question provided more information on what you are trying to accomplish. Since you know for a fact, the possible set of labels you are trying to create in each case, I would highly recommend using mutable dictionaries instead of arrays.
To illustrate, given the following hypothetical class definition:
#interface MyClass: NSObject {
NSMutableDictionary * _labelDict;
}
#property (nonatomic, retain) NSMutableDictionary * labelDict;
- ( void )methodA;
- ( void )methodB;
- (NSMutableDictionary *) labelsForRunLoop: (NSUInteger) loopIdx;
#end
You would have the following, hypothetical, class implementation:
#implementation MyClass
#synthesize labelDict = _labelDict;
- ( id ) init {
if( ( self = [ super init ] ) ) {
[self setLabelDict: [NSMutableDictionary dictionaryWithCapacity: 8]];
}
}
- ( void ) dealloc {
[ self.labelDict release ];
[ super dealloc ];
}
- ( void ) methodA {
for(NSUInteger i = 0; i < some index; i++) {
[self.labelDict setObject: [self labelsForRunLoop: i] forKey: [NSString stringWithFormat: #"%d", i]];
}
}
- ( void ) methodB {
// Locate the label you need to work with. Example based on this crude pseudo code
NSMutableDictionary * subDict = (NSMutableDictionary *) [self.labelDict objectForKey: #"0"];
UILabel * theLabel = (UILabel * ) [subDict objectForKey: #"UILabel.Z"];
theLabel.text = #"Label 1";
}
- (NSMutableDictionary *) labelsForRunLoop: (NSUInteger) loopIdx {
NSMutableDictionary * dictionary = [NSMutableDictionary dictionaryWithCapacity: 4] ;
[dictionary setObject: create-w-label forKey: #"UILabel.W"];
[dictionary setObject: create-x-label forKey: #"UILabel.X"];
[dictionary setObject: create-y-label forKey: #"UILabel.Y"];
[dictionary setObject: create-z-label forKey: #"UILabel.Z"];
return [dictionary retain];
}
#end
This is basically pseudo code and will not successfully compile. However it will serve as a good starting point. You probably want to store each label dictionary under some key that makes sense, instead of just using the loop's index. Hope this helps.
They don’t need to adhere to NSCopying to be added to an array. It sounds like you just need to do something like this:
NSMutableArray *mainArray = [NSMutableArray array];
for(int i = 0; i < 5; i++)
{
NSMutableArray *subArray = [[NSMutableArray alloc] initWithCapacity:5];
for(int j = 0; j < 4; j++)
{
UILabel *label = [[UILabel alloc] init];
// etc.
[subArray addObject:label];
[label release];
}
[mainArray addObject:subArray];
[subArray release];
}
// then, to get one of the labels:
UILabel *someSpecificLabel = [[mainArray objectAtIndex:2] objectAtIndex:1];
when for . . . in . . . is available?
Specifically, when we can write:
NSArray *array;
// array allocated and initialized here.
for (id obj in array) {
// do something to the object here
}
Why would we ever use an NSEnumerator?
NSEnumerator was created before fast enumeration (for/in loop) was available. Think of it as backward-compatibility if you like.
But with NSEnumerator you can enumerate the collection in customized order, e.g. backwards:
NSEnumerator* enu = [array reverseObjectEnumerator];
id object;
while ((object = [enu nextObject])) {
...
}
(Of course, since NSEnumerator also supports for/in loop you can use a better way:
for (id object in [array reverseObjectEnumerator]) {
...
}
)
or define your own iterator class by subclassing NSEnumerator, e.g.
#import <Foundation/Foundation.h>
#interface RangeEnumerator : NSEnumerator {
int cur, len;
}
+(RangeEnumerator*)enumeratorWithLength:(int)length;
-(id)initWithLength:(int)length;
-(id)nextObject;
#end
#implementation RangeEnumerator
-(id)initWithLength:(int)length {
if ((self = [super init]))
len = length;
return self;
}
+(RangeEnumerator*)enumeratorWithLength:(int)length {
return [[(RangeEnumerator*)[self alloc] initWithLength:length] autorelease];
}
-(id)nextObject {
if (cur < len)
return [NSNumber numberWithInt:cur++];
else
return nil;
}
#end
int main () {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
for (NSNumber* num in [RangeEnumerator enumeratorWithLength:12])
printf("%d\n", [num intValue]);
[pool drain];
return 0;
}