Saturday, September 24, 2022

#1 2022-08-04 01:40:25 pm

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2522

Convert the Text to Record (effectively)

I know how to convert following text to record using text item delimiters ":", then  the SPACE. But maybe someone know better and more effective way (using shell commands or AsObjC, or some other method).

The text is following (note that every paragraph has spaces at beginning. The site doesn't show them, but there are here. I want eliminate them from key names, as well):

Applescript:


set currentWirelessNetworkInfo to " agrCtlRSSI: -29
agrExtRSSI: 0
agrCtlNoise: -87
agrExtNoise: 0
state: running
op mode: station
lastTxRate: 59
maxRate: 72
lastAssocStatus: 0
802.11 auth: open
link auth: wpa2-psk
BSSID: 8:78:8:0:aa:5c
SSID: AndroidAP
MCS: 6
channel: 6"

Last edited by KniazidisR (2022-08-05 03:59:41 am)


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 14.1
Ram: 4 GB


Filed under: Record

Offline

 

#2 2022-08-04 05:21:37 pm

Marc Anthony
Member
From:: Dallas, TX
Registered: 2006-04-27
Posts: 1090

Re: Convert the Text to Record (effectively)

I use run script to make records.

Applescript:


set theContent to "agrCtlRSSI: -29
agrExtRSSI: 0
agrCtlNoise: -87
agrExtNoise: 0
state: running
op mode: station
lastTxRate: 59
maxRate: 72
lastAssocStatus: 0
802.11 auth: open
link auth: wpa2-psk
BSSID: 8:78:8:0:aa:5c
SSID: AndroidAP
MCS: 6
channel: 6"


set theContent to do shell script "echo " & theContent's quoted form & " | tr -d ' ' "
set {keylist, textList, recordz} to {{}, {}, {}}

repeat with index from 1 to count theContent's paragraphs
   set keylist's end to theContent's paragraph index's text 1 thru ((offset of ":" in my theContent's paragraph index) - 1)
   set textList's end to (theContent's paragraph index's text ((offset of ":" in my theContent's paragraph index) + 1) thru -1)
   set recordz to recordz & (run script "{|" & (my keylist's item index) & "|: " & (quote & my textList's item index & quote) & "}")
end repeat
recordz

--edited for brevity, list creation reduction, and potential speed improvements

Last edited by Marc Anthony (2022-08-05 06:30:56 am)

Offline

 

#3 2022-08-04 05:30:40 pm

estockly
Member
Registered: 2009-01-03
Posts: 124

Re: Convert the Text to Record (effectively)

Marc Anthony wrote:

I use run script to make records.



This is cool. I have something similar but this is faster and smoother.

If you change this line:

Applescript:

   set recordz to recordz & (run script "{|" & my keylist's item index & "|: " & (quote & my textList's item index & quote) & "}")

to this

Applescript:

   set recordz to recordz & (run script "{|" & my keylist's item index & "|: " & (quote & my textList's item index & quote) & "}")

Then you get a record with several items rather than several records each with one item.

Offline

 

#4 2022-08-04 08:23:40 pm

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

Re: Convert the Text to Record (effectively)

Here is a short version I learned from another post

https://macscripter.net/viewtopic.php?id=48212

Applescript:

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

set theContent to "agrCtlRSSI: -29
agrExtRSSI: 0
agrCtlNoise: -87
agrExtNoise: 0
state: running
op mode: station
lastTxRate: 59
maxRate: 72
lastAssocStatus: 0
802.11 auth: open
link auth: wpa2-psk
BSSID: 8:78:8:0:aa:5c
SSID: AndroidAP
MCS: 6
channel: 6"


set text item delimiters to {":"}
set theContent to paragraphs of theContent
set myRec to {}
repeat with i in theContent
   set tmp to text items of i
   set end of myRec to item 1 of tmp
   set tmp to (rest of tmp) as text
   try
       set tmp to tmp as integer
   end try
   set end of myRec to tmp
end repeat
set rawRecord to {«class usrf»:myRec}

Hmmm! I seem to be having problems with the script. It works somewhat in ScriptDebugger.
it shows properly but doesn't actually convert to record

Last edited by robertfern (2022-08-04 09:01:55 pm)

Offline

 

#5 2022-08-04 10:03:24 pm

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

Re: Convert the Text to Record (effectively)

AppleScript

Applescript:

