Wednesday, May 22, 2019

#1 2019-03-01 04:00:03 pm

maweir
Member
Registered: 2014-03-28
Posts: 10

Working through Records

Is there a better, more efficient, way to loop my Aarons_Stores list / records?  Often times I will only need the store_number from the list and just want to make sure I am doing it correctly.

set Aarons_Stores to {¬
    {store_number:"F0346", city:"Grand Junction", region:"R0002"}, ¬
    {store_number:"F0394", city:"Cortez", region:"R0002"}, ¬
    {store_number:"F0657", city:"Vernal", region:"R0001"}, ¬
    {store_number:"F0739", city:"Rock Springs", region:"R0001"}, ¬
    {store_number:"F0858", city:"Riverton", region:"R0001"}, ¬
    {store_number:"F0910", city:"Rifle", region:"R0002"}, ¬
    {store_number:"F0935", city:"Ontario", region:"R0003"}, ¬
    {store_number:"F1132", city:"Evanston", region:"R0001"}, ¬
    {store_number:"TM001", city:"Cortez", region:"R0002"} ¬
        }

repeat with x from 1 to length of Aarons_Stores
    set Store to store_number of item x of Aarons_Stores
    set region to region of item x of Aarons_Stores
    display dialog "Store #" & x & " is " & Store & " and is aligned in Region " & region
end repeat

Offline

 

#2 2019-03-02 02:57:13 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3442

Re: Working through Records

You may try :

Applescript:

set Aarons_Stores to
   {store_number:"F0346", city:"Grand Junction", region:"R0002"}, ¬
   {store_number:"F0394", city:"Cortez", region:"R0002"}, ¬
   {store_number:"F0657", city:"Vernal", region:"R0001"}, ¬
   {store_number:"F0739", city:"Rock Springs", region:"R0001"}, ¬
   {store_number:"F0858", city:"Riverton", region:"R0001"}, ¬
   {store_number:"F0910", city:"Rifle", region:"R0002"}, ¬
   {store_number:"F0935", city:"Ontario", region:"R0003"}, ¬
   {store_number:"F1132", city:"Evanston", region:"R0001"}, ¬
   {store_number:"TM001", city:"Cortez", region:"R0002"} ¬
       }
repeat with x from 1 to count Aarons_Stores
   set {theStore, theCity, theRegion} to {store_number, city, region} of item x of Aarons_Stores
   --log "Store #" & x & " is " & theStore & " in city " & theCity & " and is aligned in Region " & theRegion
   display dialog "Store #" & x & " is " & theStore & " and is aligned in Region " & theRegion
end repeat

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 2 mars 2019 09:56:59

Last edited by Yvan Koenig (2019-03-02 02:58:54 am)

Offline

 

#3 2019-03-02 03:07:43 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4869

Re: Working through Records

Hi.

What you've got is good enough for a short list like that. In general, though, if you need to access a fetched value (such as a record in your list) more than once, it's more efficient to fetch it just once and store it in a variable, from which it can be accessed more quickly:

Applescript:

set Aarons_Stores to
   {store_number:"F0346", city:"Grand Junction", region:"R0002"}, ¬
   {store_number:"F0394", city:"Cortez", region:"R0002"}, ¬
   {store_number:"F0657", city:"Vernal", region:"R0001"}, ¬
   {store_number:"F0739", city:"Rock Springs", region:"R0001"}, ¬
   {store_number:"F0858", city:"Riverton", region:"R0001"}, ¬
   {store_number:"F0910", city:"Rifle", region:"R0002"}, ¬
   {store_number:"F0935", city:"Ontario", region:"R0003"}, ¬
   {store_number:"F1132", city:"Evanston", region:"R0001"}, ¬
   {store_number:"TM001", city:"Cortez", region:"R0002"} ¬
       }

repeat with x from 1 to length of Aarons_Stores
   set thisRecord to item x of Aarons_Stores -- Fetch from the list just once and store in a variable
   set Store to store_number of thisRecord -- Read from the variable.
   set region to region of thisRecord -- Ditto.
   display dialog "Store #" & x & " is " & Store & " and is aligned in Region " & region
end repeat

There are also ways to read several properties of a record with just one command — although I'm not sure how "one command" they are beneath the bonnet!

Applescript:

repeat with x from 1 to length of Aarons_Stores
set {Store, region} to {store_number, region} of item x of Aarons_Stores
display dialog "Store #" & x & " is " & Store & " and is aligned in Region " & region
end repeat

Or:

Applescript:

repeat with x from 1 to length of Aarons_Stores
set {store_number:Store, region:region} to item x of Aarons_Stores
display dialog "Store #" & x & " is " & Store & " and is aligned in Region " & region
end repeat

