Building a List of Lists

I would never have thought that I could not combine 2 lists into 1 list of lists.


set listA to {12, "cool", 1.1}
set listB to {13, "bool", pi, "A"}
set listC to listA & listB
-- Result: {12, "cool", 1.1, 13, "bool", 3.14159265359, "A"}

How to create one list, which contains N lists? How to append next list in repeat loop? This is the main problem.
I need as result listC this form: {{12, “cool”, 1.1},{13, “bool”, pi, “A”}}

I found:

set listA to {12, "cool", 1.1}
set listB to {13, "bool", pi, "A"}
set listC to {listA} & {listB}

I needed this for rounding list of numbers with Shane Stanley’s great handler.
(See below the repeat loop using):


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

set numberList to {11.2345, pi}

set allResults to {}
repeat with aNumber in numberList
	set roundingResult to my roundNumber({theNumber:(aNumber as list), roundingMethod:"school", nDecimalPlaces:1})
	set end of allResults to roundingResult
end repeat
return allResults

on roundNumber(inputArgument)
	tell (inputArgument & {roundingMethod:"school", nDecimalPlaces:0}) to set {theNumber, roundingMethod, nDecimalPlaces} to {its theNumber, its roundingMethod, its nDecimalPlaces}
	if roundingMethod = "school" then
		set roundingMethod to (current application's NSRoundPlain)
	else if roundingMethod = "up" then
		set roundingMethod to (current application's NSRoundUp)
	else if roundingMethod = "down" then
		set roundingMethod to (current application's NSRoundDown)
	else if roundingMethod = "nearest" then
		set roundingMethod to (current application's NSRoundBankers)
	end if
	-- make decimal number
	if theNumber's class = text then
		set theNSDecimalNumber to current application's NSDecimalNumber's decimalNumberWithString:theNumber
	else
		set theNSDecimalNumber to current application's NSDecimalNumber's numberWithDouble:theNumber
	end if
	-- round it
	set theHandler to current application's NSDecimalNumberHandler's decimalNumberHandlerWithRoundingMode:roundingMethod scale:nDecimalPlaces raiseOnExactness:false raiseOnOverflow:true raiseOnUnderflow:true raiseOnDivideByZero:true
	set theRoundedNumber to theNSDecimalNumber's decimalNumberByRoundingAccordingToBehavior:theHandler
	-- get AS number
	try
		set roundednumber to theRoundedNumber's doubleValue()
	on error
		set roundednumber to "too large"
	end try
	-- get formatted strings
	set theFormatter to current application's NSNumberFormatter's alloc()'s init()
	theFormatter's setNumberStyle:(current application's NSNumberFormatterDecimalStyle)
	theFormatter's setMinimumFractionDigits:nDecimalPlaces
	theFormatter's setMaximumFractionDigits:nDecimalPlaces
	theFormatter's setHasThousandSeparators:false
	set decimalForm to (theFormatter's stringFromNumber:theRoundedNumber) as text
	theFormatter's setNumberStyle:(current application's NSNumberFormatterScientificStyle)
	theFormatter's setFormat:"0.#E+0;0.#E-0"
	set exponentialForm to (theFormatter's stringFromNumber:roundednumber) as text
	return {roundednumber:roundednumber, decimalForm:decimalForm, exponentialForm:exponentialForm}
end roundNumber

Shane, thanks!

Why not this simple:

set listA to {12, "cool", 1.1}
set listB to {13, "bool", pi, "A"}
set listC to {}
set end of listC to listA
set end of listC to listB
listC

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 21 septembre 2019 10:58:13

Here is why. Try this in code above to see, that your code gives wrong results in repeat loop:


set allResults to {}
repeat with aNumber in numberList
    set roundingResult to my roundNumber({theNumber:(aNumber as list), roundingMethod:"school", nDecimalPlaces:1})
    set end of allResults to roundingResult
end repeat

Generally, when building a list from individual items, its faster to set the end of the list to each item as you get it. This works even when the item itself happens to be a list or a record. With this technique, the list being built is (nominally) always the same list, just with more items added.

Concatenating items to a list creates a new, longer list, as does concatenating two lists together. This is why the two lists need to be in lists themselves if you want the result to be a list containing those two lists. (Or just present the second list in a list if you want the result to be the same as the first list with the second in it.) While generally less efficient for repeated actions, there may be times when this technique is to be preferred.

The items in KniazidisR’s larger script are actually records, but the same techniques are required because of a related issue. When items are concatenated together, the item on the right of the ampersand is automatically (if both necessary and possible) coerced to the same class as the item on the left for the concatenation. If this isn’t possible, the concatenation result is a list containing both items. When concatenating an item to a list, its coercion to list, in most cases, will be a list containing just that item. But coercing a record to list produces a list containing just the record’s values. So if you want to preserve the record as an entity in itself, it has to be explicitly presented in a list.

OK, I put them in list with your method, but this again results not to list of lists:


set allResults to {}
repeat with aNumber in numberList
	set roundingResult to my roundNumber({theNumber:(aNumber as list), roundingMethod:"school", nDecimalPlaces:1})
	set end of allResults to {roundingResult}
end repeat

It seems, the reason is other. The AppleScript need first operand type to determine to what result you want coerce. The & method is preferable here.

No. The result from this is the result of the last action performed in the repeat. You do have to add return allResults at the end to see what you’ve got. And as I said above, your ‘roundingResult’ values are records, not lists.

Thank you, I somehow forgot about it. Then I will correct my script above, since the set end of… to… method is more efficient. :slight_smile:

No need { and } too, as they are list of records already. I added return allResults to on run handler so that I no longer confuse people for no reason.

I apologize but when I run your script, I get:

[format]{{roundednumber:11.2, decimalForm:“11,2”, exponentialForm:“1,1E+1”}, {roundednumber:3.1, decimalForm:“3,1”, exponentialForm:“3,1E+0”}}[/format]

which is what it was supposed to build, a list whose first item is the record
[format]{roundednumber:11.2, decimalForm:“11,2”, exponentialForm:“1,1E+1”}[/format]

and second item is the record :
[format]{roundednumber:3.1, decimalForm:“3,1”, exponentialForm:“3,1E+0”}[/format]

My original proposal may be rewritten with a loop:

set listA to {12, "cool", 1.1}
set listB to {13, "bool", pi, "A"}

set twoLists to {listA, listB}
set listC to {}
(*
repeat with aList in twoLists
	set end of listC to (contents of aList)
end repeat
listC
*)
repeat with i from 1 to count twoLists
	set end of listC to (get item i of twoLists)
end repeat
listC

The result would be the same:
[format]{{12, “cool”, 1.1}, {13, “bool”, 3.14159265359, “A”}}[/format]

Yvan KOENIG running High Sierra 10.13.6 in French (VALLAURIS, France) samedi 21 septembre 2019 13:54:07

Hi Yvan.

KniazidisR’s script wasn’t returning his ‘allResults’ list. This was sorted out in posts #7 & #8 above.