This question already has answers here:
Sorting two NSArrays together side by side
(4 answers)
Closed 6 years ago.
I have 2 NSMutableArrays declared. One is filled with names and then another one is filled with string values of NSDate.
I want to sort both of them according to the date in the second one. For example if element 3 in the date array becomes element 0 I want the same to happen for the name array.
What is the easiest way to do this? I know how to sort the date array just not the corresponding name array!
(Objective-C Please!)
Sorting 2 arrays and keeping them in sync is a pain. You basically have to sort them by hand.
Probably the easiest way to do this is to create an array of dictionaries where each dictionary contains a data and a name.
Then sort the array of dictionaries by date.
EDIT:
Here is the code for creating custom objects containing a name and a date. There is code to sort by date as well as code to sort by name:
/**
The thing class has a property date and a property name.
It is purely for creating sorted arrays of objects
*/
#interface Thing : NSObject
#property (nonatomic, strong) NSDate *date;
#property (nonatomic, strong) NSString *name;
#end
#implementation Thing
/**
This is a dummy init method that creates a Thing ojbect with a random name and date
*/
- (instancetype) init {
self = [super init];
if (!self) return nil;
NSString *letters = #"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
NSMutableString *temp = [NSMutableString new];
for (int x = 0; x < 10; x++) {
unichar aChar = [letters characterAtIndex:arc4random_uniform(26)];
[temp appendFormat: #"%C", aChar];
}
self.name = [temp copy];
//Create a random date
uint32 halfMax = 2000000000;
uint32 max = halfMax * 2;
int32_t value = arc4random_uniform(max) - halfMax;
NSTimeInterval now = [[NSDate date] timeIntervalSinceReferenceDate];
self.date = [NSDate dateWithTimeIntervalSinceReferenceDate: now + value];
return self;
}
- (NSString *) description {
return [NSString stringWithFormat: #"Name: %# Date: %#", self.name, self.date];
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
//Create an array of Thing objects
const int count = 50;
NSMutableArray *thingArray = [NSMutableArray arrayWithCapacity: count];
for (int x = 0; x < count; x++) {
thingArray[x] = [[Thing alloc] init];
}
#if 1
//Sort by date, ascending
[thingArray sortUsingComparator:^NSComparisonResult(Thing *obj1,
Thing *obj2) {
NSComparisonResult bigger =
[obj1.date timeIntervalSinceReferenceDate] <
[obj2.date timeIntervalSinceReferenceDate] ?
NSOrderedAscending : NSOrderedDescending;
return bigger;
}];
#else
//Sort by name
[thingArray sortUsingComparator:^NSComparisonResult(Thing *obj1,
Thing *obj2) {
return [obj1.name compare: obj2.name];
}];
#endif
NSLog(#"%#", thingArray);
}
return 0;
}
Related
Say I have n variables
NSNumber* A = #(1);
NSNumber* B = #(2);
NSNumber* C = #(3);
NSNumber* D = #(4);
NSNumber* E = #(5);
...
I need a dictionary like
{#"A":#(1), #"B":#(2), #"C":#(3), #"D":#(4), ... }
One can imagine a more cumbersome example that would be tedious to type out
I think saw a C style function for it but I can't remember. Something like NSDictionaryForVariables()
The C preprocessor macro (not a function) you're looking for is NSDictionaryOfVariableBindings. However, outside of Auto Layout (and, debatably, even there), it's not really a great idea to be setting up dependencies between runtime and compile-time identifiers like that.
Depending on what you're trying to actually accomplish, Key-Value Coding might be a better solution.
It's not a good approach you may find another way to solve your issue but if you want to have an idea about your requested solution here here my try
Our properties
#interface TestyViewController ()
#property (nonatomic) NSNumber* a;
#property (nonatomic) NSNumber* b;
#property (nonatomic) NSNumber* c;
#property (nonatomic) NSNumber* d;
#end
Set the values
- (void)viewDidLoad {
[super viewDidLoad];
self.a=#(1);
self.b=#(2);
self.c=#(3);
self.d=#(4);
}
Get our instance variables
-(NSArray *)propertyNames{
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList([self class], &propertyCount);
NSMutableArray * propertyNames = [NSMutableArray array];
for (unsigned int i = 0; i < propertyCount; ++i) {
objc_property_t property = properties[i];
const char * name = property_getName(property);
[propertyNames addObject:[NSString stringWithUTF8String:name]];
}
free(properties);
return propertyNames;
}
Create the dictionary
- (IBAction)buttonClicked:(id)sender
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
for (NSString* varName in [self propertyNames])
{
[dict setObject:[self valueForKey:varName] forKey:varName];
}
NSLog(#"%#",dict);
}
result
2015-07-15 20:30:56.546 TestC[879:27973] {
a = 1;
b = 2;
c = 3;
d = 4;
}
I would like to know what's the best or most appropriate approach for this question: Given a list of numbers example [2, 3, 4, 2, 3], return the first number that occurs only once in the list.
I have followed some algorithms approach and came up with this, but not sure if there are any built-in helper functions in Objective-C that will allow me to do this with a better performance..
If there are not built-ins solutions, is there is any improvements that can be made to my approach or any other solution that could be better in terms of performance?
This is my updated solution for this:
For testing:
#import "NSArray+Addons.h"
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray<NSNumber *> *array = #[#(2), #(7), #(3), #(2), #(3), #(2), #(7), #(3), #(2), #(3), #(4), #(7), #(5), #(5), #(9)];
NSLog(#"Unique number: %#", [array firstUniqueNumber]);
}
#end
NSArray category:
#import "NSArray+Addons.h"
#import "NSMutableDictionary+Addons.h"
#implementation NSArray (Addons)
- (NSNumber *)firstUniqueNumber
{
if (!self.count)
{
return nil;
}
NSMutableDictionary<NSNumber *, NSNumber *> *myUniqueNumbers = [[NSMutableDictionary alloc] init];
return [myUniqueNumbers uniqueValueFromArray:self];
}
#end
NSMutableDictionary category:
#import "NSMutableDictionary+Addons.h"
#implementation NSMutableDictionary (Addons)
- (NSNumber *)uniqueValueFromArray:(NSArray<NSNumber *> *)array
{
if (!array.count)
{
return nil;
}
for (NSNumber *number in array)
{
if (!self[number])
{
self[number] = [NSNumber numberWithInteger:1];
}
else
{
NSInteger count = [self[number] integerValue];
count++;
self[number] = [NSNumber numberWithInteger:count];
}
}
return [self uniqueNumberWithArray:array];
}
- (NSNumber *)uniqueNumberWithArray:(NSArray<NSNumber *> *)array
{
if (!array.count)
{
return nil;
}
NSNumber *uniqueNumber = nil;
for (NSInteger index = array.count - 1; index > 0; index--)
{
NSNumber *key = array[index];
if (self[key] && [self[key] integerValue] == 1)
{
uniqueNumber = key;
}
}
return uniqueNumber;
}
#end
NSCountedSet* set = [[NSCountedSet alloc] initWithArray:array];
NSUInteger index = [array indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop){
return [set countForObject:obj] == 1;
}];
return index == NSNotFound ? nil : [array objectAtIndex:index];
This problem can be reduced to element distinctness problem, so there is no linear time solution, without using hashing and extra space.
One simple solution in O(n) time on average + space is:
Build a hash based histogram of the data, that maps each value to the number of its occurances.
Find the first number in the array that its value in the histogram is 1.
Pseudo code:
map = new hashmap
for each element x:
if map contains x is a key:
map.put(x,map.get(x)+1)
else:
map.put(x,1)
for each element x in array:
if map.get(x) == 1:
return x
//if reached here - no distinct element
Example:
array = [2, 3, 4, 2, 3]
create histogram: {[2=2] [3=2], [4=1]}
iterate the array:
check 2, it has value of 2 in histogram. continue
check 3, it has value of 2 in histogram. continue
check 4, it has value of 1 in histogram. Return it and finish.
-(NSNumber *)returnFirstUniqueFromArray: (NSArray *)array{
//put the numbers in a set
NSCountedSet *numbers = [[NSCountedSet alloc] initWithArray:array];
for(NSNumber *number in array){
//if it only occurs once return
if([numbers countForObject:number]==1) return number;
}
return nil;
}
They key being here you need a good way to keep track of how many times something occurs so take advantage of NSCountedSet's "count" method. Will tell you how many times an object occurs.
#property NSMutableArray*textFieldsA;
#property NSMutableArray*textFieldsB;
#property NSMutableArray*textFieldsR;
#property NSMutableArray *operandArrayA;
#property NSMutableArray *operandArrayB;
_textFieldsA = #[self.textFieldA0, self.textFieldA1, self.textFieldA2, self.textFieldA3, self.textFieldA4, self.textFieldA5, self.textFieldA6, self.textFieldA7, self.textFieldA8];
_textFieldsB = #[self.textFieldB0, self.textFieldB1, self.textFieldB2, self.textFieldB3, self.textFieldB4, self.textFieldB5, self.textFieldB6, self.textFieldB7, self.textFieldB8];
_textFieldsR = #[self.textFieldR0, self.textFieldR1, self.textFieldR2, self.textFieldR3, self.textFieldR4, self.textFieldR5, self.textFieldR6, self.textFieldR7, self.textFieldR8];
for (int i = 0; i < 9; i++) {
_operandA = [((UITextField*)_textFieldsA[i]).text integerValue];
_operandB = [((UITextField*)_textFieldsB[i]).text integerValue];
[self.arithmetic setOperandA:_operandA operandB:_operandB operator:_operator];
_finalString = [NSString stringWithFormat:#"%d",[self.arithmetic result]];
((UITextField*)_textFieldsR[i]).text = _finalString;
}
how can i put the integerValue of
self.textFieldA0.text , self.textFieldA1.text , self.textFieldA3.text ........self.textFieldA8.text
in to the array (operandArrayA)?
i have tried
operandArrayA[i] = [((UITextField*)_textFieldsA[i]).text integerValue];
but it is not work, how should i do?
Unfortunately you can not add primitive datatypes in NSArray. You need to create object of NSNumber for integer value. Store this object to an array.
Like this:
NSNumber *number = #([((UITextField*)_textFieldsA[i]).text integerValue]);
operandArrayA[i] = number;
You need to alloc and initialize all the arrays..like below
operandArrayA=[[NSMutableArray alloc]init];
operandArrayB=[[NSMutableArray alloc]init];
//if u want to store strings
[operandArrayA addObject:((UITextField*)_textFieldsA[i]).text];
[operandArrayB addObject:((UITextField*)_textFieldsB[i]).text];
//(OR) if u want to store integer value then u need to convert to NSNumber
[operandArrayA addObject:[NSNumber numberWithInt:[((UITextField*)_textFieldsA[i]).text integerValue]]];
[operandArrayB addObject:[NSNumber numberWithInt:[((UITextField*)_textFieldsB[i]).text integerValue]]];
//(OR)
operandArrayA[i]=[NSNumber numberWithInt:[((UITextField*)_textFieldsA[i]).text integerValue]];
operandArrayB[i]=[NSNumber numberWithInt:[((UITextField*)_textFieldsB[i]).text integerValue]];
Hope it helps you..
I am working on a project where I download a few values (stored as float in my database) into my array and sum them. I can NSLog the numbers but Unfortunately I am having the hardest time to sum those values in my array.
Adding some code
DETAILS.H
#import <Foundation/Foundation.h>
#interface Details: NSObject
#property (nonatomic, strong)NSNumber *min;
//#property (nonatomic, strong) NSString *min;
#end
just the part of the code where I put the values in the array
for (int i = 0; i < jsonArray.count; i++)
{
NSDictionary *jsonElement = jsonArray[i];
tempDetails *downloadTemps = [[tempDetails alloc] init];
downloadTemps.min = jsonElement[#"min"];
// Add this question to the locations array
[_locations addObject:downloadTemps];
}
View controller code
for (Details *sav in _importArray )
{
NSLog(#"High :- %# ", sav.min); //THIS LIST ALL MY VALUES CORRECTLY
}
NSMutableArray *newArray = [_importArray mutableCopy];
int totalSum = 0;
for(int i=0; i<[newArray count];i++)
{
totalSum = totalSum + [[newArray objectAtIndex:i] intValue];
}
NSLog(#"Total:%d",totalSum);
In the view controller I get the error
[Details intValue]: unrecognized selector sent to instance ,
I am assuming I am getting this error because min is not declared right, But I am unsure how to do it other wise. Any help offered would be appreciated.
Thank you
You are asking for the selector intValue, but you should be asking for the selector min.
totalSum = totalSum + [[[newArray objectAtIndex:i] min] intValue];
You were pretty close in the first block you posted:
for (Details *sav in _importArray )
{
NSLog(#"High :- %# ", sav.min); //THIS LIST ALL MY VALUES CORRECTLY
}
Then:
int totalSum = 0;
for (Details *sav in _importArray )
{
totalSum += [sav.min intValue];
}
Incidentally, why are you asking for the intValue of something that you initially wrote was a float?
I have a 'winners screen' in a mini-golf scorecard app I'm making. I have two arrays that store 1). The player's names and 2). Their scores.
playerNameArray = [[NSMutableArray alloc]initWithObjects:playerOneName.text, playerTwoName.text, playerThreeName.text, playerFourName.text, playerFiveName.text, playerSixName.text, nil];
scoreArray = [[NSMutableArray alloc]initWithObjects:playerOneStat.text, playerTwoStat.text, playerThreeStat.text, playerFourStat.text, playerFiveStat.text, playerSixStat.text, nil];
I'm able to find the name of the player that scored the lowest with this:
int winner = [[scoreArray valueForKeyPath:#"#min.intValue"] intValue];
NSLog(#"the winner scored %d", winner);
NSString *string = [NSString stringWithFormat:#"%d", winner];
NSUInteger indexOfTheObject = [scoreArray indexOfObject:string];
NSString *playerString = [playerNameArray objectAtIndex:indexOfTheObject];
NSLog(#"The winner name is %#", playerString);
This works if there's only one player, but if there are two or more players that scored the same lowest score it can only display the name of the player that is first in the array order. How would I be able to modify the code to create a list/array of winners that share the same score?
Since you have two "parallel" arrays, perhaps the simplest thing is to have a loop, like this:
for (int i = 0 ; i != scoreArray.count ; i++) {
if (scoreArray[i].intValue == winner) {
NSLog(#"Winner %# at index %d.", playerNameArray[i], i);
}
}
If you followed a more traditional approach and created an object representing a player, say,
#interface Player : NSObject
#property (nonatomic, readonly) NSString *name;
#property (nonatomic, readonly) int score;
-(id)initWithName:(NSString*)name andScore:(int)score;
#end
then you would be able to use a predicate to do a search, and retrieve all winners in a single shot.
I would do it using NSMutableDictionary instead of two arrays.
//NSMutableDictionary to store player scores rather than two arrays
NSMutableDictionary *players = [NSMutableDictionary new];
//Each player name is a key with their score stored as an NSNumber
[players setObject:#(10) forKey:#"John"];
[players setObject:#(15) forKey:#"Tim"];
[players setObject:#(20) forKey:#"Phil"];
[players setObject:#(17) forKey:#"Kurt"];
[players setObject:#(19) forKey:#"Cory"];
[players setObject:#(10) forKey:#"Robert"];
//Find the lowest score out of the bunch
NSNumber *theLowestScore = [[players allValues] valueForKeyPath:#"#min.intValue"];
//There could be multiple lowest score players so this will get everyone that is associated with the lowest score
NSArray *playersWithLowestScores = [players allKeysForObject:theLowestScore];
//Print out the players name who have the lowest score
[playersWithLowestScores enumerateObjectsUsingBlock:^(NSString *playerName, NSUInteger idx, BOOL *stop) {
NSLog(#"%#", playerName);
}];
Just add a for loop to your code. Other things are fine. Have not tested the code though. Check for formatting errors:
int winner = [[scoreArray valueForKeyPath:#"#min.intValue"] intValue];
NSLog(#"the winner scored %d", winner);
NSString *string = [NSString stringWithFormat:#"%d", winner];
for (int i=0; i<scoreArray.count; i++) {
if ([scoreArray[i] isEqualToString:string]) {
[indices addObject:#(i)];
}
}
NSLog(#"%#",indices);
for (int i=0; i<indices.count; i++) {
//print all player names with lowest score
NSLog(#"The winner(s) are:");
NSLog(#"%#,",playerNameArray[indices[i].intValue]);
}