Objective-C Version comparison [duplicate] - objective-c

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Comparing version numbers
How to use compare on a version number where theres less parts in one number in Objective-C?
I am attempting to sort an NSMutableArray of custom objects based on a property called referenceID which essentially resembles a version number.
It seems that treating referenceID as an NSNumber and sorting it using compareTo: almost gets it right, but where it breaks is cases such as:
Result: Should Be:
1.1.1 1.1.1
1.1.10 1.1.2
1.1.2 ...
... 1.1.9
1.1.9 1.1.10
(Where ... is 1.1.2 through 1.1.9)
Are there any built in functions that will sort this properly? Or should I get started writing the sorting algorithm?

If your reference id is a string, you can use localizedStandardCompare:, which compares numbers in strings according to their numerical value.
Example (with sortedArrayUsingComparator, because that is used by the OP in his comment):
NSArray *versions = #[#"2.1.1.1", #"2.10.1", #"2.2.1"];
NSArray *sorted = [versions sortedArrayUsingComparator:^NSComparisonResult(NSString *s1, NSString *s2) {
return [s1 localizedStandardCompare:s2];
}];
NSLog(#"%#", sorted);
Output:
2012-11-29 23:51:28.962 test27[1962:303] (
"2.1.1.1",
"2.2.1",
"2.10.1"
)

sort it with a block
#autoreleasepool {
//in this example, array of NSStrings
id array = #[#"1.1.1",#"2.2",#"1.0",#"1.1.0.1",#"1.1.2.0", #"1.0.3", #"2.1.1.1", #"2.1.1", #"2.1.10"];
//block
id sorted = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSArray *comps1 = [obj1 componentsSeparatedByString:#"."];
NSArray *comps2 = [obj2 componentsSeparatedByString:#"."];
//get ints from comps
int res1 = 0;
for (int i=0; i<comps1.count; i++) {
res1 += [comps1[i] intValue] * (4 - i);
}
int res2 = 0;
for (int i=0; i<comps2.count; i++) {
res2 += [comps2[i] intValue] * (4 - i);
}
return res1<res2 ? NSOrderedAscending : res1>res2 ? NSOrderedSame : NSOrderedDescending;
}];
NSLog(#"%#", sorted);
}

Related

How to pair strings together from NSArray? [duplicate]

This question already has answers here:
split array of objects into groups of 4 - ios
(3 answers)
Closed 1 year ago.
I have a NSArray that looks like this:
NSArray *array = {#"1.100.2", #"23465343", #"1.100.1", #"46535334", #"1.0.03", #"24353454" ...};
I need to pair every 2 strings into an array within an array like this:
NSArray *pairedArray = {{#"1.100.2", #"23465343"}, {#"1.100.1", #"46535334"}, {#"1.0.03", #"24353454"}, ...};
The array is dynamic.
I think there's a for statement I can use but can't remember. Something like for (uint i = 0; i < len - 1; i += 2)
Any help would be appreciated.
Here is something that tries to be fast, by using the array's iterator and by making heavy use of arrayWithCapacity.
NSArray *array = #[ #"1.100.2", #"23465343", #"1.100.1", #"46535334", #"1.0.03", #"24353454" ];
NSUInteger countPerPair = 2;
NSMutableArray * pair = [NSMutableArray arrayWithCapacity:countPerPair];
NSMutableArray * pairedArray = [NSMutableArray arrayWithCapacity:( array.count + countPerPair / 2 ) / countPerPair];
for ( NSObject * i in array )
{
if ( pair.count == countPerPair )
{
[pairedArray addObject:pair];
pair = [NSMutableArray arrayWithCapacity:countPerPair];
}
[pair addObject:i];
}
if ( pair.count )
{
[pairedArray addObject:pair];
}

Calculating value of K without messages

Question:
Find the value of K in myInterViewArray without any messages/calls
I was given this hint:
The numbers in the array will never exceed 1-9.
NSArray *myInterViewArray = #[#2,#1,#3,#9,#9,#8,#7];
Example:
If you send 3, the array will return the 3 biggest values in myInterViewArray * 3. So in the example below, K = 9 + 9 + 8.
--
I was asked this question a while back in an interview and was completely stumped. The first solution that I could think of looked something like this:
Interview Test Array:
[self findingK:myInterViewArray abc:3];
-(int)findingK:(NSArray *)myArray abc:(int)k{ // With Reverse Object Enumerator
myArray = [[[myArray sortedArrayUsingSelector:#selector(compare:)] reverseObjectEnumerator] allObjects];
int tempA = 0;
for (int i = 0; i < k; i++) {
tempA += [[myArray objectAtIndex:i] intValue];
}
k = tempA;
return k;
}
But apparently that was a big no-no. They wanted me to find the value of K without using any messages. That means that I was unable to use sortedArrayUsingSelector and even reverseObjectEnumerator.
Now to the point!
I've been thinking about this for quite a while and I still can't think of an approach without messages. Does anyone have any ideas?
There is only one way to do that and that is bridging the array to CF type and then use plain C, e.g.:
NSArray *array = #[#1, #2, #3];
CFArrayRef cfArray = (__bridge CFArrayRef)(array);
NSLog(#"%#", CFArrayGetValueAtIndex(cfArray, 0));
However, if the value is a NSNumber, you will still need messages to access its numeric value.
Most likely the authors of the question didn't have a very good knowledge of the concept of messages. Maybe they thought that subscripting and property access were not messages or something else.
Using objects in Obj-C without messages is impossible. Every property access, every method call, every method initialization is done using messages.
Rereading the question, they probably wanted you to implement the algorithm without using library functions, e.g. sort (e.g. you could implement a K-heap and use that heap to find the K highest numbers in a for iteration).
I assume what is meant is that you can't mutate the original array. Otherwise, that restriction doesn't make sense.
Here's something that might work:
NSMutableArray *a = [NSMutableArray array];
for (NSNumber *num in array) {
BOOL shouldAdd = NO;
for (int i = a.count - 1; i >= k; i--) {
if ([a[i] intValue] < [num intValue]) {
shouldAdd = YES;
break;
}
}
if (shouldAdd) {
[a addObject:num];
}
}
int result = a[a.count - k];
for (int i = k; k < a.count; k++) {
result += [a[i] intValue];
}
return result;

Array of unique elements but with a condition-

I have an array of integers (NSMutable array to be exact), now i want the array to have unique elements, but there is a catch.
Condition-1. If there are 2 (even number of )elements that are similar then both the elements need to be deleted.
Condition-2. If there are 3(odd number of) elements then only 1 element shall remain in the array.
am having a bit of a problem trying to solve it.
I have tried to loop around the array with 2 temporary variables
for(int i =1;i<[tagArray count];i++){
int temp1 = [[tagArray objectAtIndex:i] intValue];
int temp2 = [[tagArray objectAtIndex:i-1] intValue];
if(temp1==temp2){
[tagArray removeObjectIdenticalTo:[tagArray objectAtIndex:i]];
[tagArray removeObjectAtIndex:i];
NSLog(#"%#",tagArray);
}
}
This code works but, when there are a lot of elements in the array the output i get is not the desired one.
P.S- the array is already being filled up randomly so i took the liberty of sorting it first.
1 more thing. please dont say abt set, i want unique elements but when i use SET see condition1 and 2. i cant perform those.
sort array (by ascending or descending intValue)
loop through each element (from 0 to N-1) and count number of previous equals elements (write it to equalNumbersCount).
After current number becomes not equal to previous, look at equalNumberCount and remove necessary number of elements (if this count is > 1 and even, remove all, if it's odd, remove only equalNumberCount - 1 previous elements)
Reset equalNumberCount to 0.
Something like this. Tested and seems to work fine:
NSMutableArray *arr = [#[#1, #4, #4, #2, #2, #3, #3, #3, #4, #4, #5] mutableCopy];
arr = [[arr sortedArrayWithOptions:NSSortStable usingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1 compare:obj2];
}] mutableCopy];
uint countOfEqualElements = 1;
for (uint i = 1; i < arr.count; i++) {
NSNumber *n1 = arr[i-1];
NSNumber *n2 = arr[i];
if ( [n2 isEqualToNumber:n1]) {
countOfEqualElements++;
}
else if (countOfEqualElements > 1){
uint numToRemove = countOfEqualElements % 2 == 0 ? countOfEqualElements : countOfEqualElements - 1;
countOfEqualElements = 1;
NSRange r;
r.length = numToRemove;
r.location = i - numToRemove;
NSIndexSet * is = [NSIndexSet indexSetWithIndexesInRange:r];
[arr removeObjectsAtIndexes:is];
i -= numToRemove;
}
}
Though I've not tested with other elements yet, at present I think this might atleast interest you.
NSArray *localArray = #[#"1",#"23",#"2",#"3",#"4",#"5",#"5",#"9",#"1",#"11",#"10",#"1",#"23",#"3"];
NSCountedSet *countedSet= [[NSCountedSet alloc] initWithArray:localArray];
NSMutableArray *arrayToCheck = [localArray mutableCopy];
for (id obj in [localArray mutableCopy])
{
if([countedSet countForObject:obj]==2)
{
[arrayToCheck removeObject:obj inRange:NSMakeRange([[localArray mutableCopy] indexOfObject:obj], [arrayToCheck count]-[[localArray mutableCopy] indexOfObject:obj])];
}
else if ([countedSet countForObject:obj]==3)
{
[arrayToCheck removeObject:obj inRange:NSMakeRange([[localArray mutableCopy] indexOfObject:obj], [arrayToCheck count]-[[localArray mutableCopy] indexOfObject:obj])];
[arrayToCheck addObject:obj];
}
}
localArray = [NSArray arrayWithArray:arrayToCheck];
NSLog(#" array is %#",localArray);

UIButton with random text/value or tag [duplicate]

This question already has answers here:
iOS: How do I generate 8 unique random integers?
(6 answers)
Closed 9 years ago.
I have 10 UIButtons created in historyboard, OK?
I want to add random numbers that do not repeat these numbers, ie, numbers from 0 to 9 that interspersed whenever the View is loaded.
I tried to find on Google and here a way to use my existing buttons ( 10 UIButton ), and just apply them to random values​​. Most ways found ( arc4random() % 10 ), repeat the numbers.
Here's one
here's another
here's another
All results found that creating buttons dynamically. Anyone been through this?
Create an array of the numbers. Then perform a set of random swapping of elements in the array. You now have your unique numbers in random order.
- (NSArray *)generateRandomNumbers:(NSUInteger)count {
NSMutableArray *res = [NSMutableArray arrayWithCapacity:count];
// Populate with the numbers 1 .. count (never use a tag of 0)
for (NSUInteger i = 1; i <= count; i++) {
[res addObject:#(i)];
}
// Shuffle the values - the greater the number of shuffles, the more randomized
for (NSUInteger i = 0; i < count * 20; i++) {
NSUInteger x = arc4random_uniform(count);
NSUInteger y = arc4random_uniform(count);
[res exchangeObjectAtIndex:x withObjectAtIndex:y];
}
return res;
}
// Apply the tags to the buttons. This assumes you have 10 separate ivars for the 10 buttons
NSArray *randomNumbers = [self generateRandomNumbers:10];
button1.tag = [randomNumbers[0] integerValue];
button2.tag = [randomNumbers[1] integerValue];
...
button10.tag = [randomNumbers[9] integerValue];
#meth has the right idea. If you wanna make sure the numbers aren't repeating, try something like this: (note: top would the highest number to generate. Make sure this => amount or else this will loop forever and ever and ever ;)
- (NSArray*) makeNumbers: (NSInteger) amount withTopBound: (int) top
{
NSMutableArray* temp = [[NSMutableArray alloc] initWithCapacity: amount];
for (int i = 0; i < amount; i++)
{
// make random number
NSNumber* randomNum;
// flag to check duplicates
BOOL duplicate;
// check if randomNum is already in your array
do
{
duplicate = NO;
randomNum = [NSNumber numberWithInt: arc4random() % top];
for (NSNumber* currentNum in temp)
{
if ([randomNum isEqualToNumber: currentNum])
{
// now we'll try to make a new number on the next pass
duplicate = YES;
}
}
} while (duplicate)
[temp addObject: randomNum];
}
return temp;
}

compare 2 strings (that are numbers) [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to do a natural sort on an NSArray?
Comparing version numbers
I have my app version saved as an NSString. The version is in the format x.y.z, or x.y (where x,y,z represent integers).
If I want to compare 2 versions (ie: 2 strings representing 2 different/same versions), what is the best way to go about doing this?
Thanks!
Sunny
i had the same problem and couldn't make it work. I finally decided to save version with NSNumber ...saved me a lot of time ...so i suggest you do the same
if you already have the app in the App Store...just do something like:
NSString *string = <get version>
if ([string isKindOfClass:[NSString class]]){
update version and save as NSNumber
}
using NSString to save version may seem like a good idea at first but its tricky when you try to update the app. Using NSNumber is easier because it ...well..uses numbers
so i looked through NSScanner Class Reference and came up with this solution:
int i1,i2,i3;
NSScanner *scanner =[NSScanner alloc]initWithString:string];
BOOL scanI1 = [scanner scanInteger:&i1];
[scanner setScanLocation:3];
BOOL scanI2 = [scanner scanInteger:&i2];
[scanner setScanLocation:5];
BOOL scanI3 = [scanner scanInteger:&i3];
[scanner release];
it's not pretty but it should work
There are about a million ways you could go about this, but here is a quick example:
void versionStringComponents(NSString* versionStr_, NSInteger* major__, NSInteger* minor__, NSInteger* bugfix__)
{
NSArray* elements = [versionStr_ componentsSeparatedByString:#"."];
*major__ = [[elements objectAtIndex:0] intValue];
*minor__ = [[elements objectAtIndex:1] intValue];
*bugfix__ = 0;
if([elements count] > 2)
{
*bugfix__ = [[elements objectAtIndex:2] intValue];
}
}
bool versionLessThan(NSString* versionStr1_, NSString* versionStr2_)
{
NSInteger major1 = 0, minor1 = 0, bugfix1 = 0;
versionStringComponents(versionStr1_, &major1, &minor1, &bugfix1);
NSInteger major2 = 0, minor2 = 0, bugfix2 = 0;
versionStringComponents(versionStr2_, &major2, &minor2, &bugfix2);
return (
major1 < major2 ||
(major1 == major2 && minor1 < minor2) ||
(major1 == major2 && minor1 == minor2 && bugfix1 < bugfix2)
);
}
Obviously this is a quick little hack, as versionStringComponents() just blindly separates the version numbers from a string, turns them into ints, etc. Also, it could use a few more comparison functions, but I'm sure you can figure those out.
This link, as mentioned by Saphrosit, is a much shorter way of accomplishing the same task, but requires the use of code from the Sparkle framework.