Sunday, November 19, 2017

#1 2017-03-16 04:49:02 pm

t.spoon
Member
From:: BFE, Massachusetts
Registered: 2013-01-13
Posts: 206

Elegant flattening of list-of-lists

This is 100% unimportant, just for the fun of it.

I've got an application that always returns a 2-layer (never deeper) list of lists to Applescript, where I just want all the items in the second level lists as a single list.

so given:

{{1, 2, 3}, {4, 5, 6}}

I just want:

{1, 2, 3, 4, 5, 6}

Obviously, I can do it with repeat loops.

Applescript:

set delisted to {}
repeat with i in lol
   repeat with j in i
       copy the contents of j to the end of delisted
   end repeat
end repeat

I was just surprised I couldn't quickly hack together some syntax to get it in one line of Applescript.

I thought something like:

Applescript:

set lol to {{1, 2, 3}, {4, 5, 6}}

set delisted to the contents of every item of lol

or

Applescript:

set lol to {{1, 2, 3}, {4, 5, 6}}

set delisted to the items of the items of lol

would work, but no luck. Anybody want to show off their AS knowledge?

Oh, and yes, I'm sure I could do it in one line with a "do shell script," that doesn't count.


Hackintosh built February, 2012 |  Mac OS Sierra
GIGABYTE GA-Z68X-UD3H-B3 | Core i5 2500k | 16 GB DDR3 | GIGABYTE Geforce 1050 TI 4GB
250 GB Samsung 850 EVO | 4 TB RAID
Dell Ultrasharp U3011 | Dell Ultrasharp 2007FPb

Offline

 

#2 2017-03-16 05:27:27 pm

Adam Bell
Administrator
From:: Nova Scotia, Canada
Registered: 2005-10-04
Posts: 4660

Re: Elegant flattening of list-of-lists

How about:

Applescript:

set lol to characters of ({{1, 2, 3}, {4, 5, 6}} as text)


iMac running OS X 10.13.1

Offline

 

#3 2017-03-16 05:37:41 pm

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

Re: Elegant flattening of list-of-lists

t.spoon wrote:

I was just surprised I couldn't quickly hack together some syntax to get it in one line of Applescript.


What matters ultimately is that it works, not how many lines it takes.  smile

Here's an effort for general use: http://macscripter.net/viewtopic.php?pid=140475#p140475


NG

Offline

 

#4 2017-03-16 06:48:48 pm

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

Re: Elegant flattening of list-of-lists

Only need one repeat loop

Applescript:

set clist to {{1, 2, 3}, {4, 5, 6}}
set delisted to {}
repeat with i in clist
   set delisted to delisted & i
end repeat

Last edited by robertfern (2017-03-16 06:49:34 pm)

Offline

 

#5 2017-03-16 10:00:53 pm

t.spoon
Member
From:: BFE, Massachusetts
Registered: 2013-01-13
Posts: 206

Re: Elegant flattening of list-of-lists

Adam,

I made that simplified list to post, but the program actually returns references, so coercing them to another class destroys their usefulness. Also, the text of the coerced references each contain multiple words and spaces, so {"characters of", "words of", "paragraphs of" } are no-gos for separating them.

Nigel,

I know, of course. I was just surprised I couldn't quickly do it, and was curious if it could be done.

Robert,

Thanks. That approach is faster by... a factor of [divide by 0].

Just for the heck of it, I checked:

Applescript:

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

set numberOfSublists to 50

set masterList to {}

repeat numberOfSublists times
   set subsetCount to random number from 5 to 400
   set seedNumber to random number from 1 to 1000
   set stepSize to random number from 1 to 10
   set sublist to {}
   repeat subsetCount times
       set seedNumber to seedNumber + stepSize
       copy seedNumber to end of sublist
   end repeat
   copy sublist to end of masterList
end repeat

set t1 to (time of (current date))

set delistedRobertFern to {}
repeat with i in masterList
   set delistedRobertFern to delistedRobertFern & i
end repeat

set t2 to (time of (current date))

set delistedTspoon to {}
repeat with i in masterList
   repeat with j in i
       copy the contents of j to the end of delistedTspoon
   end repeat
end repeat

set t3 to (time of (current date))

set totalItemCount to count of delistedTspoon

if t2 - t1 < t3 - t2 then
   set endDialogText to "The single loop approach was faster by" & t3 - t2 / t2 - t1 * 100 & "%"
else
   set endDialogText to "The double loop approach was faster by" & t2 - t1 / t3 - t2 * 100 & "%"