script WifiStats
   property parent : a reference to {«class usrf»:my list's paragraphs}
   property key : a reference to AppleScript's text item delimiters
   property item : a reference to my list's text items
   property list : "        agrCtlRSSI: -29
       agrExtRSSI: 0
       agrCtlNoise: -87
       agrExtNoise: 0
       state: running
       op mode: station
       lastTxRate: 59
       maxRate: 72
       lastAssocStatus: 0
       802.11 auth: open
       link auth: wpa2-psk
       BSSID: 8:78:8:0:aa:5c
       SSID: AndroidAP
       MCS: 6\n channel: 6"

end script

tell WifiStats
   set key's contents to {linefeed, ": "}
   set its list to its item as text
   set key's contents to {{space},tab}
   set its list to its item as text
   return the contents as anything
end tell

Run Script (JXA)

Applescript:

set WifiStats to "        agrCtlRSSI: -29
       agrExtRSSI: 0
       agrCtlNoise: -87
       agrExtNoise: 0
       state: running
       op mode: station
       lastTxRate: 59
       maxRate: 72
       lastAssocStatus: 0
       802.11 auth: open
       link auth: wpa2-psk
       BSSID: 8:78:8:0:aa:5c
       SSID: AndroidAP
       MCS: 6\n channel: 6"


run script "let R=`" & WifiStats & "`" & "
       .replace(/[\\s\t]*(\\w[^:]+): (.+)\\x12?/g,
                   '\"$1\" : \"$2\" \\u{0A}'
                   ).split( '\\u{0A}' ).slice( 0, -1 ).join( ',' );
       JSON.parse( '{' + R + '}' );"
in "JavaScript"

ASObjC

Applescript:


-- Format the string first into the following format,
-- e.g. using Text Item Delimiters:

set WifiStats to "        \"agrCtlRSSI\" = \"-29\";
       \"agrExtRSSI\" = \"0\";
       \"agrCtlNoise\" = \"-87\";
       \"agrExtNoise\" = \"0\";
       \"state\" = \"running\";
       \"op mode\" = \"station\" ;
       \"lastTxRate\" = \"59\";
       \"maxRate\" = \"72\";
       \"lastAssocStatus\" = \"0\";
       \"802.11 auth\" = \"open\";
       \"link auth\" = \"wpa2-psk\";
       \"BSSID\" = \"8:78:8:0:aa:5c\";
       \"SSID\" = \"AndroidAP\";
       \"MCS\" = \"6\";
       \"channel\" = \"6\";"


-- Then the Foundation framework allows this:
use framework "Foundation"
my (NSString's stringWithString:WifiStats)'s propertyList() as record

Sorry, I'm currently too tired to do the labour of formatting the string in the last example, but it's fairly straight forward.

Last edited by CK (2022-08-05 09:21:44 am)

Offline

 

#6 2022-08-05 01:09:34 am

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2522

Re: Convert the Text to Record (effectively)

I'm sorry that I disappeared temporarily from the radar. Now I can only say one thing:

Friends, I'm impressed! big_smile

Because I did not expect that 6 options for solving this problem would appear at once. (This is a very common task faced by users when getting results from utilities, so a good solution will come in handy).

As I tested, they all work, and quite quickly, but 2 of them give the result almost instantly. I will provide the results of speed testing + my full script obtained with your help.

Thanks everyone!!!!

Last edited by KniazidisR (2022-08-05 01:09:53 am)


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#7 2022-08-05 01:35:07 am

technomorph
Member
Registered: 2017-12-14
Posts: 279

Re: Convert the Text to Record (effectively)

If you can format your string in JSON format,
You can create a NSData object from it.
The use NSJSONSerialization to create a NSDictionary from it.

https://developer.apple.com/documentati … ialization

Offline

 

#8 2022-08-05 02:22:47 am

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2522

Re: Convert the Text to Record (effectively)

Speed testing results (from fast to fastest):

13 ms : scripts from @Mark Anthony and from @Peavine

3 ms : JXA script from @CK

2 ms : script from @robertfern, but for some reason it doesn't trim spaces before key names when working with result of do shell script

Applescript:


set currentWiFiNetworkInfo to do shell script "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I"

set {ATID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, ": "}
set theContent to paragraphs of currentWiFiNetworkInfo

set myRec to {}
repeat with i in theContent
   set tmp to text items of i
   set spaceOffset to item 1 of tmp
   set end of myRec to item 1 of tmp
   set tmp to (rest of tmp) as text
   try
       set tmp to tmp as integer
   end try
   set end of myRec to tmp
end repeat
set AppleScript's text item delimiters to ATID
set rawRecord to {«class usrf»:myRec}

'

2 ms : plain AppleScript version from @CK. I see this solution as the best provided solution.

Here is full script (it gets current WiFi Network state info) using the excellent @CK suggestion :

Applescript:


on currentWiFiNetworkInfo()
   script o
       property parent : a reference to {«class usrf»:my list's paragraphs}
       property key : a reference to AppleScript's text item delimiters
       property item : a reference to my list's text items
       property list : "" -- EDITED
   end script
   set currentWiFiNetworkInfo to ¬
       do shell script "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I" -- ADDED
   tell o
       set its list to currentWiFiNetworkInfo -- ADDED
       set key's contents to {linefeed, ": "}
       set its list to its item as text
       set key's contents to {[space]}
       set its list to its item as text
       return the contents as any
   end tell
end currentWiFiNetworkInfo

my currentWiFiNetworkInfo()

Last edited by KniazidisR (2022-08-05 02:43:29 am)


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#9 2022-08-05 03:40:36 am

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2522

Re: Convert the Text to Record (effectively)

No, after all, the JXA solution from @CK in this case is the best, since it preserves the necessary spaces in the compound key names, unlike the last script in the previous post:

Applescript:


on currentWiFiNetworkInfo()
   set currentWiFiNetworkInfo to do shell script "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I"
   run script "let R=`" & currentWiFiNetworkInfo & "`" & "
.replace(/\\s*(\\w[^:]+): (.+)\\x12?/g,
'\"$1\" : \"$2\" \\u{0A}'
).split( '\\u{0A}' ).slice( 0, -1 ).join( ',' );
JSON.parse( '{' + R + '}' );"
in "JavaScript"
end currentWiFiNetworkInfo

my currentWiFiNetworkInfo()

Last edited by KniazidisR (2022-08-05 03:49:51 am)


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#10 2022-08-05 05:42:45 am

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

Re: Convert the Text to Record (effectively)

KniazidisR wrote:

2 ms : plain AppleScript version from @CK. I see this solution as the best provided solution.


You mean the fastest solution provided.  smile  You can only judge it to be the best if you yourself understand how it works and can judge whether or not the methods used are legitimate. The square brackets in {[space]} certainly aren't part of AppleScript and replacing them with braces produces exactly the same end result. On the face of it, using reserved terms as property labels is also to be discouraged, but in this case they appear to integral to the method.

The result I get from the script is …

Applescript:

{|\t\tagrctlrssi|:"-29", |\t\tagrextrssi|:"0", |\t\tagrctlnoise|:"-87", |\t\tagrextnoise|:"0", |\t\tstate|:"running", |\t\topmode|:"station", |\t\tlasttxrate|:"59", |\t\tmaxrate|:"72", |\t\tlastassocstatus|:"0", |\t\t802.11auth|:"open", |\t\tlinkauth|:"wpa2-psk", |\t\tbssid|:"8:78:8:0:aa:5c", |\t\tssid|:"AndroidAP", |\t\tmcs|:"6", channel:"6"}

… but presumably it's because the beginnings of the text lines are rendered above as tabs rather than spaces. I see you've already noticed that spaces within the labels are removed too.


NG

Offline

 

#11 2022-08-05 06:35:14 am

Marc Anthony
Member
From:: Dallas, TX
Registered: 2006-04-27
Posts: 1090

Re: Convert the Text to Record (effectively)

I've modified my script with some script object hocus pocus. Let me know if it fares differently in your speed test, and perhaps I'll change my default method.

Offline

 

#12 2022-08-05 07:53:11 am

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2522

Re: Convert the Text to Record (effectively)

Marc Anthony wrote:

I've modified my script with some script object hocus pocus. Let me know if it fares differently in your speed test, and perhaps I'll change my default method.


@Marc Anthony,

Thanks for updating your version. As I checked, it is still 7 times slower than the JXA solution. It also uncomfortably removes spaces in compound key names.

I took the liberty of tweaking your script. Now it gives the correct result. Moreover, its execution in Script Geek is equal in speed with the JXA solution (3 ms). (Note: in the Script Debugger following script for some reason is still 3 times slower then the JXA solution, I don't know why this difference with Script Geek testings).

It makes sense to bring it here for users who are weak in JXA, like me:

Applescript:


on currentWiFiNetworkInfo()
   set currentWiFiNetworkInfo to do shell script "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I"
   set recordz to {}
   repeat with theParagraph in my (currentWiFiNetworkInfo's paragraphs)
       set o to offset of ":" in theParagraph
       set theKey to theParagraph's text 1 thru (o - 1)
       repeat while theKey begins with space
           set theKey to text 2 thru -1 of theKey
       end repeat
       set recordz to recordz & (run script "{|" & theKey & "|: " & (quote & (theParagraph's text (o + 2) thru -1) & quote) & "}")
   end repeat
end currentWiFiNetworkInfo

my currentWiFiNetworkInfo()

Last edited by KniazidisR (2022-08-05 08:18:28 am)


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#13 2022-08-05 08:46:16 am

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2522

Re: Convert the Text to Record (effectively)

peavine wrote:

I ran timing tests of the suggestions and my results are similar to those reported by KniazidisR. For some reason, I couldn't get CK's JXA script to run.



remove keyword result inside run script. I think, here @CK simply made typo.


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#14 2022-08-05 08:53:07 am

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

Re: Convert the Text to Record (effectively)

KniazidisR wrote:

remove keyword result inside run script. I think, here @CK simply made typo.



Thanks KniazdisR--that got it working. I'll modify my post above.


2018 Mac mini - macOS Monterey - Script Debugger 8

Offline

 

#15 2022-08-05 08:56:00 am

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

Re: Convert the Text to Record (effectively)

I cleaned up a version from above to save fields that are integers as integers not text.
It also does REALS and BOOLEANS.
Also i only call run script once to cut down on processing time

Applescript:


set theContent to "agrCtlRSSI: -29
agrExtRSSI: true
agrCtlNoise: -87
agrExtNoise: 2.10
state: running
op mode: station
lastTxRate: 59
maxRate: 72
lastAssocStatus: 0
802.11 auth: open
link auth: wpa2-psk
BSSID: 8:78:8:0:aa:5c
SSID: AndroidAP
MCS: 6
channel: 6"


set text item delimiters to {": "}
set theContent to (theContent's text items)
set text item delimiters to {":"}
set theContent to theContent as text
set theContent to paragraphs of the theContent
set {keylist, textList, recordz} to {{}, {}, {}}

repeat with index from 1 to count theContent
   set anItem to theContent's item index
   set keylist's end to anItem's text item 1
   if (count (text items of anItem)) = 2 then
       set textList's end to anItem's text item 2
   else
       set textList's end to (anItem's text items 2 thru -1) as text
   end if
   try -- see if field is an boolean
       set textList's last item to textList's last item as boolean
       set end of recordz to ("|" & my keylist's item index & "|: " & (textList's item index))
   on error
       try -- see if field is a REAL
           if textList's last item contains "." then
               set textList's last item to textList's last item as real
           else
               error
           end if
           set end of recordz to ("|" & my keylist's item index & "|: " & (textList's item index))
       on error -- its text
           try -- see if field is an integer
               set textList's last item to textList's last item as integer
               set end of recordz to ("|" & my keylist's item index & "|: " & (textList's item index))
           on error -- its text
               set end of recordz to ("|" & my keylist's item index & "|: \"" & my textList's item index & "\"")
           end try
       end try
   end try
end repeat
set text item delimiters to {","}
set recordz to run script "{" & (recordz as text) & "}"
recordz

Last edited by robertfern (2022-08-05 09:12:38 am)

Offline

 

#16 2022-08-05 09:08:48 am

estockly
Member
Registered: 2009-01-03
Posts: 124

Re: Convert the Text to Record (effectively)

robertfern wrote:

I cleaned up a version from above to save fields that are integers as integers not text.
Also i only call run script once to cut down on processing time



Very cool! If you want to handle reals and integers:

Applescript:


set textList's last item to textList's last item as number

Should be easy enough to do booleans also.

Last edited by estockly (2022-08-05 09:12:31 am)

Offline

 

#17 2022-08-05 09:13:52 am

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

Re: Convert the Text to Record (effectively)

Done!

I edited my post above.

Offline

 

#18 2022-08-05 09:18:18 am

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

Re: Convert the Text to Record (effectively)

Nigel Garvey wrote:
KniazidisR wrote:

2 ms : plain AppleScript version from @CK. I see this solution as the best provided solution.


You mean the fastest solution provided.  smile  You can only judge it to be the best if you yourself understand how it works and can judge whether or not the methods used are legitimate. The square brackets in {[space]} certainly aren't part of AppleScript



Yes, they are.  I think the length of time for which you've been saying this sort of demonstrates their permanency, and they've most certainly outlived many apparently stable "core" features of AppleScript in their lifetime.  So I think when it comes to defining what the term "legitimate" actually means, I'm taking the pragmatic view in this instance (and I must admit, pragmatism doesn't always come easily to me), which is to acknowledge that these things have utility, they're not going anywhere, and they let one do things that aren't possible otherwise.  I've previously given a run-down on their history in AppleScript, what capabilities they retain, and the pros and cons in various scenarios.

Nigel Garvey wrote:

and replacing them with braces produces exactly the same end result.



Yes, correct.  They aren't serving a functional purpose in this case, purely a typographical one, largely for my benefit to be able to distinguish neighbouring brackets from each other more easily.  Anyone else who's dyslexic might understand how confusing consecutive sets of symbols can be.

I actually normally switch them back out for curly ones when publishing, but my tiredness made me overlook it in this instance.  That said, they pose no drawbacks here nor do any harm, and if anything, it's opened up discussion for people to learn about it.

Oh, and those tab characters shouldn't be there.  I don't know whether that's something you inserted into the script or whether that was me again.  Simply appending a tab item to the tids value in question will resolve their unwelcome appearance.

Nigel Garvey wrote:

On the face of it, using reserved terms as property labels is also to be discouraged, but in this case they appear to integral to the method.



There's no particular reason on the face of it, or on the back of it, or from any angle, to discourage their use.  Once again, it's about appreciating that they have utility, but also—as you rightly said—knowing and understanding how to use them in ways that maintain robustness and offer effectiveness.  Come to think of it, that's true of everything.

I would encourage you to try and start making a point to use them, even though it may be outside of your comfort zone.  But getting familiar with something usually allows reservations that are less warranted to subside.  You may even end up liking it. 

Nigel Garvey wrote:

The result I get from the script is …

Applescript:

{|\t\tagrctlrssi|:"-29", |\t\tagrextrssi|:"0", |\t\tagrctlnoise|:"-87", |\t\tagrextnoise|:"0", |\t\tstate|:"running", |\t\topmode|:"station", |\t\tlasttxrate|:"59", |\t\tmaxrate|:"72", |\t\tlastassocstatus|:"0", |\t\t802.11auth|:"open", |\t\tlinkauth|:"wpa2-psk", |\t\tbssid|:"8:78:8:0:aa:5c", |\t\tssid|:"AndroidAP", |\t\tmcs|:"6", channel:"6"}

… but presumably it's because the beginnings of the text lines are rendered above as tabs rather than spaces. I see you've already noticed that spaces within the labels are removed too.



Ah, is that where they're coming from ?  I don't know whether the forum mutated the white space or whether I did it in a hypnogogic state.

Last edited by CK (2022-08-05 09:32:54 am)

Offline

 

#19 2022-08-05 09:22:37 am

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

Re: Convert the Text to Record (effectively)

KniazidisR wrote:
peavine wrote:

I ran timing tests of the suggestions and my results are similar to those reported by KniazidisR. For some reason, I couldn't get CK's JXA script to run.



remove keyword result inside run script. I think, here @CK simply made typo.



Yes, really sorry.  I was half asleep last night and made a few blunders.  I've amended my post to correct these.

Last edited by CK (2022-08-05 09:23:31 am)

Offline

 

#20 2022-08-05 09:30:54 am

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

Re: Convert the Text to Record (effectively)

robertfern wrote:

Applescript:

try -- see if field is an boolean
       set textList's last item to textList's last item as boolean
       set end of recordz to ("|" & my keylist's item index & "|: " & (textList's item index))
   on error
       try -- see if field is a REAL
           if textList's last item contains "." then
               set textList's last item to textList's last item as real
           else
               error
           end if
           set end of recordz to ("|" & my keylist's item index & "|: " & (textList's item index))
       on error -- its text
           try -- see if field is an integer
               set textList's last item to textList's last item as integer
               set end of recordz to ("|" & my keylist's item index & "|: " & (textList's item index))
           on error -- its text
               set end of recordz to ("|" & my keylist's item index & "|: \"" & my textList's item index & "\"")
           end try
       end try
   end try


try blocks will slow a script down quite significantly when an error is caught.  Error catching is a fairly intense set of operations that get performed in order to catch the error, itemise the components of the script responsible for the error, reporting it, and logging it.  Luckily, you can probably do away with all of that and simply do this:

Applescript:

tell the textList to set the last item to the last item as {boolean, real, integer, text}

Offline

 

#21 2022-08-05 11:06:19 am

KniazidisR
Member
From:: Greece
Registered: 2019-03-03
Posts: 2522

Re: Convert the Text to Record (effectively)

Thanks, thanks, thanks!

I ended up with 3 solutions that return the exact result, but at different speeds. The most effective method (for me) here was the JXA-method, provided by @CK. I also discovered a lot of interesting things about his programming technique.

1) JXA solution from @CK turned out to be the fastest (3 ms). It is ideal for users who understand JXA. See my post #10 for how I applied this solution.

