Multiple arrays to fill tableView - ios

I'm using a tableView to display a friend list. Each type is stored in an array.
When the viewController is presented i'm requesting each array from my web-service separately.(OUT,IN and FRIENDS)
The data i'm getting back is a array of objects, each object with three properties: pictureID, profileID and name.
Each array gets displayed in a section, but the order of those sections is important.
I want the outgoing friend request to be in the first section, incoming in the second and friends in the third, provided that there are elements in those arrays, if not, all other will move one place up in the order. I hope you understand...
The way i'm doing it right now is really awkward.
For every array i get from my server i do (friendsArray, incomingArray, outgoingArray):
for (NSDictionary*dict in data){
User *friend = [User new];
friend.profileID = [dict objectForKey:#"profileID"];
friend.pictureID = [dict objectForKey:#"pictureID"];
friend.name = [dict objectForKey:#"name"];
[friendsArray addObject:friend];
}
I think this part is completely normal.
But now numberOfSectionsInTableView:
if (outgoingArray.count > 0 && incomingArray.count == 0 && friendsArray.count == 0) {
return 1;
}
if (outgoingArray.count == 0 && incomingArray.count > 0 && friendsArray.count == 0) {
return 1;
}
if (outgoingArray.count == 0 && incomingArray.count == 0 && friendsArray.count > 0) {
return 1;
}
if (outgoingArray.count > 0 && incomingArray.count > 0 && friendsArray.count == 0) {
return 2;
}
if (outgoingArray.count > 0 && incomingArray.count == 0 && friendsArray.count > 0) {
return 2;
}
if (outgoingArray.count == 0 && incomingArray.count > 0 && friendsArray.count > 0) {
return 2;
}
if (outgoingArray.count > 0 && incomingArray.count > 0 && friendsArray.count > 0) {
return 3;
}
//default
return 0;
PLEASE... there must be a way to do this more easily?
Same goes for numberOfRowsInSection:
switch (section) {
case 0:
if (outgoingArray.count > 0){
return outgoingArray.count;
}else if (incomingArray.count > 0){
return incomingArray.count;
}else if (friendsArray.count > 0){
return friendsArray.count;
}else{
return 0;
}
break;
case 1:
if (outgoingArray.count > 0 && incomingArray.count > 0){
return incomingArray.count;
}
if (outgoingArray.count == 0 && incomingArray.count > 0) {
return friendsArray.count;
}
case 2:
return friendsArray.count;
default:
return 0;
break;
}
And i still got problems when the user selects a cell. I need to know the user id of the profile that is selected because i'm transferring it to the next viewController. Right now i'm doing this the same way im getting the number of rows.

Can you create a dictionary that contains all the objects? You could do three parent keys with array children so all you would have to do is check if the dictionary contains the parent key and then check the children for the number of rows.

Related

Using multiple else if statements for NSInteger

I have a section of code, in which I want a different accessoryView for the TableView cell, based off the number for that cell's entry. The code I have set up is:
NSInteger warriors = [entry.prayerWarriors intValue];
if (warriors == 0) {
//Do nothing
//NSLog(#"0");
}
else if (0 < warriors < 50) {
cell.accessoryView = firstLevel;
// NSLog(#"50");
}
else if (51 < warriors < 100) {
cell.accessoryView = secondLevel;
// NSLog(#"100");
}
else if (101 < warriors < 500) {
cell.accessoryView = thirdLevel;
// NSLog(#"500");
}
else {
cell.accessoryView = fourthLevel;
// NSLog(#"A Lot");
}
However, it always returns only the first entry for warriors == 0. What am I doing wrong?
Rather than doing this...
else if (0 < warriors < 50) {
cell.accessoryView = firstLevel;
// NSLog(#"50");
}
do this...
else if (0 < warriors && warriors < 50) {
cell.accessoryView = firstLevel;
// NSLog(#"50");
}
EDIT
To answer your comment...you probably mean to have some <= or >= in there, as it's going to the last else when warriors equals the border of your if conditionals (50, 100 or 500).
You probably want it to look like this...
NSInteger warriors = [entry.prayerWarriors intValue];
if (warriors == 0) {
//Do nothing
//NSLog(#"0");
}
else if (0 < warriors && warriors <= 50) {
cell.accessoryView = firstLevel;
// NSLog(#"50");
}
else if (50 < warriors && warriors <= 100) {
cell.accessoryView = secondLevel;
// NSLog(#"100");
}
else if (100 < warriors && warriors <= 500) {
cell.accessoryView = thirdLevel;
// NSLog(#"500");
}
else {
cell.accessoryView = fourthLevel;
// NSLog(#"A Lot");
}
The statement
if (0 < warriors < 50)
evaluates different than you might think. The first part
(0 < warriors)
evaluates as a boolean, and that boolean will be compared to 50.
So, you need to do: if (0 < warriors && warriors < 50)
The other answers make good points about the later cases needing a logical and but if you are getting stuck at if (warriors == 0) { most likely your object entry.prayerWarriors is nil. Put a break point and print it out. (and print out it's class to make sure its as expected)
Also minor but a good habit to be in your conversion of what I'm guessing is an NSNumber isn't using the same type as your variable. Since you are writing into a NSInteger you should replace intValue with integerValue
For clarity, I prefer to put the variable first in each condition and to encapsulate each part of the condition:
if (warriors == 0) {
//Do nothing
NSLog(#"0");
}
else if ((warriors > 0) && (warriors < 50))
{
cell.accessoryView = firstLevel;
NSLog(#"50");
}
else if ((warriors > 51) && (warriors < 100)) {
cell.accessoryView = secondLevel;
NSLog(#"100");
}
else if ((warriors > 101) && (warriors < 500)) {
cell.accessoryView = thirdLevel;
NSLog(#"500");
}
else {
cell.accessoryView = fourthLevel;
NSLog(#"A Lot");
}
Just be sure you've have enough parens around the conditions.
You do not need an if cascade for this:
You can store the levels one time anywhere
NSArray *levels = #[firstLevel, secondLevel, thirdLevel, fourthLevel];
And use it indexed:
if( warriors > 0) {
cell.accessoryView = levels[(warriors-1) / 50]
}
But if you want to have an if cascade, you do not have to double check:
NSInteger warriors = [entry.prayerWarriors intValue];
if (warriors == 0) {
//Do nothing
//NSLog(#"0");
}
else if (warriors <= 50) {
cell.accessoryView = firstLevel;
// NSLog(#"50");
}
else if (warriors <= 100) {
cell.accessoryView = secondLevel;
// NSLog(#"100");
}
else if (warriors <= 500) {
cell.accessoryView = thirdLevel;
// NSLog(#"500");
}
else {
cell.accessoryView = fourthLevel;
// NSLog(#"A Lot");
}
If you are in an else, it is already tested that the preceding condition failed.(That's the meaning of else.)

Sort an array not alphabetically but with string prefix parameter

Initial Array
{
"Golf > Short game > Ballflight / Target",
"Mental > I do (behavior/skills - how) > Energy / Emotions",
"Fitness > Endurance",
"Fitness > Flexibility",
"Golf > Long game",
"Golf > Long game > Approach from fairway",
"Golf > Practice Game",
}
I want to sort the above array as starting from golf , fitness and mental.
so Resulting array is like as below
{
"Golf > Short game > Ballflight / Target",
"Golf > Long game",
"Golf > Long game > Approach from fairway",
"Golf > Practice Game",
"Fitness > Endurance",
"Fitness > Flexibility",
"Mental > I do (behavior/skills - how) > Energy / Emotions",
}
Please guide me.
I have try using for loop but i want some simple solution to parse it.
Thanks .
Example code (sort based on the first word of the line):
NSMutableArray *myArray = [#[
#"Golf > Short game > Ballflight / Target",
#"Mental > I do (behavior/skills - how) > Energy / Emotions",
#"Fitness > Endurance",
#"Fitness > Flexibility",
#"Golf > Long game",
#"Golf > Long game > Approach from fairway",
#"Golf > Practice Game",
] mutableCopy];
NSDictionary *scores = #{#"Golf":#1, #"Fitness":#2, #"Mental":#3};
[myArray sortUsingComparator:^NSComparisonResult(NSString *str1, NSString *str2) {
// first word (can be refined checking for better word break chars)
NSString *prefix1;
NSRange rangeUntilSpace1 = [str1 rangeOfString:#" "];
if (rangeUntilSpace1.location != NSNotFound)
prefix1 = [str1 substringToIndex:rangeUntilSpace1.location];
else
prefix1 = str1;
NSString *prefix2;
NSRange rangeUntilSpace2 = [str2 rangeOfString:#" "];
if (rangeUntilSpace2.location != NSNotFound)
prefix2 = [str2 substringToIndex:rangeUntilSpace2.location];
else
prefix2 = str2;
// scores (taken from the previous dictionary)
NSInteger score1 = [scores[prefix1] intValue];
NSInteger score2 = [scores[prefix2] intValue];
if (score1 && score2) {
return score1 > score2 ? NSOrderedDescending : NSOrderedAscending;
} else if (score1) {
return NSOrderedAscending; // if not in scores dictionary, put down
} else {
return NSOrderedDescending; // if not in scores dictionary, put down
}
}];
You can use - (NSArray *)sortedArrayUsingComparator:(NSComparator)cmptr
Here is an example.
NSArray *array = #[#"G", #"M", #"F"] ;
array = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSString *s1 = obj1 ;
NSString *s2 = obj2 ;
unichar u1 = [s1 characterAtIndex:0] ;
unichar u2 = [s2 characterAtIndex:0] ;
if (u1 == u2) {
return [s1 compare:s2] ;
} else {
if (u1 == 'G') {
return NSOrderedAscending ;
} else if (u1 == 'M') {
return NSOrderedDescending ;
} else {
return u2 == 'G' ? NSOrderedDescending : NSOrderedAscending ;
}
}
}] ;
NSLog(#"%#", array) ;

iBeacon Errors: Setting up major and minor id's

This seems like a relatively easy question but I am getting errors. I am working on an iBeacon project which will display different messages to the user depending on which iBeacon they are closest to. I already set up major and minor values from the transmitter and then tried calling them in a different class but I get errors saying "Expected Identifier"
self.majorLabel.text = [NSString stringWithFormat:#"%#", beacon.major];
self.minorLabel.text = [NSString stringWithFormat:#"%#", beacon.minor];
self.accuracyLabel.text = [NSString stringWithFormat:#"%f", beacon.accuracy];
if (([beacon.minor] == 1) && ([beacon.major] == 2) && (beacon.proximity == CLProximityFar)) {
self.distanceLabel.text = #"Good Bye";
self.distanceLabel.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#""]];
Is there something i am doing wrong here??
Which line is giving an error, and what is the exact text of the error? I'm guessing it's the if statement, since that's the only line of your code that includes beacon minor and major values.
What is the variable "beacon"? Is it of type CLBeacon? I would assume so.
The major and minor value properties of a CLBeacon object (and also of a CLBeaconRegion object) are NSNumbers, not integers. Thus your if statement should read
if (([[beacon.minor] integerValue] == 1) &&
([[beacon.major] integerValue] == 2) &&
(beacon.proximity == CLProximityFar))
{
}
Based on your error message: Expected identifier it appears you are mixing the property dot (.) and message syntax.
Change your code:
if (([beacon.minor] == 1) && ([beacon.major] == 2) && (beacon.proximity == CLProximityFar)) {
...
}
to either property (.) syntax (removing the [] brackets):
if ((beacon.minor == 1) && (beacon.major == 2) && (beacon.proximity == CLProximityFar)) {
...
}
or method syntax (removing the .):
if (([beacon minor] == 1) && ([beacon major] == 2) && (beacon.proximity == CLProximityFar)) {
...
}

if x > 0 but less than y

Sorry for kind of stupid question.
I use an UITextField, where I can enter some numbers.
No i use this code to detect if the entered number is 0, greater than 0, and less than 15.
if (myNr <= 15){
NSLog (#"the number is OK");
}
else if (myNr > 15)
{
NSLog(#"this number doesn't existing");
}
else if (myNr == 0)
{
NSLog(#"number should be between 1 and 15");
}
I got some errors when the number is 0.
i Need to be able to insert numbers only between 1 and 15, if the number is 0 or greater then 15, NSLog should say.
thanks
you should put if(myNr == 0) at first
if (myNr == 0)
{
NSLog(#"number should be between 1 and 15");
} else if (myNr <= 15)
{
NSLog (#"the number is OK");
}
else if (myNr > 15)
{
NSLog(#"this number doesn't existing");
}
What error do you get?
Offhand, remember: Capitalization matters. NSLog is different than nslog.
Your code reduces to:
if (myNr <= 15)
{
NSLog(#"the number is OK");
}
else
{
NSLog(#"this number doesn't existing");
}
The case for 0 is never reached.
Remember the tests are considered sequentially.

Get an array of 3 ordered values whilst favouring a specified value

Sorry for the somewhat generic title, if anyone has a better suggestion please let me know.
Basically I am writing a custom leaderboard view whereby I want to show 3 scores only. If possible it will show the current user's score in the middle but, if the user is at the top or the bottom of the list, it should still show 3 scores but itll show another users above or below the list.
e.g.
Me (If I am top, then show 2 below)
User 1
User 2
or
User 1
Me (usual case where I am in the middle of two scores)
User 2
or
User 1
User 2
Me (If I am bottom show two scores above me)
I have a function written that does the first part of this but doesnt take into account edge cases which is what I am struggling with. Can anyone please advise?
-(void)getNearbyScores:(int)score{
GCLeaderboardScore *closestScoreAbove = nil; //Custom container for GC properties
GCLeaderboardScore *closestScoreBelow = nil; //Contains playerID, score, alias etc
if ([playerScores count] == 0){ //playerScores is an NSMutableDictionary
return;
}
for (NSString* key in playerScores) {
GCLeaderboardScore *playerScore = (GCLeaderboardScore *)[playerScores objectForKey:key];
if ((closestScoreAbove == nil || closestScoreAbove->score > playerScore->score) && playerScore->score > score){
closestScoreAbove = playerScore;
}
else if ((closestScoreBelow == nil || closestScoreAbove->score < playerScore->score) && playerScore->score < score){
closestScoreBelow = playerScore;
}
}
me->score = score;
me->rank = 1;
if (closestScoreAbove != nil) {
me->rank = closestScoreAbove->rank + 1;
nearbyScores = [NSMutableArray arrayWithObjects: closestScoreAbove, me, closestScoreBelow, nil];
}
else {
nearbyScores = [NSMutableArray arrayWithObjects: me, closestScoreBelow, nil];
}
}
Assuming there is a me GCLeaderboardScore object, the method below should return an array with the desired GCLeaderboardScore objects (untested):
-(NSArray *)getNearbyScores {
if(playerScores.count==0) return nil;
// Create an array sorted by score
NSArray *sortedByScore=[playerScores sortedArrayUsingComparator: ^(id object1, id object2) {
GCLeaderboardScore *score1=object1;
GCLeaderboardScore *score2=object2;
if(score1->score < score2->score) return NSOrderedAscending;
if(score1->score > score2->score) return NSOrderedDescending;
return NSOrderedSame;
}];
// Find index of me
NSUInteger idx=[sortedByScore indexOfObject:me];
// If me not found, return nil
if(idx==NSNotFound) return nil;
// Ideally we want to show the player before and behind
idx=MAX(0,(NSInteger)idx-1);
// maxIdx will be idx+2 or index of last object if lower
NSUInteger maxIdx=MIN(sortedByScore.count-1,idx+2);
// In case we are last, show two previous results (if array large enough)
if (maxIdx > 3)
idx=MAX(0,maxIdx-3);
// And return the objects, may be 1..3 objects
return [sortedByScore subarrayWithRange:NSMakeRange(idx,maxIdx-idx+1)];
}
I assume you have an array of scores. (Actual implementation can be adapted to your code)
Initialize:
firstScoreAbove = VERY_LARGE_SCORE; secondScoreAbove = VERY_LARGE_SCORE + 1;
firstScoreBelow = -1; secondScoreBelow = -2;
Scan the array elements.
if ( newScore > myScore ) {
if ( newScore < firstScoreAbove ) {
secondScoreAbove = firstScoreAbove;
firstScoreAbove = newScore;
} else if ( newScore < secondScoreAbove ) {
secondScoreAbove = newScore;
}
} else {
// similarly for below.
}
After scanning,
If firstScoreAbove has not changed, then myScore is top and output the two below scores.
If firstScoreBelow has not changed, then myScore is lowest and output the two above scores.
Else Output firstScoreAbove, myScore, firstScoreBelow.

Resources