end if

display dialog "The Main List contained " & numberOfSublists & " and " & totalItemCount & " total items." & return & "The single loop approach took " & t2 - t1 & " seconds to run." & return & "The double loop approach took " & t3 - t2 & " seconds to run." & return & endDialogText

And it actually turns out that you don't want to set any value for "number of sublists" that returns a time > 0 seconds for your loop, because if you do, you won't want to wait around to get a result out of my loop.


Hackintosh built February, 2012 |  Mac OS Sierra
GIGABYTE GA-Z68X-UD3H-B3 | Core i5 2500k | 16 GB DDR3 | GIGABYTE Geforce 1050 TI 4GB
250 GB Samsung 850 EVO | 4 TB RAID
Dell Ultrasharp U3011 | Dell Ultrasharp 2007FPb

Offline

 

#6 2017-03-17 04:05:35 am

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

Re: Elegant flattening of list-of-lists

CAUTION

The variable named sublist conflicts with Satimage's function named sublist too.


Yvan KOENIG running Sierra 10.12.3 in French (VALLAURIS, France) vendredi 17 mars 2017 10:05:29

Offline

 

#7 2017-03-17 05:47:15 am

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

Re: Elegant flattening of list-of-lists

t.spoon wrote:

And it actually turns out that you don't want to set any value for "number of sublists" that returns a time > 0 seconds for your loop, because if you do, you won't want to wait around to get a result out of my loop.


You can speed up the double loop considerably by using references to the list variables and, to a lesser extent, by using 'set' instead of 'copy'. The single loop still usually seems to be faster, though. The subtractions in the maths for the dialog text need to be done before the division and the multiplication, ie. (t3 - t2) / (t2 - t1) * 100, not t3 - t2 / t2 - t1 * 100. But I've changed the calculation and the wording below because in my understanding of English, the calculation for a "faster by" percentage would be (t3 - t2) / (t2 - t1) * 100 - 100. I've also allowed for the possibility of the two results being the same or the lower one 0.

Applescript:

--use AppleScript version "2.4" -- The code actually works with any version of AppleScript likely to be still in use.
--use scripting additions

set numberOfSublists to 200

local o, delistedRobertFern

script
   property |sublist| : missing value
   property masterList : missing value
   
   property delistedTspoon : missing value
end script
set o to result

set o's masterList to {}
repeat numberOfSublists times
   set subsetCount to random number from 5 to 400
   set seedNumber to random number from 1 to 1000
   set stepSize to random number from 1 to 10
   set o's |sublist| to {}
   repeat subsetCount times
       set seedNumber to seedNumber + stepSize
       set end of o's |sublist| to seedNumber
   end repeat
   set end of o's masterList to o's |sublist|
end repeat

set t1 to (time of (current date))