2) The method suggested by @Marc Anthony is also fast (9 ms), although it is 3 times slower than the JXA solution. Since the difference in speed is not fatal, this solution is ideal for users weak in JXA. See my post #13 for how I applied this 2nd solution.

3) The method suggested by @robertfern is the most accurate solution, so it's worth considering. It is ideal for those who would like to receive the values of numerical keys in numerical form. I tried to get the most speed out of it, and made it to remove spaces before key names. I ended up with a script that is 10 times slower than the JXA solution (30 ms):

Applescript:


on currentWiFiNetworkInfo()
   script o
       property currentWiFiNetworkInfo : ""
   end script
   set o's currentWiFiNetworkInfo to paragraphs of (do shell script "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I")
   set {ATID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, ": "}
   set myRec to {}
   repeat with i in o's currentWiFiNetworkInfo
       set tmp to text items of i
       set spaceOffset to item 1 of tmp
       repeat while spaceOffset begins with space -- added by me
           set spaceOffset to text 2 thru -1 of spaceOffset
       end repeat
       set end of myRec to spaceOffset
       set end of myRec to (rest of tmp) as {integer, text} -- the @CK's last suggestion applied here
   end repeat
   set AppleScript's text item delimiters to ATID
   set rawRecord to {«class usrf»:myRec}
