Get Item from List by Position [Example]

##Macro Library   Get Item from List by Position [Example]


####DOWNLOAD:
<a class="attachment" href="/uploads/default/original/2X/e/e1a8f9d135be42376e25f6fc568dafbd92450069.kmmacros">Get Item from List by Position [Example].kmmacros</a> (4.1 KB)

---

###ReleaseNotes

Simple example of how to read a list from a KM Variable, or a file, and then select the desired item by position in the list.

Created in response to this question:
https://forum.keyboardmaestro.com/t/how-to-create-and-call-variable-as-function/5021

---

<img src="/uploads/default/original/2X/f/f28155c0f119708e703396468f2a6ff78d94f64e.png" width="459" height="1190">

###Script

```javascript


main()

function main() {

var app = Application.currentApplication()
app.includeStandardAdditions = true

var kmeApp = Application("Keyboard Maestro Engine");

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// #### CHOOSE THE METHOD FOR SOURCE ####
//   Comment out or Delele the unused method

//#1 -- KM VARIABLE
var zipData = kmeApp.getvariable("zipData");

//  OR

//#2 -- FILE

/*
var pathInputFile = app.chooseFile({
    prompt: "Select the ZipCode file",
    type: "text"
    })
//--- READ THE FILE CONTENTS ---
var zipData = app.read(pathInputFile)
*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



//----- NOW, CONVERT ZIP DATA TO ZIP LIST ---

var zipList = zipData.split("\n");
var zipCodePos = kmeApp.getvariable("zipCodePos");  // must SET this KM Var PRIOR to Executing Script

var zipCode = zipList[zipCodePos-1];

kmeApp.setvariable('zipCode', { to: zipCode });

//  OR
// just return the zipCode to a Execute Script Action with Variable

return zipCode


  
} // END function main

//--=============== END OF MAIN SCRIPT ==============



```
1 Like

Or, as AppleScript:

__Tmp.kmmacros (3.0 KB)

3 Likes

Very nice, Tom. Great compaction, but still very readable.

Sorry, but I couldn’t resist creating the compact JXA version:

(function(){
  var kmeApp = Application("Keyboard Maestro Engine");
  return (kmeApp.getvariable("zipData")).split("\n")[kmeApp.getvariable("zipCodePos")-1];
})();
1 Like

:blush:‌‌‌‌

Of course, that's for someone who already knows the answer. LOL

For learning, I still think my verbose version is best. :wink:

Well, I managed to bloat it a bit:

:wink:

Doesn't make much difference here (except perhaps to readability ?) but it's generally worth caching methods of an object if they are going to be used repeatedly.

(Each dot is a fetch across an interface)

For example:

(function () {
    var getVar = Application("Keyboard Maestro Engine").getvariable;
    
    return (getVar("zipData")).split("\n")[getVar("zipCodePos") - 1];
})();

It's not that I doubt you... no wait, I do doubt you. :slight_smile: Can you show me a legitimate example where this would make a noticeable difference, with scripts used with KM?

1 Like

I think it makes a noticeable difference here :slight_smile:

I do agree with you to the extent that I think run-time performance is generally a frivolous preoccupation for personal desktop scripting, and not something that I would chase except on a very wet weekend.

On the other hand, that’s an argument for not caching the reference to the application object either – something which we all seem to do as a kind of reflex, and because the cognitive processing costs are lower, both in the reading and the writing.

Here, if you’re going to cache at all, I would personally prefer to cache the reference to the method, rather than just the reference to the application object.

I wouldn’t be particularly interested in changing anyone else’s view, but for me this is a bit cleaner and more readable, and for some reason (don’t tell Peter) I don’t entirely like the all-lower-case .getvariable method name :slight_smile:

return (getVar('zipData')).split('\n')[getVar('zipCodePos') - 1]

(Also, I notice that scripters don’t always seem be very keenly aware of functions as first-class objects in themselves – they may not think of binding a local name to a function/method in the way that they would automatically do with a number or string, for example.)

I have not done any timing studies, but I can say that when I have used the below function I have not noticed any material delays. The response is near instantaneous (from the end user's perspective).

While you might be able to shave some milliseconds off execution time, IMO it is not worth the extra programming effort (and the potential introduction of bugs) to use "caching methods of an object".

I suppose if I were designing a system where this function is a low level function where the call to the object method could be done hundreds or thousands of times, then it might be worth optimizing the code. But that is rarely the case with the scripts used in KM macros.

This function loops through many KM variables as defined in the pObject parameter, which came from a JSON.

function setKMVarsFromObject(pObject, pVarPrefix) {

    var kme = Application("Keyboard Maestro Engine");
    var prefix = pVarPrefix || "";
    
    var numKMVars = 0
    var KMVars = "";
    
    Object.keys(pObject).forEach(function(key) {
        //console.log(prefix + key + ': ' + pObject[key]);
        numKMVars += 1;
        KMVars += "\n" + prefix + key;
        
        kme.setvariable(prefix + key, { to: pObject[key] });
    });

  return "Num of KM Variables Set: " + numKMVars + KMVars;

If I remember correctly, this function is based on large part on a script written by @DanThomas. Thanks, Dan. Your script has been very useful.

IIRC, I did write something like that, but of course you've changed the variable names (which I would do if the roles were reversed, so no worries there).

The code I personally use most of the time actually does wrap the "setvariable" function, although it doesn't cache it (but it could). I probably decided that for what we were talking about, it was better to provide less code than more.

There again, mileage will vary, and the stakes are low to negligible, but instead of caching the reference to the application object, as you are already doing (not strictly necessary either, and you might be hard pressed, there too, to notice the performance benefit), you might as well cache where it counts, save a little typing, and remove a little noise.

var setVar = Application('Keyboard Maestro Engine').setvariable;

...

	setVar(prefix + key, { to: pObject[key] });

I suppose "noise" and "readability" is in the eyes of the beholder. :wink:
Clearly we have different views on this.

for me, this is explicitly clear:

kme.getvariable("KM_Var_Name");

whereas, with this, I have to search for the getVar() code to learn exactly what it does:

getVar("KM_Var_Name");

With the function, I don't immediately know that it is associated with KM, nor do I know what other processing it may, or may not do.

Fair enough, though that would also be an argument for writing

Application('Keyboard Maestro Engine')

rather than just

kme

(The runtime difference, though, is only 20% faster for the getVar version – clearly not essential :slight_smile: )

Thanks for your posts sharing your insight. It’s always good to learn how to write efficient code, even without any huge performance gain.

1 Like

I agree – exploring efficiency can often build insight into underlying processes; useful, in turn, in debugging and general problem-solving.

Perhaps. But "efficient code" can often come at the expense of readability and maintainability.

"Efficient code" can often be difficult to read/understand by others, and even by the original author months later. I first encountered this decades ago, when the "expert" programmers wanted to use assembly language for small performance gains.

Reducing readability and maintainability for the benefit of a small performance gain does not seem wise to me.

But each of us is clearly free to choose the style each prefers.

Since this is my topic, specifically about posting a macro, may I ask you guys who wish to further discuss optimizing code to start a new thread. Thanks.

Exactly same could be said of "kme", which you don't need to cache either :slight_smile:

(You can just choose a variable name which you find mnemonic, as you have done with kme – perhaps kmGetVar ?)