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.
This question already has answers here:
Does Objective-C use short-circuit evaluation?
(4 answers)
Closed 6 years ago.
I can't test this well but I was wondering if someone might know this. Having this simple check
if ([dict objectForKey:#"key"] != nil || [a isEqualToString:b] || someIntValue> 12) { ... }
Obviously (no matter the small margin) the first check will probably be heavier than the second, and the third even lighter.
So my question is; will the binary in my app still check the other 2 statements if the first was already true? Because if so I was thinking of doing a small round checks in my code-base to tweak checks like those above to move the simple int comparison earlier in that if-statement.
p.s.
I know you can't check unexisting objects in Objective-C like you can in PHP and I also only mean existing objects in this example, not to do something hacky that object(s) in the second statement won't exist:
$a = 0; $b = '';
if ($a == 0 || $i_dont_exist > 0) {
...
}
This is called short-circuit evaluation. Objective-C is a strict superset of C, since C supports short-circuit evaluation so does Objective-C.
So if the first condition in an "or" statement is true the rest of the conditions should not be evaluated.
I'm trying to refactor a small amount of legacy non-functional-totally-procedural-old-school-"is this FORTRAN?!" code, and I'd like some input.
Since Swift 3 is getting rid of the "confusing" ++ prefix/postfix operator, along with C-style for loops, I've been hard at work updating my gross Swift 2.0 (and really gross C and Objective-C) files, and for the most part, it hasn't been too bad. However, I've run into one of those pesky do...while loops that I probably wrote after a night of heavy drinking.
Here's a slimmed down version of the original code:
BSDPath *startPath = &ctx->paths[startIdx];
BSDPath *endPath = &ctx->paths[endIdx];
BSDPath *currentPath = startPath;
do {
if (flags & CurveElement) {
// ... Some code here ...
}
} while (currentPath++ != endPath);
I've Swiftified some stuff, and now I'm here:
let startPath = ctx.paths[index]
let endPath = ctx.paths[endIndex]
var currentPath = startPath
repeat {
if flags.contains(.Curve) {
// ... Some code here ...
}
index += 1
currentPath = ctx.paths[index]
} while currentPath != endPath
As you can C, I've Swiftly converted most of it to the new nice and clean Apple of my eye. However, I can't help but wonder if there's an even cleaner way of writing this — that is, without totally throwing out the bath water and giving in to the for...in siren song. To be honest, I'm not even sure if that code runs correctly. I've never really been able to wrap my head around the control flow of the do...while statement. I mean, I get how it works, but it just seems backwards to me. But I digress.
I suppose the reason I want to know the "cleanest" way to rewrite my old stuff is that I just kind of miss being able wrap assignment statements in parentheses to kill two bytes with one var. I totally agree that the ++ operator is counterintuitive, but for all it's jankiness, it certainly did take a little of the tedium out of typing into a code editor all day. But I digress (again?!).
Anyway, that's all I've got. I would love to know of any timesavers I'm unaware of, but any thoughts, ideas, or comments are welcome and greatly appreciated. Thanks!
This question already has answers here:
Why are variables "i" and "j" used for counters?
(23 answers)
Closed 9 years ago.
I assume there is some historical/mathematical reason that whenever I write I for loop, I use i:
for (var i=0; i<10; i++) {
// do something 10 times
}
I know i is used in mathematics for summations (Σ) and products (∏). Does it just mean "index", or is there some more significant meaning?
I believe it does just mean "index" in the example mentioned.
I know it's not answering your question and probably stating the obvious but I personally would always try to give the variable name some meaning for readability and avoid this confusion when reading others code e.g.
for (int productCounter = 0; productCounter<10; productCounter++){
// do something 10 times
}
In Fortran, variables starting with letters I through M were automatically of type INTEGER, so people could write:
DO 10 J = 1, 10
...do something 10 times...
10 CONTINUE
Labels were numeric in columns 1-5; column 6 was for the continuation character (if needed), and the code started in column 7 (originally up to column 72; column 73-80 on the punched card were for the card's sequence number in the deck).
Single letter names are convenient. In Fortran, you didn't have to declare the variables before you used them.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
As far as variable naming conventions go, should iterators be named i or something more semantic like count? If you don't use i, why not? If you feel that i is acceptable, are there cases of iteration where it shouldn't be used?
Depends on the context I suppose. If you where looping through a set of Objects in some
collection then it should be fairly obvious from the context what you are doing.
for(int i = 0; i < 10; i++)
{
// i is well known here to be the index
objectCollection[i].SomeProperty = someValue;
}
However if it is not immediately clear from the context what it is you are doing, or if you are making modifications to the index you should use a variable name that is more indicative of the usage.
for(int currentRow = 0; currentRow < numRows; currentRow++)
{
for(int currentCol = 0; currentCol < numCols; currentCol++)
{
someTable[currentRow][currentCol] = someValue;
}
}
"i" means "loop counter" to a programmer. There's nothing wrong with it.
Here's another example of something that's perfectly okay:
foreach (Product p in ProductList)
{
// Do something with p
}
I tend to use i, j, k for very localized loops (only exist for a short period in terms of number of source lines). For variables that exist over a larger source area, I tend to use more detailed names so I can see what they're for without searching back in the code.
By the way, I think that the naming convention for these came from the early Fortran language where I was the first integer variable (A - H were floats)?
i is acceptable, for certain. However, I learned a tremendous amount one semester from a C++ teacher I had who refused code that did not have a descriptive name for every single variable. The simple act of naming everything descriptively forced me to think harder about my code, and I wrote better programs after that course, not from learning C++, but from learning to name everything. Code Complete has some good words on this same topic.
i is fine, but something like this is not:
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
string s = datarow[i][j].ToString(); // or worse
}
}
Very common for programmers to inadvertently swap the i and the j in the code, especially if they have bad eyesight or their Windows theme is "hotdog". This is always a "code smell" for me - it's kind of rare when this doesn't get screwed up.
i is so common that it is acceptable, even for people that love descriptive variable names.
What is absolutely unacceptable (and a sin in my book) is using i,j, or k in any other context than as an integer index in a loop.... e.g.
foreach(Input i in inputs)
{
Process(i);
}
i is definitely acceptable. Not sure what kind of justification I need to make -- but I do use it all of the time, and other very respected programmers do as well.
Social validation, I guess :)
Yes, in fact it's preferred since any programmer reading your code will understand that it's simply an iterator.
What is the value of using i instead of a more specific variable name? To save 1 second or 10 seconds or maybe, maybe, even 30 seconds of thinking and typing?
What is the cost of using i? Maybe nothing. Maybe the code is so simple that using i is fine. But maybe, maybe, using i will force developers who come to this code in the future to have to think for a moment "what does i mean here?" They will have to think: "is it an index, a count, an offset, a flag?" They will have to think: "is this change safe, is it correct, will I be off by 1?"
Using i saves time and intellectual effort when writing code but may end up costing more intellectual effort in the future, or perhaps even result in the inadvertent introduction of defects due to misunderstanding the code.
Generally speaking, most software development is maintenance and extension, so the amount of time spent reading your code will vastly exceed the amount of time spent writing it.
It's very easy to develop the habit of using meaningful names everywhere, and once you have that habit it takes only a few seconds more to write code with meaningful names, but then you have code which is easier to read, easier to understand, and more obviously correct.
I use i for short loops.
The reason it's OK is that I find it utterly implausible that someone could see a declaration of iterator type, with initializer, and then three lines later claim that it's not clear what the variable represents. They're just pretending, because they've decided that "meaningful variable names" must mean "long variable names".
The reason I actually do it, is that I find that using something unrelated to the specific task at hand, and that I would only ever use in a small scope, saves me worrying that I might use a name that's misleading, or ambiguous, or will some day be useful for something else in the larger scope. The reason it's "i" rather than "q" or "count" is just convention borrowed from mathematics.
I don't use i if:
The loop body is not small, or
the iterator does anything other than advance (or retreat) from the start of a range to the finish of the loop:
i doesn't necessarily have to go in increments of 1 so long as the increment is consistent and clear, and of course might stop before the end of the iterand, but if it ever changes direction, or is unmodified by an iteration of the loop (including the devilish use of iterator.insertAfter() in a forward loop), I try to remember to use something different. This signals "this is not just a trivial loop variable, hence this may not be a trivial loop".
If the "something more semantic" is "iterator" then there is no reason not to use i; it is a well understood idiom.
i think i is completely acceptable in for-loop situations. i have always found this to be pretty standard and never really run into interpretation issues when i is used in this instance. foreach-loops get a little trickier and i think really depends on your situation. i rarely if ever use i in foreach, only in for loops, as i find i to be too un-descriptive in these cases. for foreach i try to use an abbreviation of the object type being looped. e.g:
foreach(DataRow dr in datatable.Rows)
{
//do stuff to/with datarow dr here
}
anyways, just my $0.02.
It helps if you name it something that describes what it is looping through. But I usually just use i.
As long as you are either using i to count loops, or part of an index that goes from 0 (or 1 depending on PL) to n, then I would say i is fine.
Otherwise its probably easy to name i something meaningful it its more than just an index.
I should point out that i and j are also mathematical notation for matrix indices. And usually, you're looping over an array. So it makes sense.
As long as you're using it temporarily inside a simple loop and it's obvious what you're doing, sure. That said, is there no other short word you can use instead?
i is widely known as a loop iterator, so you're actually more likely to confuse maintenance programmers if you use it outside of a loop, but if you use something more descriptive (like filecounter), it makes code nicer.
It depends.
If you're iterating over some particular set of data then I think it makes more sense to use a descriptive name. (eg. filecounter as Dan suggested).
However, if you're performing an arbitrary loop then i is acceptable. As one work mate described it to me - i is a convention that means "this variable is only ever modified by the for loop construct. If that's not true, don't use i"
The use of i, j, k for INTEGER loop counters goes back to the early days of FORTRAN.
Personally I don't have a problem with them so long as they are INTEGER counts.
But then I grew up on FORTRAN!
my feeling is that the concept of using a single letter is fine for "simple" loops, however, i learned to use double-letters a long time ago and it has worked out great.
i asked a similar question last week and the following is part of my own answer:// recommended style ● // "typical" single-letter style
●
for (ii=0; ii<10; ++ii) { ● for (i=0; i<10; ++i) {
for (jj=0; jj<10; ++jj) { ● for (j=0; j<10; ++j) {
mm[ii][jj] = ii * jj; ● m[i][j] = i * j;
} ● }
} ● }
in case the benefit isn't immediately obvious: searching through code for any single letter will find many things that aren't what you're looking for. the letter i occurs quite often in code where it isn't the variable you're looking for.
i've been doing it this way for at least 10 years.
note that plenty of people commented that either/both of the above are "ugly"...
I am going to go against the grain and say no.
For the crowd that says "i is understood as an iterator", that may be true, but to me that is the equivalent of comments like 'Assign the value 5 to variable Y. Variable names like comment should explain the why/what not the how.
To use an example from a previous answer:
for(int i = 0; i < 10; i++)
{
// i is well known here to be the index
objectCollection[i].SomeProperty = someValue;
}
Is it that much harder to just use a meaningful name like so?
for(int objectCollectionIndex = 0; objectCollectionIndex < 10; objectCollectionIndex ++)
{
objectCollection[objectCollectionIndex].SomeProperty = someValue;
}
Granted the (borrowed) variable name objectCollection is pretty badly named too.