When posting AppleScript code here, it would be great if you could wrap it in MacScripter's special [applescript] and [/applescript] tags. These make it appear as above when posted, in a box with a clickable link which opens it it people's default editors. There's an "Applescript" button for them just above the text field in posting pages.


NG

Offline

 

#4 2019-03-02 04:12:23 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3442

Re: Working through Records

For a long list it would be efficient to drop the use of repeat with x from 1 to length of Aarons_Stores

Applescript:

set Aarons_Stores to
   {store_number:"F0346", city:"Grand Junction", region:"R0002"}, ¬
   {store_number:"F0394", city:"Cortez", region:"R0002"}, ¬
   {store_number:"F0657", city:"Vernal", region:"R0001"}, ¬
   {store_number:"F0739", city:"Rock Springs", region:"R0001"}, ¬
   {store_number:"F0858", city:"Riverton", region:"R0001"}, ¬
   {store_number:"F0910", city:"Rifle", region:"R0002"}, ¬
   {store_number:"F0935", city:"Ontario", region:"R0003"}, ¬
   {store_number:"F1132", city:"Evanston", region:"R0001"}, ¬
   {store_number:"TM001", city:"Cortez", region:"R0002"} ¬
       }
set x to 0
repeat with aStore in Aarons_Stores
   set {theStore, theCity, theRegion} to {store_number, city, region} of aStore
   set x to x + 1
   --log "Store #" & x & " is " & theStore & " in city " & theCity & " and is aligned in Region " & theRegion
   display dialog "Store #" & x & " is " & theStore & " and is aligned in Region " & theRegion
end repeat

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 2 mars 2019 11:11:15

Offline

 

#5 2019-03-03 10:45:53 am

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 69

Re: Working through Records

If the list is fairly long, I wondered if there would be any speed difference between:

repeat with x from 1 to length of Aarons_Stores

and

set listCount to length of Aarons_Stores
repeat with x from 1 to listCount

My thought was that getting the number of list items on every loop might slow things. Thanks.

Last edited by peavine (2019-03-03 10:46:26 am)

Offline

 

#6 2019-03-03 11:24:21 am

robertfern
Member
Registered: 2011-11-29
Posts: 34

Re: Working through Records

A repeat loop only calculates the value of "length of Aarons_Stores" once at the beginning of the loop

here is an example

Applescript:

set mycount to 4

repeat with i from 1 to mycount
   display alert i giving up after 1
   set mycount to mycount - 2 -- recalculating mycount
end repeat
display alert mycount giving up after 1

As you can see, even though I'm recalculating "mycount", it still runs the repeat 4 times as this was the value of "mycount" at the start of the repeat loop

Last edited by robertfern (2019-03-03 11:27:20 am)

Offline

 

#7 2019-03-03 01:45:57 pm

peavine
Member
From:: Prescott, Arizona
Registered: 2018-09-04
Posts: 69

Re: Working through Records

Thanks robertfern for the explanation and example, which makes clear what's happening.

Offline

 

#8 2019-03-04 10:03:22 am

CK
Member
From:: UK
Registered: 2018-11-04
Posts: 78

Re: Working through Records

Yvan Koenig wrote:

For a long list it would be efficient to drop the use of repeat with x from 1 to length of Aarons_Stores

This is likely negated by having x + 1 evaluated within each iteration of your repeat loop (i.e. set x to x + 1).

If speed is important, and where there are a large number of items in a list (in the thousands), I might suggest looping through a reference to the list instead:

Applescript:

repeat with Store in (a reference to Aarons_Stores)
       log the store_number of the Store
end repeat

or:

Applescript:

repeat with Store in my Aarons_Stores
       log the store_number of the Store
end repeat

To do this with item-based access, you can put the list inside a script object, via which you would then access each item:

Applescript:

script Aarons_Stores
       property list : {"a", 2, pi, ...} -- a large list
end script

repeat with i from 1 to the length of the list of Aarons_Stores
       set Store to item i in the list of Aarons_Stores
       log the Store
end repeat

Offline

 

#9 2019-03-04 10:38:38 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3442

Re: Working through Records

CK wrote:
Yvan Koenig wrote:

For a long list it would be efficient to drop the use of repeat with x from 1 to length of Aarons_Stores

This is likely negated by having x + 1 evaluated within each iteration of your repeat loop (i.e. set x to x + 1).




Have you made tests with a long list before posting that ?


Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) lundi 4 mars 2019 17:38:23

Offline

 

#10 2019-03-05 08:00:43 am

CK
Member
From:: UK
Registered: 2018-11-04
Posts: 78

Re: Working through Records

Yvan Koenig wrote:
CK wrote:
Yvan Koenig wrote:

For a long list it would be efficient to drop the use of repeat with x from 1 to length of Aarons_Stores

