Extended list

This script com­bines some use­ful meth­ods of AS3 Array with those of Lin­go List. More­over, some of the orig­i­nal Lin­go list meth­ods like add(), append(), deleteAt() and getAt() are mod­i­fied. For exam­ple, getAt() method returns VOID instead of “index out of range” exception.

new( {…args} ) - Returns a new list, you can spec­i­fy the ini­tial val­ues as arguments.
dis­pose() - Destroys the list
shuf­fle() - Shuf­fles the list val­ues in-place, using Fisher–Yates algorithm.
add( {…args} )* - Adds val­ues to a list. For a sort­ed list, the val­ues are placed in their prop­er order.
getAt()* - Pre­vents “Index out of range” exception.
deleteAt()* - deleteAt()*
append( {…args} )* - Adds the spec­i­fied val­ues to the end of a list.
shift() - Removes the first ele­ment from an array and returns that element.
pop() - Removes the last ele­ment from a list and returns the val­ue of that element.
unshift( …args ) - Adds one or more ele­ments to the begin­ning of a list and returns the new count of the list.
slice( startIn­dex, endIndex ) - Returns a new list that con­sists of a range of ele­ments from the orig­i­nal list, with­out mod­i­fy­ing the orig­i­nal list.
splice( startIn­dex, delete­Count{, … values} ) - Adds ele­ments to and removes ele­ments from a list.
some( han­dler{, object} ) - Exe­cutes a test func­tion on each item in the list until an item is reached that returns TRUE.
every( han­dler{, object} ) - Exe­cutes a test han­dler on each item in the list until an item is reached that returns FALSE for the spec­i­fied handler.
fil­ter( han­dler{, object} ) - Exe­cutes a test han­dler on each item in the list and con­structs a new list for all items that return TRUE for the spec­i­fied handler.
forE­ach( han­dler{, object} ) - Exe­cutes a han­dler on each item in the list.
map( han­dler{, object} )  - Exe­cutes a han­dler on each item in a list, and con­structs a new list of items cor­re­spond­ing to the results of the han­dler on each item in the orig­i­nal list.
sort( {sym­bol­Order} ) - Sorts the list in ascend­ing or descend­ing order.( #ASC / #DESC )
sor­tOn( int­ColumnIn­dex{, symbolOrder} ) - Sorts the list on giv­en “col­umn”.
con­cat() - Con­cate­nates the ele­ments spec­i­fied in the para­me­ters with the ele­ments in a list and cre­ates a new list.
reverse() - Revers­es the list in-place.
join( sep­a­ra­tor ) - Returns a string, con­struct­ed by items in the list, with the sep­a­ra­tor insert­ed between them.
toString() - Returns a string that rep­re­sents the ele­ments in the spec­i­fied list, sep­a­rat­ed by commas.
lastIn­dex­Of( searchEle­ment, startIndex ) - Search­es for an item in a list, work­ing back­ward from the last item, and returns the index posi­tion of the match­ing item.
type­Of() - Returns the name of the script as a symbol.
Show code

– “Xlist” par­ent script
— Author: Vladin M. Mitov
— May, 2011
— Last Update — Feb­ru­ary, 2012
— http://www.ed-multimedia.com

EXAMPLES:
——————————
— myList = script( “xList” ).new( 1, 2, 3 )
— put myList.interface()

– myList.reverse()
— put myList.toString() — “3,2,1”
— myList.add( 4, 5, 6 )
— put myList.toString() — “3,2,1,4,5,6”
— myList.sort()
— put myList.toString() — “1,2,3,4,5,6”

– myList = script( “xList” ).new( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 )
— put myList.filter( #getEven ).toString() — “2,4,6,8,10”

– A han­dler, defined in a movie script
–on getEven( aelement )
— return( aele­ment mod 2 = 0 )
–end getEven

————————————————-
PROPERTIES
————————————————-
on ___________________________PROPERTIES
end
prop­er­ty ancestor

————————————————-
CONSTRUCTOR / DESTRUCTOR
————————————————-
on ___________________________CONSTRUCTOR_DESTRUCTOR
end
on new( me )
ances­tor = list()
p = the paramCount
repeat with i = 2 to p
callAnces­tor( #add, me, param( i ) )
end repeat
return me
end new

on dis­pose( me )
ancestor.deleteAll()
ances­tor = VOID
end dispose

————————————————-
PUBLIC METHODS
————————————————-
on ___________________________PUBLIC_METHODS
end

on getAt( me, n )
if( n < 1 or n > count( ances­tor ) ) then return VOID
return ances­tor[ n ]
end getAt

on deleteAt( me, n )
if( n < 1 or n > count( ances­tor ) ) then return VOID
callAnces­tor( #deleteAt, me, n )
return TRUE
end deleteAt

on lastIn­dex­Of( me, searchEle­ment, startIndex )
if( voidP( startIn­dex ) ) then startIn­dex = count( ancestor )
repeat with i = startIn­dex down to 1
if( ances­tor[ i ] = searchEle­ment ) then
return i
end if
end repeat
return VOID
end lastIndexOf

on add( me )
p = the paramCount
repeat with i = 2 to p
callAnces­tor( #add, me, param( i ) )
end repeat
end add

on addAt( me, pos, val )
if( voidP( pos ) ) then return FALSE
if( pos < 1 ) then return FALSE
if( voidP( val ) ) then return FALSE
callAnces­tor( #addAt, me, pos, val )
end addAt

on append( me )
p = the paramCount
repeat with i = 2 to p
callAnces­tor( #append, me, param( i ) )
end repeat
end append

on pop( me )
n = callAnces­tor( #get­Last, me )
callAnces­tor( #deleteAt, me, count( ancestor ) )
return n
end pop

on shift( me )
e = callAnces­tor( #getAt, me, 1 )
callAnces­tor( #deleteAt, me, 1 )
return e
end shift

on unshift( me )
p = the paramCount
repeat with i = p down to 2
callAnces­tor( #addAt, me, 1, param( i ) )
end repeat
return count( ancestor )
end unshift

on slice( me, startIn­dex, endIndex )
ret­val = me.script.new()
p = the paramCount
if( voidP( startIn­dex ) ) then
s = 1
e = e = count( ancestor )
end if
if( p = 2 ) then
if( startIn­dex = 1 or startIn­dex = 0 ) then
s = 1
e = count( ancestor )
else if( startIn­dex < 0 ) then
e = count( ancestor )
s = startIn­dex + e + 1
else if( startIn­dex > 0 ) then
s = startIndex
e = count( ancestor )
end if
else if( p = 3 ) then
if( startIn­dex = 0 or endIn­dex = 0 ) then
return retval
else if( startIn­dex > 0 and endIn­dex < 0 ) then
s = startIndex
e = count( ances­tor ) + endIn­dex + 1
else if( startIn­dex < 0 and endIn­dex > 0 ) then
return retval
else if( startIn­dex > 0 and endIn­dex > 0 ) then
if( startIn­dex = endIn­dex ) then
s = startIndex
e = startIndex
else if( startIn­dex > endIn­dex ) then
return retval
else if( startIn­dex < endIn­dex ) then
s = startIndex
e = endIndex
end if
else if( startIn­dex < 0 and endIn­dex < 0 ) then
if( startIn­dex = endIn­dex ) then
s = count( ances­tor ) + startIn­dex + 1
e = s
else if( startIn­dex > endIn­dex ) then
return retval
else if( startIn­dex < endIn­dex ) then
s = count( ances­tor ) + startIn­dex + 1
e = count( ances­tor ) + endIn­dex + 1
end if
end if
end if
repeat with i = s to e
retval.add( ances­tor[ i ] )
end repeat
return retval
end slice

on splice( me, startIn­dex, delete­Count, values )
ret­val = me.script.new()
p = the paramcount
s = inte­ger( startIndex )
if( voidp( s ) or s = 0 ) then s = 1
if( s < 0 ) then s = count( ances­tor ) + s + 1
if( p = 1 ) then
return retval
else if( p <= 2 ) then
e = count( ancestor )
else
if( ilk( delete­Count ) <> #inte­ger ) then delete­Count = 0
e = min( count( ances­tor ), s + delete­Count — 1 )
end if
repeat with i = e down to s
retval.addAt( 1, ances­tor[ i ] )
callAnces­tor( #deleteAt, me, i )
end repeat
if( p < 4 ) then return retval
repeat with j = p down to 4
xparam = param( j )
if( ilk( xparam ) = #list or ilk( xparam ) = #pro­pList ) then
cnt = count( xparam )
repeat with jj = cnt down to 1
callAnces­tor( #addAt, me, s, xparam[ jj ] )
end repeat
else
callAnces­tor( #addAt, me, s, xparam )
end if
end repeat
return retval
end splice

on some( me, ahan­dler, aobject )
if( voidP( aob­ject ) ) then aob­ject = _movie
cnt = count( ancestor )
repeat with i = 1 to cnt
ele­ment = ances­tor[ i ]
f = call( ahan­dler, aob­ject, ele­ment, i, me )
if( f = TRUE ) then
return f
end if
end repeat
return FALSE
end some

on every( me, ahan­dler, aobject )
if( voidP( aob­ject ) ) then aob­ject = _movie
cnt = count( ancestor )
repeat with i = 1 to cnt
ele­ment = ances­tor[ i ]
f = call( ahan­dler, aob­ject, element )
if( f = FALSE ) then
return f
end if
end repeat
return TRUE
end every

on fil­ter( me, ahan­dler, aobject )
if( voidP( aob­ject ) ) then aob­ject = _movie
cnt = count( ancestor )
ret­val = me.script.new()
repeat with i = 1 to cnt
ele­ment = ances­tor[ i ]
f = call( ahan­dler, aob­ject, element )
if( f = TRUE ) then
retval.append( element )
end if
end repeat
return retval
end filter

on forE­ach( me, ahan­dler, aobject )
if( voidP( aob­ject ) ) then aob­ject = _movie
cnt = count( ancestor )
repeat with i = cnt down to 1
ele­ment = ances­tor[ i ]
f = call( ahan­dler, aob­ject, ele­ment, i, me )
end repeat
end forEach

on map( me, ahan­dler, aobject )
if( voidP( aob­ject ) ) then aob­ject = _movie
cnt = count( ancestor )
ret­val = me.script.new()
repeat with i = 1 to cnt
ele­ment = ances­tor[ i ]
retval.append( call( ahan­dler, aob­ject, element ) )
end repeat
return retval
end map

on shuf­fle( me )
cnt = count( ancestor )
if ( cnt = 0 ) then return FALSE
repeat while ( cnt > 0 )
j = ran­dom( cnt )
tem­pi = ances­tor[ cnt ]
tem­pj = ances­tor[ j ]
ances­tor[ cnt ] = tempj
ances­tor[ j ] = tempi
cnt = cnt — 1
end repeat
return TRUE
end shuffle

on count( me )
if( voidP( ances­tor ) ) then return VOID
return count( ancestor )
end count

on con­cat( me )
p = the paramCount
t = callAnces­tor( #dupli­cate, me )
ret­val = me.script.new()
repeat with i = 2 to p
e = param( i )
if( ilk( e ) = #list ) then
cnt = count( e )
repeat with j = 1 to cnt
t.append( e[ j ] )
end repeat
else
t.append( e )
end if
end repeat
retval.ancestor = t
return retval
end concat

on reverse( me )
left = 1
right = count( ancestor )
repeat while( left < right )
temp = ances­tor[ left ]
ances­tor[ left ] = ances­tor[ right ]
ances­tor[ right ] = temp
left = left + 1
right = right — 1
end repeat
end reverseList

on join( me, sep )
t = EMPTY
cnt = count( ancestor )
repeat with i = 1 to cnt
put ances­tor[ i ] & sep after t
end repeat
len = t.length
real­length = ( len — sep.length ) + 1
repeat with j = len down to reallength
delete t.char[ t.length ]
end repeat
return t
end join

on toString( me )
t = me.join( “,” )
return t
end toString

on sort( me, order )
if( voidP( order ) or [#ASC, #DESC].getPos( order ) = 0 ) then order = #ASC
case( order ) of
#ASC :
callAnces­tor( #sort, me )
#DESC:
callAnces­tor( #sort, me )
me.reverse()
end case
end sort

on sor­tOn( me, colum­n­toSor­tOn, order )
if ( ilk( ances­tor ) <> #list ) then return FALSE
if ( count( ances­tor ) = 0 ) then return FALSE
if ( ilk( ancestor[1] ) <> #list ) then return FALSE
if( voidP( order ) or [#ASC, #DESC].getPos( order ) = 0 ) then order = #ASC
case( order ) of
#ASC :__sort_on_internal_ASC( me, columntoSortOn )
#DESC:__sort_on_internal_DESC( me, columntoSortOn )
end case
end sortOn

on is( me, atype )
obj = me
types = [ #list, #xlist ]
t = obj.typeOf()
repeat while t <> #xlist
types.append( t )
obj = obj.getAprop( #ances­tor )
t = obj.typeOf()
end repeat
put types
return( types.getPos( atype ) > 0 )
end is

on type­Of( me )
the itemde­lim­iter = quote
return sym­bol( string( me ).item[ 2 ] )
end typeOf

on inter­face( me )
msg = EMPTY
put “new( {…args} ) — Returns a new list, you can spec­i­fy the ini­tial val­ues as argu­ments.” & RETURN after msg
put “dis­pose() — Destroys the list” & RETURN after msg
put “shuf­fle() — Shuf­fles the list val­ues in-place, using Fisher–Yates algo­rithm.” & RETURN after msg
put “add( {…args} )* — Adds val­ues to a list. For a sort­ed list, the val­ues are placed in their prop­er order.” & RETURN after msg
put “getAt()* — Pre­vents “&quote&“Index out of range”&quote&” excep­tion.” & RETURN after msg
put “deleteAt()* — Pre­vents “&quote&“Index out of range”&quote&” excep­tion.” & RETURN after msg
put “append( {…args} )* — Adds the spec­i­fied val­ues to the end of a list.” & RETURN after msg
put “shift() — Removes the first ele­ment from an array and returns that ele­ment.” & RETURN after msg
put “pop() — Removes the last ele­ment from a list and returns the val­ue of that ele­ment.” & RETURN after msg
put “unshift( …args ) — Adds one or more ele­ments to the begin­ning of a list and returns the new count of the list.” & RETURN after msg
put “slice( startIn­dex, endIn­dex ) — Returns a new list that con­sists of a range of ele­ments from the orig­i­nal list, with­out mod­i­fy­ing the orig­i­nal list.” & RETURN after msg
put “splice( startIn­dex, delete­Count{, … val­ues} ) — Adds ele­ments to and removes ele­ments from a list.” & RETURN after msg
put “some( han­dler{, object} ) — Exe­cutes a test func­tion on each item in the list until an item is reached that returns TRUE.” & RETURN after msg
put “every( han­dler{, object} ) — Exe­cutes a test han­dler on each item in the list until an item is reached that returns FALSE for the spec­i­fied han­dler.” & RETURN after msg
put “fil­ter( han­dler{, object} ) — Exe­cutes a test han­dler on each item in the list and con­structs a new list for all items that return TRUE for the spec­i­fied han­dler.” & RETURN after msg
put “forE­ach( han­dler{, object} ) — Exe­cutes a han­dler on each item in the list.” & RETURN after msg
put “map( han­dler{, object} ) — Exe­cutes a han­dler on each item in a list, and con­structs a new list of items cor­re­spond­ing to the results of the han­dler on each item in the orig­i­nal list.” & RETURN after msg
put “sort( {sym­bol­Order} ) — Sorts the list ascend­ing or descend­ing ( #ASC / #DESC )” & RETURN after msg
put “sor­tOn( int­ColumnIn­dex{, sym­bol­Order} ) — Sorts the list on giv­en “&quote&“column”&quote&”( #ASC or #DESC ).” & RETURN after msg
put “con­cat() — Con­cate­nates the ele­ments spec­i­fied in the para­me­ters with the ele­ments in a list and cre­ates a new list.” & RETURN after msg
put “reverse() — Revers­es the list in-place.” & RETURN after msg
put “join( sep­a­ra­tor ) — Returns a string, con­struct­ed by items in the list, with the sep­a­ra­tor insert­ed between them.” & RETURN after msg
put “toString() — Returns a string that rep­re­sents the ele­ments in the spec­i­fied list, sep­a­rat­ed by com­mas.” & RETURN after msg
put “lastIn­dex­Of( searchEle­ment, startIn­dex ) — Search­es for an item in a list, work­ing back­ward from the last item, and returns the index posi­tion of the match­ing item.” & RETURN after msg
put “type­Of() — Returns the name of the script as a sym­bol.” & RETURN after msg
put “is()” after msg
return msg
end interface

————————————————-
PRIVATE METHODS
————————————————-
on ___________________________PRIVATE_METHODS
end
on __sort_on_internal_ASC( me, c )
t = list()
t[ c ] = 0
ancestor.add( t )
cnt = count( ancestor )
repeat with i = cnt down to 0
flipped = FALSE
j = 0
repeat while j < i
j = j + 1
if ( j + 1 < cnt ) then
aInt = float( ances­tor[ j ][ c ] )
bInt = float ( ances­tor[ j + 1 ][ c ] )
if ( aInt > bInt ) then
t = ances­tor[ j ]
ances­tor[ j ] = ances­tor[ j + 1 ]
ances­tor[ j + 1 ] = t
flipped = TRUE
end if
end if
end repeat
end repeat
ancestor.deleteAt( cnt )
return TRUE
end __sort_on_internal_ASC

on __sort_on_internal_DESC( me, c )
t = list()
t[ c ] = 0
ancestor.add( t )
cnt = count( ancestor )
repeat with i = cnt down to 0
flipped = FALSE
j = 0
repeat while j < i
j = j + 1
if ( j + 1 < cnt ) then
aInt = float( ances­tor[ j ][ c ] )
bInt = float ( ances­tor[ j + 1 ][ c ] )
if ( aInt < bInt ) then
t = ances­tor[ j ]
ances­tor[ j ] = ances­tor[ j + 1 ]
ances­tor[ j + 1 ] = t
flipped = TRUE
end if
end if
end repeat
end repeat
ancestor.deleteAt( cnt )
return TRUE
end __sort_on_internal_DESC

on ___________________________END_XLIST
end 


An exam­ple of fil­ter() method:

myList = script( “xList” ).new( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 )
put myList.filter( #getEven ).toString() — “2,4,6,8,10”

– A han­dler, defined in a movie script:
on getEven( aelement )
return( aele­ment mod 2 = 0 )
end getEven

 
Comments

Added sort() method.

sort( {order} )
order — #Sym­bol; #ASC or #DESC

Hi Vlad,

An excel­lent com­ple­ment to the Lin­go list function.

I have one ques­tion on the dis­pos­al of lists returned by the extend­ed list object. Exam­ples are : splice, slice, con­cat, fil­ter, etc. which cre­ate instances of the extend­ed list object.

Nor­mal­ly, when I use Lin­go’s native lists as a local vari­able in a han­dler, I do not have to explic­it­ly destroy the vari­able upon exit. Now, sup­pose in one of my han­dlers I call the fil­ter func­tion of the extend­ed list and receive a fil­tered list, which is local to my han­dler, would I have to destroy the fil­tered list explic­it­ly? Would it cause a mem­o­ry leak if I don’t destroy it?

I sup­pose, the same would apply to your extend­ed list fil­ter func­tion as well. You cre­ate a new instance of the list and sim­ply return it to the call­ing func­tion. Would this have cre­at­ed a mem­o­ry leak, or would Lin­go’s garbage col­lec­tor take care of it automatically?

For­give me if the ques­tion sounds stu­pid. But I am con­fused and hence tempt­ed to ask.

Thanks.

Hi, Man­ish!

Sor­ry for the delayed response. 

Frankly, I have not test­ed the issues you men­tioned and can not say any­thing for sure.
Are you face any prob­lems with mem­o­ry leaks using the “Xlist”?

Cheers,
vlad

Thanks for the response, Vlad.

Unfor­tu­nate­ly, I don’t think there is any way of know­ing if this would cause a mem­o­ry leak.

What is known is that Direc­tor takes care of releas­ing the mem­o­ry used up by a local vari­able when it goes out of scope. So, a nor­mal list assigned to a local vari­able would be auto­mat­i­cal­ly dis­posed. What I don’t know is when a list is wrapped into a prop­er­ty vari­able (like it is in xList), would Direc­tor dis­pose the xList object also when it goes out of scope of an xList method, since the xList object is itself local to the method in which it was created?

My apolo­gies, Vlad. xList does not cause any mem­o­ry leaks.

Since ret­Val is local to each method in which it cre­ate a new xList child object, Direc­tor dis­pos­es it once it is out of scope. There­fore, if the vari­able to which the returned list is assigned in a call­ing han­dler is also local to that han­dler, Direc­tor would sim­i­lar­ly dis­pose the vari­able once it is out of scope, caus­ing the xList child object ref­er­ence count to become 0. So, no mem­o­ry leak would occur.