end currentWiFiNetworkInfo

my currentWiFiNetworkInfo()

Last edited by KniazidisR (2022-08-05 11:34:21 am)


Model: MacBook Pro
OS X: Catalina 10.15.7
Web Browser: Safari 14.1
Ram: 4 GB

Offline

 

#22 2022-08-05 11:52:25 am

estockly
Member
Registered: 2009-01-03
Posts: 124

Re: Convert the Text to Record (effectively)

CK wrote:


try blocks will slow a script down quite significantly when an error is caught.  Error catching is a fairly intense set of operations that get performed in order to catch the error, itemise the components of the script responsible for the error, reporting it, and logging it.  Luckily, you can probably do away with all of that and simply do this:

Applescript:

tell the textList to set the last item to the last item as {boolean, real, integer, text}



Hmmm... I tried various forms of that, and it changes the class of everything to text. But the below works.

Applescript:


set theContent to "agrCtlRSSI: -29
agrExtRSSI: true
agrCtlNoise: -87
agrExtNoise: 2.10
state: running
       op mode: station
lastTxRate: 59
maxRate: 72
lastAssocStatus: 0
802.11 auth: open
link auth: wpa2-psk
BSSID: 8:78:8:0:aa:5c
SSID: AndroidAP
MCS: 6
channel: 6"

