Calculating group row at run time of a NSTableView - objective-c

I have a NSTableView, filled with song titles.
The songs are ordered by artists, so I can display the artist name as a row view.
Code
Until now, I had to iterate through the table data and manually add the artists to the array.
NSMutableArray *songsAndArtists = [NSMutableArray array];
Artist *artist;
for (Song *song in self.songs) {
if (artist != song.artist) {
artist = song.artist;
[songsAndArtists addObject:artist];
}
[songsAndArtists addObject:song];
}
This can be really slow if you have 1'000 - 5'000 songs.
I haven't really figured out how I could speed up this process.
Does anybody have an idea how to calculate this at runtime?
EDIT
I'll try to clarify what I meant:
NSTableView supports group rows. My table view displays all the songs in CoreData.
I'd like to use the group rows to display the artist of the songs, like in the print screen I added above.
To do this I have to provide an array with an artist, followed by it's songs, followed by the next artist, and so on.
The code above shows how to insert the artists, but it's a pretty time-consuming process.
So my question, how can I speed this up?
HINT
Like nielsbot and Feloneous Cat suggested, iterating through all the artists won't work for me.
The user also has the option to search though the library.
Therefore, not all the songs should actually appear in the list.
Solution
I just want you to let you know what the problem was:
The problem was, that I actually compared via NSString in the previous version.
Pretty stupid fault...
It takes about 0.1 seconds or less which is great:
tableData = [self addGroupRowsToArray:[self allSongs] withKeyPath:#"artist"];
- (NSArray *)addGroupRowsToArray:(NSArray *)array withKeyPath:(NSString *)keyPath {
NSMutableArray *mixedArray = [NSMutableArray array];
id groupRowItem;
for (id arrayItem in array) {
if (groupRowItem != [arrayItem valueForKeyPath:keyPath]) {
groupRowItem = [arrayItem valueForKeyPath:keyPath];
[mixedArray addObject:groupRowItem];
}
[mixedArray addObject:arrayItem];
}
return mixedArray;
}

Well, i've tried to reproduce your problem. I don't know where do you get data from so i've faked them just for performance test. I also tested the code on iPod 4gen, and it works without any lag. (tried either 50000 rows and it only hang upon start, then worked perfectly)
In general my approach differs from yours with data structures. So you use arrays, and i use dictionaries. Still i think if i used arrays there should be no issues anyway. Maybe i am getting wrong what you've asked?
Still, my approach seems to be better suitable for search to work(sry i've been too bothered to try to implement it). Indeed, now the search to take place, whole sections may be excluded without checking songs, unless you need to search songs of course, anyway this gives more flexibility.
okay here is a link: https://github.com/igorpakushin/BigList
will be glad to here some feedback,
thank you
regards,
Igor

Is this faster?
-(NSArray*)songsAndArtists:(NSArray*)allArtists
{
NSMutableArray * result = [ NSMutableArray array ] ;
for( Artist * artist in allArtists )
{
[ result addObject:artist ] ;
[ result addObjectsFromArray:artist.songs ] ;
}
return result ;
}
If you are getting "all artists" from Core Data, you can tell it to prefetch the objects in the songs relationship, which will speed things up further.
-(NSArray*)allArtists
{
NSFetchRequest * request = [ NSFetchRequest fetchRequestWithEntityName:#"Artist" ] ;
[ request setRelationshipKeyPathsForPrefetching:#[ #"songs" ] ] ;
...
return results ;
}

So, let's back up and look at what you REALLY have. You already have all the information that you need residing in songs. Why are you in essence duplicating it merely to break out the artist?
Think of it this way, if you have the following songs (song/artist)
(0) "Death Eater", "Raging Machine Code"
(1) "Interrupt", "Raging Machine Code"
(2) "Panic", "Times Square Revolution"
(3) "New Years", "Times Square Revolution"
(4) "Toast", "Ed & Billy's Time Machine"
(5) "Surge", "Quiet Cat"
(6) "Surveil", "Quiet Cat"
What is wrong with this picture? We have the same information duplicated. Ideally, we want to have something like this:
"Raging Machine Code" -> has an array that contains
"Death Eater"
"Interrupt"
"Times Square Revolution" -> has an array that contains
"Panic"
"New Years"
"Ed & Billy's Time Machine" -> array that contains
"Toast"
"Quiet Cat" -> array that contains
"Surge"
"Surveil"
For UITableView this makes things trivial - we tell it how many sections (four) and for each section how many songs. To generate the cell, we get an NSIndexPath that tells us the section and the row (section would be the artist and the row would be the song for that artist).
NSTableView doesn't do that. It gives us a row. However, if we do some work on the front end (i.e. the song list) then we can assure that life will be beautiful and fast (or at least faster). The key is to pre-calculate the number of songs per artist and store it.
So assume we are asked to display row 5. "Raging Machine Code" is 0-2 (artist, song, song). "Times Square Revolution" is 3-5. Ah! 5 is the last song, so we display "New Years"!
Try another one, assume we are to display row 6. "Raging..." was 0-2, "Times..." was 3-5, "Ed & Billy's" is 6-7. Bingo, we need to display the artist, "Ed & Billy's Time Machine"!
The idea is that we do much of this prework AHEAD of actually displaying the data. Looping through artists will be far faster than looping through ALL the songs. Plus now you are only doing simple math - no need to move things.
Sometimes how you store your data means the difference between success and failure. Whenever you see yourself having to "redefine a data structure" that usually means your data structure is faulty - it may be simple, but it causes more effort.
Hopefully this was helpful and didn't end up being "TLTR" (too long to read).

Related

Trying to translate Object-C into Applescriptobjc for instagram post finder

So I have this Objective-C code it does something that I had been trying to wrap my head around with plain Applescript, and also tried and failed with some python that I tried (and failed at). I'd post the Applescript I have already tried, but it is essentially worthless. So I am turning to the AppleScript/ASOBJC gurus here to help with a solution. The code is to reverse engineer an instagram media ID to a post ID (so if you have a photo that you know is from IG you can find the post ID for that photo).
-(NSString *) getInstagramPostId:(NSString *)mediaId {
NSString *postId = #"";
#try {
NSArray *myArray = [mediaId componentsSeparatedByString:#"_"];
NSString *longValue = [NSString stringWithFormat:#"%#",myArray[0]];
long itemId = [longValue longLongValue];
NSString *alphabet = #"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
while (itemId > 0) {
long remainder = (itemId % 64);
itemId = (itemId - remainder) / 64;
unsigned char charToUse = [alphabet characterAtIndex:(int)remainder];
postId = [NSString stringWithFormat:#"%c%#",charToUse , postId];
}
} #catch(NSException *exception) {
NSLog(#"%#",exception);
}
return postId;}
The code above comes from an answer on another SO question, which can be found here:
Link
I realize it is probably asking a lot but I suck at math so I don't really "get" this code, which is probably why I can't translate it to some form of Applescript myself! Hopefully I will learn something in this process.
Here is an example of the media ID the code is looking for:
45381714_262040461144618_1442077673155810739_n.jpg
And here is the post ID that the code above is supposed to translate into
BqvS62JHYH3
A lot of the research that went into these "calculators" is from this post from 5 years ago. It looks like the 18 digit to 10 digit ratio that they point out in the post is now an 11 to 19 ratio. I tried to test the code in Xcode but got an build error when I attempted to run it. Given that I am an Xcode n00b that is not surprising.
Thanks for your help with this!
Here's an (almost) "word-for-word" translation of your Objective-C code into ASObjC:
use framework "Foundation"
use scripting additions
on InstagramPostIdFromMediaId:mediaId
local mediaId
set postId to ""
set mediaId to my (NSString's stringWithString:mediaId)
set myArray to mediaId's componentsSeparatedByString:"_"
set longValue to my NSString's stringWithFormat_("%#", myArray's firstObject())
set itemId to longValue's longLongValue()
set alphabet to my (NSString's stringWithString:(("ABCDEFGHIJKLMNOPQRSTUVWXYZ" & ¬
"abcdefghijklmnopqrstuvwxyz0123456789-_")))
repeat while (itemId > 0)
set remainder to itemId mod 64
set itemId to itemId div 64
set unichar to (alphabet's characterAtIndex:remainder) as small integer
set postId to character id unichar & postId
end repeat
return postId
end InstagramPostIdFromMediaId:
By "almost", I mean that every Objective-C method utilised in the original script has been utilised by an equivalent call to the same Objective-C method by way of the ASObjC bridge, with two exceptions. I also made a trivial edit of a mathematical nature to one of the lines. Therefore, in total, I made three operational changes, two of these technically being functional changes but which end up to yielding identical results:
to replace (itemId - remainder) / 64 with itemId div 64
The AppleScript div command performs integer division, which is where a number given by regular division is truncated to remove everything after the decimal point. This is mathematically identical to what is being done when the remainder is subtracted from itemId before performing regular dividing.
to avoid the instance where stringWithFormat: is used to translate a unicode character index to a string representation
NSString objects store strings as a series of UTF-16 code points, and characterAtIndex: will retrieve a particular code point from a string, e.g. 0x0041, which refers to the character "A". stringWithFormat: uses the %c format specifier to translate an 8-bit unsigned integer (i.e. those in the range 0x0000 to 0x00FF) into its character value. AppleScript bungles this up, although I'm uncertain how or why this presents a problem. Unwrapping the value returned by charactertAtIndex: yields an opaque raw AppleScript data object that, for example, looks like «data ushr4100». This can happily be coerced into a small integer type, correctly returning the number 65 in denary. Therefore, whatever goes wrong is likely something stringWithFormat: is doing, so I used AppleScript's character id ... function to perform the same operation that stringWithFormat: was intended to do.
myArray[0] was replaced with myArray's firstObject()
Both of these are used in Objective-C to retrieve the first element in an array. myArray[0] is the very familiar C syntax that can happily be used in native Objective-C programming, but is not available to AppleScript. firstObject is an Objective-C method wrapping the underlying function and making it accessible for use in any Objective-C context, but also likely performs some additional checks to make it suitably safe to use without too much thought. As far as we're concerned in the AppleScript context, the result is identical.
With all that being said, supplying a mediaId of "45381714_262040461144618_1442077673155810739_n.jpg" to our new ASObjC handler gives this result:
"CtHhS"
rather than what you stated as the expected result, namely "BqvS62JHYH3". However, it's easy to see why. Both scripts are splitting the mediaId into components ("text items") at every occurrence of an underscore. Then only the first of these goes on to be used by either script to determine the postId. With the given mediaId above, the first text item is "45381714", which is far too short to be valid for our needs, hence the short length of the erroneous result above. The second text item is only 15 digits (characters) long so, too, is not viable. The third text item is 19 characters long, which is of the correct length.
Therefore, I replaced firstObject() in the script with item 3. As you can guess, instead of retrieving the first item from the array of text items (components) stored in myArray, it retrieves the third, namely "1442077673155810739". This produces the following result:
"BQDSgDW-VYA"
Similar, but not the identical to what you were expecting.
For now, I'll leave this with you. At this point, I would usually have compared this with your own previous attempts, but you said they were "worthless" so I'm assuming that this at least provides you with a piece of translated code that works in so far as it performs the same operations as its Objective-C counterpart. If you tell us what the nature of the actual hurdles you were facing are, that potentially lets me or someone else help further.
But since I can say with confidence that these two scripts are doing the same thing, then if the original is producing a different output with identical input, then that tells us that the data must be mutating at some point during its processing. Given that we are dealing with a number with an order of magnitude of 10¹⁹, I think it's very likely that the error is a result of floating-point precision. AppleScript stores any integers with absolute value up to and including 536870911 as type class integer, and anything exceeding this as type class real (floating point), so will be subject to floating-point errors.

Linking Text to an Integer Objective C

The goal of this post is to find a more efficient way to create this method. Right now, as I start adding more and more values, I'm going to have a very messy and confusing app. Any help is appreciated!
I am making a workout app and assign an integer value to each workout. For example:
Where the number is exersiceInt:
01 is High Knees
02 is Jumping Jacks
03 is Jog in Place
etc.
I am making it so there is a feature to randomize the workout. To do this I am using this code:
-(IBAction) setWorkoutIntervals {
exerciseInt01 = 1 + (rand() %3);
exerciseInt02 = 1 + (rand() %3);
exerciseInt03 = 1 + (rand() %3);
}
So basically the workout intervals will first be a random workout (between high knees, jumping jacks, and jog in place). What I want to do is make a universal that defines the following so I don't have to continuously hard code everything.
Right now I have:
-(void) setLabelText {
if (exerciseInt01 == 1) {
exercise01Label.text = [NSString stringWithFormat:#"High Knees"];
}
if (exerciseInt01 == 2) {
exercise01Label.text = [NSString stringWithFormat:#"Jumping Jacks"];
}
if (exerciseInt01 == 3) {
exercise01Label.text = [NSString stringWithFormat:#"Jog in Place"];
}
}
I can already tell this about to get really messy once I start specifying images for each workout and start adding workouts. Additionally, my plan was to put the same code for exercise02Label, exercise03Label, etc. which would become extremely redundant and probably unnecessary.
What I'm thinking would be perfect if there would be someway to say
exercise01Label.text = exercise01Int; (I want to to say that the Label's text equals Jumping Jacks based on the current integer value)
How can I make it so I only have to state everything once and make the code less messy and less lengthy?
Three things for you to explore to make your code easier:
1. Count from zero
A number of things can be easier if you count from zero. A simple example is if your first exercise was numbered 0 then your random calculation would just be rand() % 3 (BTW look up uniform random number, there are much better ways to get a random number).
2. Learn about enumerations
An enumeration is a type with a set of named literal values. In (Objective-)C you can also think of them as just a collection of named integer values. For example you might declare:
typedef enum
{
HighKnees,
JumpingJacks,
JogInPlace,
ExerciseKindCount
} ExerciseCount;
Which declares ExerciseCount as a new type with 4 values. Each of these is equivalent to an integer, here HighKnees is equivalent to 0 and ExerciseKindCount to 3 - this should make you think of the first thing, count from zero...
3. Discover arrays
An array is an ordered collection of items where each item has an index - which is usually an integer or enumeration value. In (Objective-)C there are two basic kinds of arrays: C-style and object-style represented by NSArray and NSMutableArray. For example here is a simple C-style array:
NSString *gExerciseLabels[ExerciseKindCount] =
{ #"High Knees",
#"Jumping Jacks",
#"Jog in Place"
}
You've probably guessed by now, the first item of the above array has index 0, back to counting from zero...
Exploring these three things should quickly show you ways to simplify your code. Later you may wish to explore structures and objects.
HTH
A simple way to start is by putting the exercise names in an array. Then you can access the names by index. eg - exerciseNames[exerciseNumber]. You can also make the list of exercises in an array (of integers). So you would get; exerciseNames[exerciseTable[i]]; for example. Eventually you will want an object to define an exercise so that you can include images, videos, counts, durations etc.

Objective-c dynamically loading more UITableViewCells

I am writing a iPhone app that calls a web service. Let say the web service returns 1000 elements in the json object. I don't want to load all 1000 of them since parsing can take some time. What I would like to do is load the first 15 elements of the NSDictionary that I created from the json object and then when the user scrolls to the bottom of the tableview have a 16th row that says "load more....". Since I already have all of the data stored in the NSDictionary object is there a way to break this up so that it returns the 15, then the user clicks "load more...." and it loads the next 15 and continues until there are no more elements in the NSDictionary? I can present examples of my code but I am wondering if anyone has an example of how to accomplish this. Thanks in advance for any assistance.
i think a UITableView does this for you. Only the visible cells are ever constructed. Their memory is then swapped with the next rows as you scroll down. I don't see a point in re-inventing the wheel.
Also as a note, only the visible cells are "parsed" as you put it. It will not construct a cell for every item in your datasource on load.
Assuming I hear you correctly I would say you just need a little tricky logic to get this working
I would simply maintain an index offset which I would multiply by the amount of rows you want to show at any time(15 in your case)
Your logic is to always return the amount of rows that you want to allow + 1 for the final. ex: return _indexOffset * 15 + 1; //for your numberOfRowsForSection
In your didSelectIndexPathOf you check if you're the last row:
if(indexPath.row == _indexOffset * 15)
{
_indexOffset ++;
[tableView reloadData];
}
This isn't an exact answer but I think it can get you started

Handling player on turn with Objective C

I'm creating a simple app which has a list of characters and a list of (4) players, the players is simply a reference to a playable character.
I'm stuck on how to do the following:
Make a reference to the current player on turn
Find out who the next player on turn is
Handling the last player so that it will return to the first player on turn.
Ideally, I would like to be able to do AFTER, FIRST, LAST BEFORE commands on a NSMutableArray, of these I'm able to do lastObject, but there is no firstObject, afterObject, etc.
I believe you can fake BEFORE,AFTER,FIRST commands with objectAtIndex; but ideally I do not want to rely on numeric references because it could be incorrect -- also if its mutable, the size will always change.
Typically, I would be able to do the following in Pseudocode.
Global player_on_turn.player = Null //player_on_turn is a pointer to the player object
; Handle next player on turn
If (player_on_turn.player = Null) Then Error("No player on turn assigned")
If (sizeOf[playerList]==0) Then Error("There are no players in the game")
If After player_on_turn = null Then
; We reset it
player_on_turn.player = First player
Else
; Move to the next player on turn
player_on_turn.player = After player_on_turn.player
End If
With this in mind, what is the best strategy to help handle a player on turn concept as described in the 1-2-3 example above.
Thanks
It probably doesn’t matter what data structure you’re using - at some level you will have to rely on a numerical index (except if you are using linked lists). And this is alright. If you don’t want to use it in your main game implementation that is alright, too. Just create a class that encapsulates those things. First think of the operations you need it to support. My guess here would be those:
- (PlayerObject *) currentPlayer;
- (void) startNextTurn;
If you have this you can write your game using those primitives and worry about how to best implement that later. You can change those at any time without breaking your actual game.
My recommendation would be something like this:
- (PlayerObject *) currentPlayer; {
return [players objectAtIndex: currentPlayerIndex];
}
- (void) startNextTurn; {
++currentPlayerIndex;
if (currentPlayerIndex >= [players count]) {
currentPlayerIndex = 0;
}
}
Using the index there is OK, even if the array is mutable. If you have methods that change the player array they also can take care of the necessary changes to the currentPlayerIndex.
Having this object makes it easy to write unit tests. The global variable you suggest in your pseudo-code makes it impossible to write meaningful unit tests.
Use a State Pattern for the main runloop of the software. Draw it out as a diagram and create variables to control which state the system is in.
You should use a circular list of the players to return current, next, and previous players.
This is also a great question for GameDev on the Stack Exchange.
PS
CocoaHeads puts out a relatively nice set of data objects, including a circular buffer.

How to properly return a value from a method and have it available immediately after application loads?

first post here so sorry for the length of it. I've been lurking and learning a lot so far but now I have to step in and ask a question. I have read numerous posts here as advised in the FAQs, but I couldn’t find exactly the answer I’m looking for.
Before anything else, let me just say that I'm a total beginner in programming (let alone Objective-C) so please excuse me for any misuse of the terminology. Same goes for any funny english as english not my native language.
I'm building an unit conversion application with a main window containing (among other stuff) two popUpButtons. I'm using indexOfSelectedItem from both popUpButtons in order to calculate a float value (I'm getting the indexes initially in the AwakeFromNib and later in the pop up buttons controller method, when the user change selection).
My problem consists of two parts: first, the code for calculation of that float is pretty massive as I'm comparing every combination of the two indexes of selected items. And second, I would need to have the calculated float value available immediately after launch as the user might want to perform a conversion before using any of the window popUpButtons (otherwise I would put the calculation code in a -(IBAction) method).
So, I'm trying with the following code for calculation of the float value:
#interface MyClass: NSObject
float calculatedFloat;
-(void)setCalculatedFloat:(float)calcFl;
-(float)calculatedFloat;
#implementation MyClass
-(void)setCalculatedFloat:(float)calcFl {
calcFl = 1.0; // I'm simplifying, this is where I'd like to perform calculation
}
-(float)calculatedFloat {
return calculatedFloat;
}
Now, for the first part of my problem, when I use the calculatedFloat in another method, say:
-(void)printIt {
NSLog(#"Calculated float equals: %.2f", calulatedFloat);
}
all I receive in Debugger is 0.00.
First question would be: if this is not working, how do I properly access this value from within another method?
For the second part of the problem, I'm using -(void)AwakeFromNib; to set up popUpButtons etc. right after the launch but I really wouldn't like to put all of the float calculation code in it only to repeat it somewhere else later.
So the second question would be: is this even possible what I'm trying to achieve? Further more, do I need to move this calculation code to another class? If so, how can I make that other class aware of the indexOfSelectedItem from a popUpButton?
Sorry for the lengthy post and possibly confusing and silly questions. I hope you didn't have to cringe your teeth too much while reading! :)
Thanks!
-(void)setCalculatedFloat:(float)calcFl {
calcFl = 1.0; // I'm simplifying, this is where I'd like to perform calculation
}
This doesn't show up when you print it later because you assigned to the variable holding the new value, not the variable for the value of the property. You need to assign to your calulatedFloat instance variable.
(You typo'ed that variable name, BTW.)
You should move the calculating into another method, and send yourself that message from awakeFromNib and from anywhere that needs to cause recalculation. That method should call setCalculatedFloat: with the calculated value—i.e., setCalculatedFloat: should be just a simple setter. Once you make that change, you could replace your custom accessors with a #synthesize statement and let the compiler write the accessors for you.
My problem consists of two parts: first, the code for calculation of that float is pretty massive as I'm comparing every combination of the two indexes of selected items.
You might see whether you can create custom objects to set as the menu items' representedObject properties, in order to cut out this massive comparison tree. It's hard to be more specific about this without knowing what your comparison tree does.