set delistedRobertFern to {}
repeat with i from 1 to (count o's masterList)
   set delistedRobertFern to delistedRobertFern & (item i of o's masterList)
end repeat

set t2 to (time of (current date))

set o's delistedTspoon to {}
repeat with i from 1 to (count o's masterList)
   set o's |sublist| to item i of o's masterList
   repeat with j from 1 to (count o's |sublist|)
       set end of o's delistedTspoon to item j of o's |sublist|
   end repeat
end repeat

set t3 to (time of (current date))

set totalItemCount to count o's delistedTspoon

set singleLoopTime to t2 - t1
set doubleLoopTime to t3 - t2
if (singleLoopTime < doubleLoopTime) then
   if (singleLoopTime > 0) then
       set endDialogText to "The single loop approach was " & doubleLoopTime / singleLoopTime & " times as fast."
   else
       set endDialogText to "The single loop approach was faster!"
   end if
else if (doubleLoopTime < singleLoopTime) then
   if (doubleLoopTime > 0) then
       set endDialogText to "The double loop approach was " & singleLoopTime / doubleLoopTime & " times as fast."
   else
       set endDialogText to "The single loop approach was faster!"
   end if
else
   set endDialogText to "The time difference between to the two approaches was too close to call."
end if

display dialog "The Main List contained " & numberOfSublists & " and " & totalItemCount & " total items." & return & "The single loop approach took " & t2 - t1 & " seconds to run." & return & "The double loop approach took " & t3 - t2 & " seconds to run." & return & endDialogText


NG

Offline

 

#8 2017-03-17 09:14:43 am

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

Re: Elegant flattening of list-of-lists

We were of course forgetting about ASObjC:

Applescript:

use AppleScript version "2.4"
use framework "Foundation"

set lol to {{1, 2, 3}, {4, 5, 6}}
set delisted to ((current application's class "NSArray"'s arrayWithArray:(lol))'s valueForKeyPath:("@unionOfArrays.self")) as list
--> {1, 2, 3, 4, 5, 6}


NG

Offline

 

#9 2017-03-17 02:03:46 pm

t.spoon
Member
From:: BFE, Massachusetts
Registered: 2013-01-13
Posts: 206

Re: Elegant flattening of list-of-lists

I wasn't _forgetting_ about ASObjC, I just don't know it... I was sort of expecting someone to pop in with an ASObjC solution to doing it in 1-line. I was interested to see it, so I didn't include ASObjC with my line about not using "do shell script."

Interested though I am to see it, like using terminal, it's doesn't address my surprise that I couldn't find a short, elegant Applescript way to do that.

Thanks for the corrections on the speed test.

I was also surprised that the Applescript divide by 0 didn't get caught by the compiler or cause a crash, that it instead returned a very high value.

After than I messed around a bit with divide by zero, and everything I tried did return a "Can't divide by 0" Applescript execution error. So I'm not sure exactly what it takes to trick it into trying it.

In Script Debugger, it does appear that the time calculations that return results under 1 second really do have the variable assigned to exactly the number "0," that there is no sub-second resolution going on that gets rounded for the dialog, or anything like that. That Applescript really is trying to do a divide by 0 there, and returns a numeric value.


Hackintosh built February, 2012 |  Mac OS Sierra
GIGABYTE GA-Z68X-UD3H-B3 | Core i5 2500k | 16 GB DDR3 | GIGABYTE Geforce 1050 TI 4GB
250 GB Samsung 850 EVO | 4 TB RAID
Dell Ultrasharp U3011 | Dell Ultrasharp 2007FPb

Offline

 

#10 2017-03-17 02:15:47 pm

t.spoon
Member
From:: BFE, Massachusetts
Registered: 2013-01-13
Posts: 206

Re: Elegant flattening of list-of-lists

Incidentally, I just thought I'd mention that I've used plenty of other forums, and MacScripter has the greatest collection of knowledgable, helpful, friendly people I've ever come across online.


Hackintosh built February, 2012 |  Mac OS Sierra
GIGABYTE GA-Z68X-UD3H-B3 | Core i5 2500k | 16 GB DDR3 | GIGABYTE Geforce 1050 TI 4GB
250 GB Samsung 850 EVO | 4 TB RAID
Dell Ultrasharp U3011 | Dell Ultrasharp 2007FPb

Offline

 

#11 2017-03-17 07:50:32 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5175

Re: Elegant flattening of list-of-lists

Nigel Garvey wrote:

We were of course forgetting about ASObjC:


Yes -- although this suggested to me it might not be so useful:

I made that simplified list to post, but the program actually returns references, so coercing them to another class destroys their usefulness.


But maybe I misunderstood Tom.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Offline

 

#12 2017-03-18 05:11:15 am

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

Re: Elegant flattening of list-of-lists

Ah. Yes. So we didn't so much forget about ASObjC as instinctively didn't consider it.  roll


NG

Offline

 

#13 2017-03-19 04:44:59 pm

t.spoon
Member
From:: BFE, Massachusetts
Registered: 2013-01-13
Posts: 206

Re: Elegant flattening of list-of-lists

I didn't know enough about ASObjC to know if it could maintain references when passing them back and forth between AS & ObjC, so I didn't know if an ASObjC solution was possible or not.

And since the entire exercise is just for the fun of it (the actual run time on my original horrendously inefficient double repeat loop was still a hundredth of a second when processing the actual data in question), I was still interested to see it.


Hackintosh built February, 2012 |  Mac OS Sierra
GIGABYTE GA-Z68X-UD3H-B3 | Core i5 2500k | 16 GB DDR3 | GIGABYTE Geforce 1050 TI 4GB
250 GB Samsung 850 EVO | 4 TB RAID
Dell Ultrasharp U3011 | Dell Ultrasharp 2007FPb

Offline

 

#14 2017-03-19 06:41:45 pm

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5175

Re: Elegant flattening of list-of-lists

t.spoon wrote:

I didn't know enough about ASObjC to know if it could maintain references when passing them back and forth between AS & ObjC


You're fine with text, integers and booleans. Reals are fine in 10.11 and later; before that, they can lose precision. Dates are also fine in 10.11 and later.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)