set testRecords to RecordIzeText(theContent)
set currentWiFiNetworkInfo to do shell script "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport -I"
set actualRecords to RecordIzeText(currentWiFiNetworkInfo)

on RecordIzeText(theContent)
   set the content to linefeed & theContent
   set saveTID to AppleScript's text item delimiters
   repeat
       set AppleScript's text item delimiters to {return & tab, linefeed & tab, return & space, linefeed & space}
       set fixedText to text items of theContent
       set AppleScript's text item delimiters to {linefeed}
       set fixedText to fixedText as text
       if fixedText = theContent then exit repeat
       set theContent to fixedText
   end repeat
   set the theContent to the rest of paragraphs of (theContent as text)
   set recordz to {}
   set text item delimiters to {": "}
   repeat with index from 1 to count theContent
       set anItem to theContent's item index
       set anItem to text items of anItem
       set recordLabel to item 1 of anItem
       set recordValue to the rest of anItem as text
       --set recordValue to recordValue as {boolean, real, integer, text} --turns everything to text
       try
           set recordValue to recordValue as {boolean, number}
       on error
           set recordValue to quote & recordValue & quote
       end try
       set end of recordz to ("|" & recordLabel & "|: " & (recordValue))
   end repeat
   set text item delimiters to {","}
   set recordz to run script "{" & (recordz as text) & "}"
   set AppleScript's text item delimiters to saveTID
   return recordz