This is likely negated by having x + 1 evaluated within each iteration of your repeat loop (i.e. set x to x + 1).


Have you made tests with a long list before posting that ?

I did.  I read your initial claim regarding the efficiencies of the two repeat methods, and was motivated at this point to draft a simple script that wasn't intending to give a conclusive performance evaluation in any way, but just provide a general sense of the truthfulness of that claim.

The result averaged over several runs was enough to accept your initial statement to have at least favourable odds of being true, which is all I need in this instance.  (On the assumption that your statement is true, do you happen to have an explanation to account for the disparity?  I'd be genuinely interested to know this.)

After this, I used the same script with the addition of a line that performs a single addition operation in each iteration, namely set x to x + 1.  The result averaged similarly over several runs was consistent in the way it obliterated any previous advantage one style of loop had over another, equalising their execution times.

I deleted my test script, but the premise was to iterate through a 1000-items list of integers (which were the numbers 1 to 1000), and simply perform a log command on each element.  This was, itself, put inside a repeat loop of the form repeat 100 times, to simulate an approximation of traversing a 100,000-items list that wouldn't be practical to declare at compile time.  This was done for each of the two styles of loop.

As I intimated already, this method is neither rigorous nor watertight, so I'm perfectly willing to accept a different result should anyone wish to perform a demonstrably superior test (which wouldn't be hard) that yields a result with enough confidence to refute my supposition.

Offline

 

#11 2019-03-06 11:57:49 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3442

Re: Working through Records

@CK
You are right.

I made new tests with the script :

Applescript:

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"


property longList1 : {}
on run
   my Germaine()
end run

