As an iOS programmer I sometimes delve into C to achieve faster results (well actually for fun) and I'm struggling to modify a C-array's values inside a function call. Although I think this answer may help (Modifying a array in a function in C), I don't know where to begin in implementing it.
Currently I have:
[object.guestlists enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
NSObject *someNSObject = [NSObject new];
NSInteger array[3] = {totalIncomingMales,totalIncomingFemales,
totalIncomingGuestsCount};
[self callMethod:object withCArray:cArray];
}];
- (void) callMethod:(NSObject*) object withCArray:(NSInteger*) cArray {
// do something with that object
NSInteger totalIncomingMales = cArray[0],
totalIncomingFemales = cArray[1],
totalIncomingGuestsCount = cArray[2];
// modify these integers internally so that the C-array passed in is also modified
}
Obviously this passes a pointer and therefore doesn't modify the value. I tried replacing the NSinteger * with NSInteger ** and making,
e.g. totalIncomingMales = * cArray[0], but alas I wasn't able to pass the c-array as a parameter (even with an ampersand).
Some useful material and potentially a solution to this would be much appreciated!
Not sure if I understand your question, but it seems to be trivial one:
- (void) callMethod:(NSObject*) object withCArray:(NSInteger*) cArray {
// modify these integers internally so that the C-array passed in is also modified
cArray[0] = 10;
cArray[1] = 20;
cArray[2] = 30;
}
- (void) myMethod:(NSString *) myConstString array: (NSArray *) myArray
{
NSInteger array[3] = {1,2,3};
NSLog(#"%ld %ld %ld", (long)array[0],(long)array[1],(long)array[2]);
[self callMethod:nil withCArray:array];
NSLog(#"%ld %ld %ld", (long)array[0],(long)array[1],(long)array[2]);
}
result will be:
1,2,3
after 10,20,30. No pointer trickery needed, because you are telling it is NSInteger so compiler does it for you.
Related
I have a NSString *strName = #"JonnySmith";
What I want to do is get an NSArray of NSStrings with all possible combinations of a name, omitting certain characters. For example:
#"J";
#"Jo";
#"Jon";
but also combinations like:
#"JSmith";
#"JonSmith"
#"JonnSm";
#"JonSmt";
#"Smith";
#"th";
But they need to be in the order of the original name (the characters can't be out of order, just omitted). Basically traversing left to right in a loop, over and over again, until all possible combos are made.
What is the most efficient way to do this in Objective-C without make a mess?
Let's see if we can give you some pointers, everything here is abstract/pseudocode.
There are 2^n paths to follow, where n is the number of characters, as at each character you either add it or do not.
Taking your example after the first character you might produce #"" and #"J", then to each of these you either add the second character or not, giving: #"", #"J" (add nothing), #"o", "#Jo". Observe that if you have repeated characters anywhere in your input, in your sample you have two n's, this process may produce duplicates. You can deal with duplicates by using a set to collect your results.
How long is a character? Characters may consist of sequences of unicode code points (e.g. 🇧🇪 - Belgium flag, if it prints in SO! Letters can be similarly composed), and you must not split these composed sequences while producing your strings. NSString helps you here as you can enumerate the composed sequences invoking a block for each one in order.
The above give you the pseudocode:
results <- empty set
for each composed character in input do block:
add to results a copy of each of its members with the composed character appended
You cannot modify a collection at the same time you enumerate it. So "add to results" can be done by enumerating the set creating a new collection of strings to add, then adding them all at once after the enumeration:
new items <- empty collection
for every item in results
add to new items (item appending composed character)
results union new items
Optimising it slightly maybe: in (2) we had the empty string and in (4) we append to the empty string. Maybe you could not add the empty string to start and initialise new items to the composed character?
Hint: why did I write the non-specific collection in (4)?
Have fun. If you code something up and get stuck ask a new question, describe your algorithm, show what you've written, explain the issue etc. That will (a) avoid down/close votes and (b) help people to help you.
One possibility is to consider every combination to be a mask of bits, where 1 means the character is there and 0 means the character is missing, for example:
100010000 for JonnySmith will mean JS
000000001 for JonnySmith will mean h
It's simple to generate such masks because we can just iterate from 1 (or 000000001) to 111111111.
Then we only have to map that mask into characters.
Of course, some duplicates are generated because 1110... and 1101... will both be mapped to Jon....
Sample implementation:
NSString *string = #"JonnySmith";
// split the string into characters (every character represented by a string)
NSMutableArray<NSString *> *characters = [NSMutableArray array];
[string enumerateSubstringsInRange:NSMakeRange(0, string.length)
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
[characters addObject:substring];
}];
// let's iterate over all masks
// start with zero if you want empty string to be included
NSUInteger min = 1;
NSUInteger max = (1 << characters.count) - 1;
NSMutableString *buffer = [[NSMutableString alloc] initWithCapacity:characters.count];
NSMutableSet *set = [NSMutableSet set];
for (NSUInteger mask = min; mask <= max; mask++) {
[buffer setString:#""];
// iterate over all bits in the generated mask, map it to string
for (NSInteger charIndex = 0; charIndex < characters.count; charIndex++) {
if ((mask & (1 << (characters.count - charIndex - 1))) != 0) {
[buffer appendString:[characters objectAtIndex:charIndex]];
}
}
// add the resulting string to Set, will handle duplicates
[set addObject:[buffer copy]];
}
NSLog(#"Count: %#", #(set.count)); // 767
The size for NSUInteger will give us the maximum number of characters we can use using this method.
Noticed the question is old but no answer is accepted. I think you can generate all permutations and then omit results which don't match your criteria (or tweak this code per your needs)
#interface NSString (Permute)
- (NSSet *)permutations;
#end
#implementation NSString (Permute)
- (NSSet *)permutations {
if ([self length] <= 1) {
return [NSSet setWithObject:self];
}
NSMutableSet *s = [NSMutableSet new];
[s addObject:[self substringToIndex:1]];
for (int i = 1; i < self.length; i++) {
char c = [self characterAtIndex:i];
s = [self words:s insertingLetterAtAllPositions:[NSString stringWithFormat:#"%C",c]];
}
return [s copy];
}
- (NSMutableSet *)words:(NSSet *)words insertingLetterAtAllPositions:(NSString *)letter {
NSMutableSet *collector = [NSMutableSet new];
for (NSString *word in words) {
[collector unionSet:[word allInsertionsOfLetterAtAllPositions:letter]];
}
return collector;
}
- (NSMutableSet *)allInsertionsOfLetterAtAllPositions:(NSString *)letter {
NSMutableSet *collector = [NSMutableSet new];
for (int i = 0; i < [self length] + 1; i++) {
NSMutableString *mut = [self mutableCopy];
[mut insertString:letter atIndex:i];
[collector addObject:[mut copy]];
}
return collector;
}
#end
// usage
[#"abc" permutations];
You can do it quite easily with a little recursion. It works like this:
Check if the length is 1, then return an array of 2 elements, the empty string and the string.
Call recursively with input string minus the first character and assign to sub-result.
Duplicate the sub-result, adding the first character to each string.
Return the result.
Remember to not call for empty string. If you want to omit the empty result string just remove the first element. Also, if you use the same letter several times, you will get some result strings several times. Those can be removed afterwards.
- (void)combinations:(NSString *)string result:(NSMutableArray *)result {
if (string.length == 1) {
[result addObjectsFromArray:#[ #"", string ]];
} else {
[self combinations:[string substringFromIndex:1] result:result];
for (NSInteger i = result.count - 1; i >= 0; --i)
[result addObject:[[string substringToIndex:1] stringByAppendingString:result[i]]];
}
}
// Call like this, for speed only one mutable array is allocated
NSString *test = #"0123456789";
NSMutableArray *result = [NSMutableArray arrayWithCapacity:1 << test.length];
[self combinations:test result:result];
How can I check which value in an NSMutableArray is the most frequent?
Array = "0,2,3,2,2,4
the value = "2".
Take a look at NSCountedSet this will help with your problem.
NSCountedSet *countedSet = [[NSCountedSet alloc] initWithArray:youArray];
NSInteger maxCount = 0;
id maxObject = nil;
for (id object in countedSet) {
if ([countedSet object] > maxCount) {
maxCount = [countedSet countForObject:object];
maxObject = object;
}
}
return maxObject;
This does sound like homework though.
EDIT
If they are stored as strings instead of numbers then swap out NSNumber for NSString everything else works the same.
EDIT
Actually, I just realised that it doesn't care about what object type it is...
Latest edit will work whatever the object is.
Currently working on finding the best solution to find the smallest time interval between the current time and a date inside of my array.
I have a method that takes a NSArray and returns an NSArray. The method does the following:
Loops through array sorted by time (0 index is closest to current time)
Find smallest interval by finding the delta and comparing
Grab that index, fetch it, return a dictionary
I started to look in to using timeIntervalSinceDate and so on instead of the manual work I am using below.
The array looks something like this:
tideSummary: [{
'type' : 'High Tide',
'pretty' : 'January 16 at 5:13PM EST',
'epoch' : '325267782',
...
}]
Does this code seem to be bloated for what its trying to do? I feel there is a good chunk of duplication to extract certain data based on an index and a key value?
I want to return the closest time out of the array, so I wanted to use timeIntervalSince1970 and do some simple math to find the smallest delta. My array contains a key that returns time in milliseconds
Any suggestions on how I can clean my conditional up so I can still extract: lowTideTime - highTideTime and tideType
Below is my method I am using to extract this information:
- (NSArray *)findUpcomingTides: (NSArray *)arrayOfTideCycles {
NSTimeInterval currentDateInterval;
currentDateInterval = [[NSDate date]timeIntervalSince1970];
NSInteger smallestDelta = currentDateInterval;
NSArray *upcomingTideData = [[NSArray alloc] init];
for (NSInteger i = 0; i < arrayOfTideCycles.count; i++) {
NSDictionary *eachTideSummary = [arrayOfTideCycles objectAtIndex:i];
NSInteger tideDateAsEPOCH = [[eachTideSummary valueForKeyPath:#"epoch"] intValue];
NSInteger dateDelta = tideDateAsEPOCH - smallestDelta;
if (dateDelta < smallestDelta) {
smallestDelta = dateDelta;
int iPlusOne = i+1;
upcomingTide = [arrayOfTideCycles objectAtIndex:i];
NSDictionary *tideTypeDictionary = [arrayOfTideCycles objectAtIndex:i];
tideType = [tideTypeDictionary objectForKey:#"type"];
if([[upcomingTide valueForKeyPath:#"type"] isEqualToString:#"Low Tide"] || [[upcomingTide valueForKeyPath:#"type"] isEqualToString:#"Max Ebb"]){
NSString *lowTidePrettyDateFormat = [upcomingTide valueForKeyPath:#"pretty"];
lowTideTime = [self convertAndFormatDateToTimeWithString:lowTidePrettyDateFormat];
NSDictionary *upcomingHighTide = [arrayOfTideCycles objectAtIndex:iPlusOne];
NSString *highTidePrettyDateFormat = [upcomingHighTide valueForKeyPath:#"pretty"];
highTideTime = [self convertAndFormatDateToTimeWithString:highTidePrettyDateFormat];
} else {
NSString *highTidePrettyDateFormat = [upcomingTide valueForKeyPath:#"pretty"];
highTideTime = [self convertAndFormatDateToTimeWithString:highTidePrettyDateFormat];
NSDictionary *upcomingLowTide = [arrayOfTideCycles objectAtIndex:iPlusOne];
NSString *lowTidePrettyDateFormat = [upcomingLowTide valueForKeyPath:#"pretty"];
lowTideTime = [self convertAndFormatDateToTimeWithString:lowTidePrettyDateFormat];
}
upcomingTideData = [NSArray arrayWithObjects:lowTideTime, highTideTime, tideType, nil];
}
}
return upcomingTideData;
}
Any suggestions on how I can clean this up?
As I understand the question what you dislike is violation of DRY in this parallelism:
NSString *lowTidePrettyDateFormat = [upcomingTide valueForKeyPath:#"pretty"];
lowTideTime = [self convertAndFormatDateToTimeWithString:lowTidePrettyDateFormat];
NSDictionary *upcomingHighTide = [arrayOfTideCycles objectAtIndex:iPlusOne];
NSString *highTidePrettyDateFormat = [upcomingHighTide valueForKeyPath:#"pretty"];
highTideTime = [self convertAndFormatDateToTimeWithString:highTidePrettyDateFormat];
and
NSString *highTidePrettyDateFormat = [upcomingTide valueForKeyPath:#"pretty"];
highTideTime = [self convertAndFormatDateToTimeWithString:highTidePrettyDateFormat];
NSDictionary *upcomingLowTide = [arrayOfTideCycles objectAtIndex:iPlusOne];
NSString *lowTidePrettyDateFormat = [upcomingLowTide valueForKeyPath:#"pretty"];
lowTideTime = [self convertAndFormatDateToTimeWithString:lowTidePrettyDateFormat];
As far as I can tell at first glance, they are absolutely identical (aside from some unimportant local variable names). So factor them out into a method that takes the upcomingTide value and returns an array of the two tide times (or whatever it is that these two bits of code are supposed to produce for you).
Some suggestions:
valueForKeyPath is a very general and complex method that will run relatively slow; objectForKey will be a lot quicker.
If your array contains data in milliseconds, then your code is in trouble. intValue only handles values between about +/- 2 billion. 2 billion milliseconds = 2 million seconds = a bit over a month. Use longLongValue or doubleValue. (If your data is actually in seconds, your code will go wrong some time around 2038).
I don't know exactly what you are trying to do, but this
NSInteger dateDelta = tideDateAsEPOCH - smallestDelta;
if (dateDelta < smallestDelta) {
smallestDelta = dateDelta;
is wrong. Just step through it with the debugger and see how the values change. And I'm sure you'll want an absolute value taken somewhere in there.
Your code will crash if the last array element gives the smallest delta, because you will access the array element one further which is beyond the array.
If you have say 10,000 array elements, five thousand before and five thousand after the current date, you will find the best element so far five thousand times, and each time you do significant work. I'd first find the best array element, and when that is found, get the data that you want.
Hello I am working on a project and I am trying to add an NSUInteger to an NSMutableArray. I am new to Objective-C and C in general. When I run the app NSLog displays null.
I'd appreciate any help anyone is able to provide.
Here is my code
-(NSMutableArray *)flipCardAtIndex:(NSUInteger)index
{
Card *card = [self cardAtIndex:index];
[self.flipCardIndexes addObject:index];
if(!card.isUnplayable)
{
if(!card.isFaceUp)
{
for(Card *otherCard in self.cards)
{
if(otherCard.isFaceUp && !otherCard.isUnplayable)
{
int matchScore = [card match:#[otherCard]];
if(matchScore)
{
otherCard.unplayable = YES;
card.unplayable = YES;
self.score += matchScore * MATCH_BONUS;
}
else
{
otherCard.faceUp = NO;
self.score -=MISMATCH_PENALTY;
}
break;
}
}
self.score -=FLIP_COST;
}
card.faceUp = !card.isFaceUp;
}
NSLog(#"%#",self.flipCardIndexes[self.flipCardIndexes.count-1]);
return self.flipCardIndexes;
}
NSArray (along with its subclass NSMutableArray) only supports objects, you cannot add native values to it.
Check out the signature of -addObject:
- (void)addObject:(id)anObject
As you can see it expects id as argument, which roughly means any object.
So you have to wrap your integer in a NSNumber instance as follows
[self.flipCardIndexes addObject:#(index)];
where #(index) is syntactic sugar for [NSNumber numberWithInt:index].
Then, in order to convert it back to NSUInteger when extracting it from the array, you have to "unwrap" it as follows
NSUInteger index = [self.flipCardIndexes[0] integerValue]; // 0 as example
You can only add objects to NSMutableArrays. The addObject accepts objects of type id, which means it will accept an object.
NSIntegers and NSUIntegers, however, are not objects. They are just defined to be C style variables.
#if __LP64__ || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif
As you can see, they are just defined to be ints and longs based on a typedef macro.
To add this to your array, you need to first convert it to an object. NSNumber is the Objective C class that allows you to store a number of any type. To make the NSNumber, you will want to you the numberWithInt method, passing your variable as the parameter.
NSNumber *number = [NSNumber numberWithInt:card];
Now that your variable is wrapped in an object, you can add it to the array.
[self.flipCardIndexes addObject:number];
Finally, if you want to retrieve the element at a future time, you have to remove the object and then convert it back to an int value you can use. Call
NSNumber *number = [self.flipCardIndexes objectAtIndex:index];
Where index is the index of the card you are trying to retrieve. Next, you have to convert this value to an integer by calling integerValue.
NSUInteger *value = [number integerValue];
I have a long NSString, something like "t00010000t00020000t00030000" and so on. I need to split that up into each "t0001000".
I'm using...
NSArray *tileData = [[GameP objectForKey:#"map"] componentsSeparatedByString:#"t"];
And it splits it up, but the "t" is missing, which I need (although I think I could append it back on). The other way I guess would be to split it up by counting 8 char's, although not sure how to do that.
But ideally I need it split into a [][] type array so I can get to each bit with something like...
NSString tile = [[tileData objectAtIndex:i] objectAtIndex:j]];
I'm new to obj-c so thanks for any help.
If they're not strictly the t characters that separate the sections, i. e. the parts are always 8 characters long, then it's very easy to do it:
NSString *string = #"t00010000t00020000t00030000";
NSMutableArray *arr = [NSMutableArray array];
int i;
for (i = 0; i < string.length; i += 8) {
[arr addObject:[string substringWithRange:NSMakeRange(i, 8)]];
}
and here arr will contain the 8-character substrings.
Edit: so let me also provide yet another solution for the multidimensional one. Of course #KevinH's solution with the characters is very elegant, but if you need an NSString and you don't mind implementing another method, it's fairly easy to add something like this:
#implementation NSString (EightCarAdditions)
- (NSString *)charAsStringInSection:(NSInteger)section index:(NSInteger)index
{
return [self substringWithRange:NSMakeRange(section * 8 + index, 1)];
}
- (unichar)charInSection:(NSInteger)section index:(NSInteger)index
{
return [self characterAtIndex:section * 8 + index];
}
#end
(Beware that characterAtIndex returns a unichar and not a char - be prepared for more than 1 byte-wide UTF-(8, 16, 32) stuff.) You can then call these methods on an NSString itself, so it's very convenient:
unichar ch = [string charInSection:1 index:3];
H2CO3's answer is spot-on for the first part. For the second (the multi-dimensional array), if I understand what you want, you don't need another array for the individual characters. For each NSString in the array, you can access each character by calling characterAtIndex. So, extending the example above:
for (NSString *item in arr) {
NSLog(#"The fifth character of this string is: %C", [item characterAtIndex:4]);
}
And if you're looking to chain these together, as in your example, you can do that too:
NSLog(#"The fifth character of the fourth string is: %C",
[[arr objectAtIndex:3] characterAtIndex:4]);