end RecordIzeText

Also...

(Note: in the Script Debugger following script for some reason is still 3 times slower then the JXA solution, I don't know why this difference with Script Geek testings).



Script Debugger adds a lot of overhead to script execution in order to do all its magic. That makes it less than ideal for timing scripts. I think that's why Shane made Script Geek in the first place.

Offline

 

#23 2022-08-05 01:08:12 pm

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

Re: Convert the Text to Record (effectively)

CK wrote:
Nigel Garvey wrote:
KniazidisR wrote:

2 ms : plain AppleScript version from @CK. I see this solution as the best provided solution.


You mean the fastest solution provided.  smile  You can only judge it to be the best if you yourself understand how it works and can judge whether or not the methods used are legitimate. The square brackets in {[space]} certainly aren't part of AppleScript



Yes, they are.  I think the length of time for which you've been saying this sort of demonstrates their permanency


They've not been mentioned in any edition of the AppleScript Language Guide for at least 25 years. So either the author(s) have been very forgetful or the AppleScript team hasn't intended square brackets to be an official part of the language. The latter explanation's by far the more likely. Square brackets were mentioned once or twice in my very early days on the AppleScript-Users e-mail list, but you and one other poster elsewhere (possibly the same person posting under a different name) are the only people I've ever seen actually use them. The length of time I've had to point out that they're not (or are no longer) an official part of AppleScript equates to the time you've been casually using them here without explanation or good reason. It's the permanency of your stubbornness rather than that of square brackets that's being demonstrated.

AppleScript has quite a few undocumented features and sometimes they can be the only way to get a particular job done or to get it done efficiently (if you know about them). Where this is the case, it should be noted and explained in comments. Where not, more standard methods should be used. This isn't some law I've made up myself. It's a philosophy passed on by the many professional coders with whom I've crossed swords over the past quarter century. Likewise with not using reserved terms (with a few exceptions) as labels for script and user record properties. But as I noted above, your convoluted script seems to depend on the labels not being user ones.

Not being casual or a smartarse is especially relevant in fora where people come for help in learning the language. There are subtle differences between lists with square brackets ("linked lists") and those with braces ("vectors"). If a learner sees someone who comes on clever casually using square brackets instead of braces simply as a matter of style, they might be tempted to think the two are equivalent, follow suit, and one day run foul of the differences. That said, of course, there's no reason why an undocumented feature shouldn't be demo'd at the end of a relevant, already answered topic for the interest and amusement of other scripters.

I would encourage you to try and start making a point to use them, even though it may be outside of your comfort zone.


I in turn would encourage you to read MacScripter's forum rules, which were also not made up by me. Rule 2 states: "You will be required to enter your legal first and last name when you register." The first and last name shown in your profile don't conform to this requirement and I'd encourage you to correct them before Mark notices.  smile

If anyone's interested, in April 2002, on the AppleScript-Users list, Shane quoted from a 1993 AppleScript document which appears to show the beginning of linked lists' fall from favour. By 1997, there was no mention of them at all in the AppleScript documentation.

From notes accompanying AS version 1.1 in December 1993:

The Story of Lists

Representation and Efficiency

The way in which lists are represented in memory has changed from
AppleScript 1.0 to AppleScript 1.1, and correspondingly, the characteristics
of these values has changed with respect to efficiency. Efficiency involves
both the amount of time it takes to perform certain operations, and the
amount of space these values consume. Additionally, certain operations can
generate intermediate values (garbage) that are automatically reclaimed by
AppleScript. The more of these intermediate values, the more often the
time-consuming operation of collecting them must occur.

In AppleScript 1.0, lists were represented as linked lists in memory. Linked
lists means that each item was linked to the next via an internal pointer.
On the positive side, linked lists didn't take up much memory, and
concatenating new item (at least on the left-hand side) was relatively fast
and didn't create any garbage:

On the negative side, linked lists took longer to reach elements at higher
indicies than they did to reach elements at lower indicies (i.e. the access
time was linear (increased proportionally) to the index of the item being
accessed). This is because the list had to be traversed from beginning,
counting up to the index of the item desired. Also, if you were unfortunate
enough to concatenate on the right-hand side, the entire left-hand side of
the list would have to be copied. This is because we mustn't change the
left-hand side value to perminantly be concatenated with the right-hand side
because other parts of the program may still be using its old value.

In AppleScript 1.1, lists are represented as vectors. This means that
elements are stored in consecutive locations in memory rather than linked
together. This makes accessing elements by index fast (constant time -- it
doesn't matter about the value of the index), but makes concatenation
generate much more garbage, as both halves of the concatenate operator (&)
must be copied.

This trade-off was made because we found that people accessed items of lists
far more often than they concatenated, or more precisely, that this balance
lead to better overall performance characteristics. However, for certain
scripts, the previous list representation may have been more appropriate,
therefore we did two things: We allow linked lists to be explicitly created,
and we allow insertion operations on vectors.

Explicit Linked Lists

AppleScript 1.1 allows the old-style (linked) lists to be created by using a
square bracket notation. Whereas to put variables x, y and z into a vector,
we say:

{x, y, z}

we can now also write scripts that put these variables into a linked list
via:

[x, y, z]

Note, however, that both of these print results out with braces.
Furthermore, asking the class of either of these two representations will
return the class 'list':

class of {1, 2} --> list
class of [1, 2] --> list

If the two representations need to be distinguished, the 'best type'
property may be used:

best type of {1, 2} --> vector
best type of [1, 2] --> linked list

For the concatenation operator (&), the representation of the result will be
the same as the representation of the left-hand side:

best type of ({1} & [2]) --> vector
best type of ([1] & {2}) --> linked list

When AppleScript 1.1 reads 1.0 script files, all lists stored in persistent
properties will actually be linked lists. However, the distinction will be
transparent to the script when run.

Linked lists are a very good representation for recursive programs that
"walk" lists, processing one item on each recursive step. This is usually
the case when the 'rest' property is being used. For example, the following
function computes the sum of the squares of the items in a list:

on SumOfSquares(theList)
    if theList = {} then
        return 0
    else
        set element to first item of theList
        return (element * element) + SumOfSquares(rest of theList)
    end if
end SumOfSquares

When this function is applied to a linked list or vector, the same answer
results (note that the empty list equals the empty vector, so the equality
test works in either case):

SumOfSquares([1, 2, 3]) --> 14
SumOfSquares({1, 2, 3}) --> 14

however the linked list case doesn't generate any garbage. The vector case
must generate a new sub-list for each time the 'rest' property is accessed.

Vector Insertion

The second thing we did to enhance the performance when using vectors is to
allow items to be inserted at the beginning or end of a vector. E.g.:

set myList to {1, 2}
set end of myList to 3
myList --> {1, 2, 3}

set beginning of myList to 0
myList --> {0, 1, 2, 3}

Unlike concatenation, setting the beginning or end of a list actually
changes the list. Note that it's not the variable that's being changed, it's
the list itself. That means that if two variables are referring to the same
list, and an item is inserted into the list, both variables will refer to
the changed list:

set x to {1, 2}
set y to x -- y refers to the same value that x refers to
set end of x to 3
x --> {1, 2, 3}

y --> {1, 2, 3}

However, despite the "destructive" nature of insertion, often times we're
only using lists to accumulate results, and don't care that variables
referring to the list will be modified (presumably we don't care because
only one variable is refering to that list anyway). In these cases, we can
greatly increase the efficiency of our scripts by inserting items rather
than concatenating them, forming new lists and leaving the old ones as
garbage. For example, this script that accumulates the squares of a list of
numbers:

set myNumbers to {1, 2, 3}
set resultList to {}
repeat with i in myNumbers
    set resultList to resultList & {i * i}
end repeat
resultList --> {1, 4, 9}

can be made more efficient (faster and generate less garbage) if rewritten
as:

set myNumbers to {1, 2, 3}
set resultList to {}
repeat with i in myNumbers
    set end of resultList to i * i
end repeat
resultList --> {1, 4, 9}

For the record, setting the end of a list is by far the most efficient, and
setting the beginning is second (because all the items must be slid down to
make room for a new first item).

Minor difference in Concatenation

There is one minor difference between the way concatenation works on linked
lists and vectors (and consequently between AppleScript 1.0 and AppleScript
1.1 using the curly brace notation for lists). When linked lists are
concatenated, only the left-hand argument is copied, the right-hand argument
is shared. For example:

set x to [1, 2, 3]
set y to [4, 5, 6]
set z to x & y
set item 2 of y to 999
z --> {1, 2, 3, 4, 999, 6}

Here, x and y are two linked lists (or AppleScript 1.0 lists) that are
concatenated. Since y appears on the right-hand side of the concatenate
operator (&), it is shared between y and the last three elements of z. Then
when y is updated, these changes are reflected in z also.

However, if vectors are used, both x and y are copied to create z, and
updating y does not change z:

set x to {1, 2, 3}
set y to {4, 5, 6}
set z to x & y
set item 2 of y to 999
z --> {1, 2, 3, 4, 5, 6}

Although this does constitute a change in the default behavior from
AppleScript 1.0 to 1.1, we believe the new behavior is more intuitive, and
the old behavior can be preserved using the square bracket syntax.


NG

Offline

 

#24 2022-08-06 01:18:57 am

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

Re: Convert the Text to Record (effectively)

Here's an analysis of CK's AppleScript script in post #5. The way the TIDs are used is interesting.

Applescript:

set WifiStats to "        agrCtlRSSI: -29
       agrExtRSSI: 0
       agrCtlNoise: -87
       agrExtNoise: 0
       state: running
       op mode: station
       lastTxRate: 59
       maxRate: 72
       lastAssocStatus: 0
       802.11 auth: open
       link auth: wpa2-psk
       BSSID: 8:78:8:0:aa:5c
       SSID: AndroidAP
       MCS: 6
channel: 6"


-- Replace both linefeeds and colon-spaces with linefeeds.
set AppleScript's text item delimiters to {linefeed, ": "}
set WifiStats to WifiStats's text items as text
-- Replace both spaces and tabs with empty texts resulting from a non-text delimiter.
set AppleScript's text item delimiters to {{space}, tab} -- simile: {7, space, tab}
set WifiStats to WifiStats's text items as text
-- Hack the result's alternating label and value lines into a record.
return {«class usrf»:WifiStats's paragraphs}'s contents as anything

-- Another once-popular version of the hack:
return (record {«class usrf»:WifiStats's paragraphs} as record)'s «class seld»

-- Or even:
script
   {«class usrf»:WifiStats's paragraphs}
end script
return (run script result)

Last edited by Nigel Garvey (2022-08-06 06:22:38 am)


NG

Offline

 

#25 2022-08-06 10:26:31 am

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

Re: Convert the Text to Record (effectively)

The purpose of KniazidisR's script appears to be to get information from the airport utility, and, if this is correct, an ASObjC solution is probably of little worth. However, FWIW, the following ASObjC script does the job and takes 4 milliseconds (with the Foundation framework in memory), which is competitive with the other suggestions. I had earlier posted a different version of this script, but the following is faster because it uses ": " as a separator to parse each paragraph of the source string. Error correction is needed if blank or nonconforming paragraphs are possible.

Applescript:

use framework "Foundation"

set theString to "agrCtlRSSI: -29
agrExtRSSI: 0
agrCtlNoise: -87
agrExtNoise: 0
state: running
op mode: station
lastTxRate: 59
maxRate: 72
lastAssocStatus: 0
802.11 auth: open
link auth: wpa2-psk
BSSID: 8:78:8:0:aa:5c
SSID: AndroidAP
MCS: 6
channel: 6"


on getRecord(theString)
   set theString to current application's NSString's stringWithString:theString
   set theArray to (theString's componentsSeparatedByString:linefeed)
   set theDictionary to (current application's NSMutableDictionary's new())
   set whiteSpaceCharacters to current application's NSCharacterSet's whitespaceCharacterSet()
   repeat with aParagraph in theArray
       set paragraphArray to (aParagraph's componentsSeparatedByString:": ")
       set theKey to ((paragraphArray's objectAtIndex:0)'s stringByTrimmingCharactersInSet:whiteSpaceCharacters)
       set theValue to ((paragraphArray's objectAtIndex:1)'s stringByTrimmingCharactersInSet:whiteSpaceCharacters)
       (theDictionary's setObject:theValue forKey:theKey)
   end repeat
   return theDictionary as record
end getRecord

set theRecord to getRecord(theString)

Last edited by peavine (2022-08-06 05:33:28 pm)


2018 Mac mini - macOS Monterey - Script Debugger 8

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)