on Germaine()
   set targetfile to (path to desktop as text) & "datas.txt"
   try
       close file targetfile
   end try
   
   tell application "System Events"
       delete file targetfile
   end tell
   
   set targetfile to targetfile as «class furl»
   set openFile to open for access targetfile with write permission
   
   # Fill a file starting from a blank one
   set i to 0
   set nbTimes to 10000
   repeat nbTimes times
       set i to i + 1
       write ("sample text " & i) & linefeed to openFile starting at eof as «class utf8»
   end repeat
   close access openFile
   
   say "read the file"
   set longList2 to paragraphs of (read targetfile)
   copy longList2 to longList1
   say "reading done"
   
   set stats to ""
   repeat 10 times
       set startDate1 to current application's NSDate's |date|()
       repeat with x from 1 to count longList2
           
           log "record #" & x & " is : " & item x of longList2
           
       end repeat
       set duration1 to -(startDate1's timeIntervalSinceNow()) as real
       set msg1 to "duration1" & tab & duration1 & tab & "seconds"
       say msg1
       
       set startDate2 to current application's NSDate's |date|()
       set x to 0
       repeat with aString in longList2
           set x to x + 1
           log "record #" & x & " is : " & aString
           
       end repeat
       set duration2 to -(startDate2's timeIntervalSinceNow()) as real
       set msg2 to "duration2" & tab & duration2 & tab & "seconds"
       say msg2
       
       set startDate3 to current application's NSDate's |date|()
       repeat with x from 1 to count my longList1
           
           log "record #" & x & " is : " & item x of my longList1
           
       end repeat
       set duration3 to -(startDate3's timeIntervalSinceNow()) as real
       set msg3 to "duration3" & tab & duration3 & tab & "seconds"
       say msg3
       
       set startDate4 to current application's NSDate's |date|()
       set x to 0
       repeat with aString in my longList1
           set x to x + 1
           log "record #" & x & " is : " & aString
           
       end repeat
       set duration4 to -(startDate4's timeIntervalSinceNow()) as real
       set msg4 to "duration4" & tab & duration4 & tab & "seconds"
       say msg4
       set stats to stats & linefeed & (msg1 & linefeed & msg2 & linefeed & msg3 & linefeed & msg4 & linefeed)
       
   end repeat
   
   
   set resultFile to (path to desktop as text) & "result.txt"
   try
       close resultFile
   end try
   tell application "System Events"
       delete file resultFile
   end tell
   set resultFile to resultFile as «class furl»
   set logFile to open for access resultFile with write permission
   write stats to logFile as «class utf8»
   close access logFile
end Germaine

It creates a text file with nbTimes lines.
Then it extract the contents 10 times using 4 schemes

(1) repeat with x from 1 to count longList1 (standard one)
(2) repeat with aString in longList1 (standard one)
(3) repeat with x from 1 to count of my longList2 (a property)
(4) repeat with aString in my longList2 (a property)

The average results with 10000 strings were:
(1) 10.849306084893 seconds
(2) 11.107601480051 seconds
(3) 7.773462458090 seconds
(4) 7.751518282023 seconds

With 100000 strings they were :
(1) 367.86496692354 seconds
(2) 373.629074811935 seconds
(3) 76.010842366652 seconds
(4) 75.601781541651 seconds

The difference between case 1 and 2  or between case 3 and 4 is small.
but the change introduced by the use of a property is serious.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mercredi 6 mars 2019 18:57:18

Offline

 

#12 2019-03-14 05:59:08 am

realLaotse
Member
Registered: 2019-02-27
Posts: 5

Re: Working through Records

Nice find with the property. Speeds up the reading of my contacts database quite a lot.
smile

Offline

 

#13 2019-03-16 09:59:11 pm

CK
Member
From:: UK
Registered: 2018-11-04
Posts: 78

Re: Working through Records

realLaotse wrote:

Nice find with the property. Speeds up the reading of my contacts database quite a lot.
smile

I don't believe the speed improvement is attributable to the use of a property, but rather—if you refer to my first reply in this thread where I suggested using either a reference to or my—is a demonstration of this recommendation in action, by the presence of my when referencing the property:

Applescript:

repeat with x from 1 to count my longList1

       log "record #" & x & " is : " & item x of my longList1

end repeat

I highly suspect that the speed improvements would reduce or disappear if you reference the property whilst omitting my.  I'm not entirely sure whether speed is affected by calling the Germaine() handler with vs without my but I'm fairly positive that there won't be a difference, and its presence is entirely superfluous in this instance.

Offline

 

#14 2019-03-17 05:19:49 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3442

Re: Working through Records

The handler Germaine() is not related to speed efficiency.
It's used so that no variables are stored in the file when the script is used.

Run this script then save it. as a Script

Applescript:

set maybe to (path to desktop as text)

Open the file with TextEdit.
At its very end you will see the path to your Desktop.

Now, run the script :

Applescript:

my Germaine()

on Germaine()
   set maybe to (path to desktop as text)
end Germaine

ans save it.
Open the file with TextEdit. You will see the name of the handler but you will not see the path to your Desktop.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) dimanche 17 mars 2019 12:19:33

Offline

 

#15 2019-03-17 05:46:19 am

StefanK
Member
From:: St. Gallen, Switzerland
Registered: 2006-10-21
Posts: 11560
Website

Re: Working through Records

If you need only to iterate the store numbers a very efficient way is to extract the values with Foundation's key-value coding.

- NSArray's arrayWithArray creates a Foundation NSArray from the AppleScript list
- valueForKey returns all values for the given key as array


Applescript:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set Aarons_Stores to
   {store_number:"F0346", city:"Grand Junction", region:"R0002"}, ¬
   {store_number:"F0394", city:"Cortez", region:"R0002"}, ¬
   {store_number:"F0657", city:"Vernal", region:"R0001"}, ¬
   {store_number:"F0739", city:"Rock Springs", region:"R0001"}, ¬
   {store_number:"F0858", city:"Riverton", region:"R0001"}, ¬
   {store_number:"F0910", city:"Rifle", region:"R0002"}, ¬
   {store_number:"F0935", city:"Ontario", region:"R0003"}, ¬
   {store_number:"F1132", city:"Evanston", region:"R0001"}, ¬
   {store_number:"TM001", city:"Cortez", region:"R0002"} ¬
       }


set storeNumbers to ((current application's NSArray's arrayWithArray:Aarons_Stores)'s valueForKey:"store_number") as list
repeat with aStore in storeNumbers
   display dialog aStore
end repeat


regards

Stefan

Offline

 

#16 2019-03-18 07:07:39 pm

CK
Member
From:: UK
Registered: 2018-11-04
Posts: 78

Re: Working through Records

Yvan Koenig wrote:

The handler Germaine() is not related to speed efficiency.
It's used so that no variables are stored in the file when the script is used.

I didn't say it was, you misread what I wrote.  I was querying the relevance of calling the handler using my, which I was speculating would not improve speed the way it does when used to reference variables or properties, thus surmising that it had no need to be there when calling the handler.

In other words, it's perfectly sensible to just do:

Applescript:

Germaine()

instead of

Applescript:

my Germaine()

in this instance.

Offline

 

#17 2019-03-19 02:49:50 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3442

Re: Working through Records

I always use My when I call an handler of mine.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 19 mars 2019 09:49:45

Offline

 

#18 2019-03-21 02:47:18 am

CK
Member
From:: UK
Registered: 2018-11-04
Posts: 78

Re: Working through Records

Yvan Koenig wrote:

I always use My when I call an handler of mine.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) mardi 19 mars 2019 09:49:45

Why?

Offline

 

#19 2019-03-21 03:53:40 am

Yvan Koenig
Member
Registered: 2006-09-14
Posts: 3442

Re: Working through Records

Because some handlers require it and behaving this way I never forget one.

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) jeudi 21 mars 2019 10:53:35

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)