root/lu/LUList.lua

Revision 944 (checked in by rsz, 3 years ago)

cleanup

Line 
1 --------------------------------------------------------------------------------
2 -- Title:               LUList.lua
3 -- Description:         Like a square peg in a round hole
4 -- Author:              Raphaël Szwarc http://alt.textdrive.com/lua/
5 -- Creation Date:       February 1, 2005
6 -- Legal:               Copyright (C) 2005 Raphaël Szwarc
7 --------------------------------------------------------------------------------
8
9 -- import dependencies
10 local LUObject = require( "LUObject" )
11 local LUObjectReader = require( "LUObjectReader" )
12 local LUObjectWriter = require( "LUObjectWriter" )
13 local LUString = require( "LUString" )
14 local io = require( "io" )
15 local math = require( "math" )
16 local table = require( "table" )
17
18 -- define the class
19 local super = LUObject
20 local self = super()
21
22 -- method for initialization
23 function self:init( aContent, aMode )
24         self = super.init( self )
25
26         if aMode ~= nil then
27                 setmetatable( self:content(), { __mode = aMode } )
28         end
29        
30         self:addAll( aContent )
31
32         return self
33 end
34                
35 -- method to add an object to this list
36 function self:add( aValue )
37         if aValue ~= nil then
38                 local aContent = self:content()
39        
40                 aContent[ #aContent + 1 ] = aValue
41         end
42        
43         return self
44 end
45
46 -- method to add all the values of the given list to this list
47 function self:addAll( someValues )
48         if self:isKindOf( someValues ) == true then
49                 for anIndex, aValue in someValues:iterator() do
50                         self:add( aValue )
51                 end
52         elseif type( someValues ) == "table" then
53                 for anIndex, aValue in ipairs( someValues ) do
54                         self:add( aValue )
55                 end
56         end
57        
58         return self
59 end
60
61 -- method to clear this list
62 function self:clear()
63         for anIndex, aValue in self:iterator() do
64                 self:remove( anIndex )
65         end
66        
67         return self
68 end
69
70 -- method to access this list content
71 function self:content()
72         if self._content == nil then
73                 self._content = {}
74         end
75
76         return self._content
77 end
78
79 -- method to copy this list
80 function self:copy()
81         return self:new():addAll( self )
82 end
83
84 -- method to compare the given object with this list for equality
85 function self:equals( anObject )
86         if self:isKindOf( anObject ) == true then
87                 if self:size() == anObject:size() then
88                         for anIndex, aValue in self:iterator() do
89                                 local anotherValue = anObject:get( anIndex )
90
91                                 if aValue ~= anotherValue then
92                                         return false
93                                 end
94                         end
95                        
96                         return true
97                 end
98         end
99        
100         return false
101 end
102
103 -- method to get an object at a given index
104 function self:get( anIndex )
105         anIndex = tonumber( anIndex )
106        
107         return self:content()[ anIndex or 1 ]
108 end
109
110 -- method to check if this list contains any data
111 function self:hasData()
112         for anIndex, aValue in self:iterator() do
113                 return true
114         end
115        
116         return false
117 end
118
119 -- method to check if this list contains a given object
120 function self:hasValue( aValue )
121         local anIndex = self:indexOf( aValue )
122
123         if anIndex ~= nil then
124                 return true, anIndex
125         end
126        
127         return false
128 end
129
130 -- method to get the index of an object
131 function self:indexOf( anObject )
132         if anObject ~= nil then
133                 for anIndex, aValue in self:iterator() do
134                         if anObject == aValue then
135                                 return anIndex
136                         end
137                 end
138         end
139        
140         return nil
141 end
142
143 function self:iterator()
144         return ipairs( self:content() )
145 end
146
147 -- method to join this list items with a given string
148 function self:join( aString )
149         local aBuffer = {}
150
151         for anIndex, aValue in self:iterator() do
152                 aValue = tostring( aValue )
153
154                 aBuffer[ #aBuffer + 1 ] = aValue
155         end
156                
157         return table.concat( aBuffer, aString or "" )
158 end
159
160 -- method to return a portion of this list
161 function self:range( aStart, anEnd )
162         local aList = self:new()
163        
164         for index = aStart, anEnd do
165                 local aValue = self:get( index )
166                
167                 aList:add( aValue )
168         end
169        
170         return aList
171 end
172
173 -- method to remove from this list
174 function self:remove( anIndex )
175         anIndex = tonumber( anIndex )
176
177         table.remove( self:content(), anIndex or 1 )
178        
179         return self
180 end
181
182 -- method to remove all the values of the given list
183 function self:removeAll( someValues )
184         if self:isKindOf( someValues ) == true then
185                 for anIndex, aValue in someValues:iterator() do
186                         self:removeValue( aValue )
187                 end
188         elseif type( someValues ) == "table" then
189                 for anIndex, aValue in ipairs( someValues ) do                       
190                         self:removeValue( aValue )
191                 end
192         end
193        
194         return self
195 end
196
197 -- method to remove the last value of this list
198 function self:removeLast()
199         return self:remove( self:size() )
200 end
201
202 -- method to remove the given object from this list
203 function self:removeValue( aValue )
204         local anIndex = self:indexOf( aValue )
205        
206         if anIndex ~= nil then
207                 return self:remove( anIndex )
208         end
209        
210         return self
211 end
212
213 -- method to reverse this list order
214 function self:reverse()
215         local aSize = self:size()
216        
217         if aSize > 1 then
218                 local aContent = self:content()
219                 local count = math.floor( aSize / 2 )
220                
221                 for index = 1, count do
222                         local anObject = aContent[ index ]
223                         local anotherIndex = aSize - index + 1
224                         local anotherObject = aContent[ anotherIndex ]       
225
226                         aContent[ index ] = anotherObject
227                         aContent[ anotherIndex ] = anObject
228                 end
229         end
230        
231         return self
232 end
233
234 -- method to pseudo-randomely sort this list
235 function self:shuffle()
236         if self:size() > 1 then
237                 local aContent = self:content()
238                 local count = self:size()
239                
240                 for index = 1, count do
241                         local anObject = aContent[ index ]
242                         local anotherIndex = math.random( index, count )
243                         local anotherObject = aContent[ anotherIndex ]
244                        
245                         aContent[ index ] = anotherObject
246                         aContent[ anotherIndex ] = anObject
247                 end
248         end
249
250         return self
251 end
252
253 -- method to access this list size
254 function self:size()
255         return #self:content()
256 end
257
258 -- method to sort this list
259 function self:sort( aComparator )
260         table.sort( self:content(), aComparator )
261        
262         return self
263 end
264
265 -- method for string representation
266 function self:toString( isFormal )
267         local aBuffer = {}
268
269         aBuffer[ #aBuffer + 1 ] = "{ "
270        
271         if self:size() > 0 then
272                 for anIndex, aValue in self:iterator() do
273                         if isFormal == true then
274                                 aBuffer[ #aBuffer + 1 ] = ( "[ %d ] = " ):format( anIndex )
275                         end
276                        
277                         aBuffer[ #aBuffer + 1 ] = LUString:toString( aValue, isFormal )
278                        
279                         aBuffer[ #aBuffer + 1 ] = ", "
280                 end
281
282                 aBuffer[ #aBuffer ] = nil
283         end
284        
285         aBuffer[ #aBuffer + 1 ] = " }"
286        
287         return table.concat( aBuffer, "" )
288 end
289
290 -- method to load this list content from a given location
291 function self:load( aPath )
292         if aPath == nil then
293                 aPath = self._path
294         else
295                 aPath = tostring( aPath )
296                 self._path = aPath
297         end
298
299         if aPath ~= nil then
300                 local aFile = io.open( aPath, "r" )
301                
302                 if aFile ~= nil then
303                         local aContent = aFile:read( "*all" )
304                        
305                         aFile:close()
306                        
307                         if aContent ~= nil then
308                                 self:addAll( LUObjectReader:read( aContent ) )
309                         else
310                                 return nil, ( "invalid content at '%s'" ):format( aPath )
311                         end
312                 else
313                         return nil, ( "invalid path '%s'" ):format( aPath )
314                 end
315         else
316                 return nil, "invalid path"
317         end
318        
319         return self
320 end
321
322 -- method to store this list at a given location
323 function self:store( aPath )
324         if aPath == nil then
325                 aPath = self._path
326         else
327                 aPath = tostring( aPath )
328                 self._path = aPath
329         end
330
331         if aPath ~= nil then
332                 local aFile = io.open( aPath, "w" )
333                
334                 if aFile ~= nil then
335                         aFile:write( LUObjectWriter:write( self:content() ) )
336                         aFile:close()
337                 else
338                         return nil, ( "invalid path at '%s'" ):format( aPath )
339                 end
340         else
341                 return nil, "invalid path"
342         end
343        
344         return self
345 end
346
347 --------------------------------------------------------------------------------
348 -- query methods
349 --------------------------------------------------------------------------------
350
351 function self:first()
352         return self:get( 1 )
353 end
354
355 function self:last()
356         return self:get( self:size() )
357 end
358
359 function self:min( aMethod )
360         local aType = type( aMethod )
361         local aMin = nil
362
363         for anIndex, aValue in self:iterator() do
364                 local aLocalValue = aValue
365                
366                 if aType == "string" then
367                         aLocalValue = aLocalValue:invoke( aMethod )
368                 elseif aType == "function" then
369                         aLocalValue = aMethod( aLocalValue )
370                 end
371                
372                 if aLocalValue ~= nil and aMin == nil or aMin > aLocalValue then
373                         aMin = aValue
374                 end
375         end
376
377         return aMin
378 end
379
380 function self:max( aMethod )
381         local aType = type( aMethod )
382         local aMax = nil
383
384         for anIndex, aValue in self:iterator() do
385                 local aLocalValue = aValue
386                
387                 if aType == "string" then
388                         aLocalValue = aLocalValue:invoke( aMethod )
389                 elseif aType == "function" then
390                         aLocalValue = aMethod( aLocalValue )
391                 end
392                
393                 if aLocalValue ~= nil and aMax == nil or aMax < aValue then
394                         aMax = aValue
395                 end
396         end
397
398         return aMax
399 end
400
401 function self:sum( aMethod )
402         local aType = type( aMethod )
403         local aSum = 0
404        
405         for anIndex, aValue in self:iterator() do
406                 if aType == "string" then
407                         aValue = aValue:invoke( aMethod )
408                 elseif aType == "function" then
409                         aValue = aMethod( aValue )
410                 end
411                
412                 aValue = tonumber( aValue )
413                
414                 if aValue ~= nil then
415                         aSum = aSum + aValue
416                 end
417         end
418        
419         return aSum
420 end
421
422 function self:select( aMethod )
423         local aList = self:new()
424         local aType = type( aMethod )
425
426         for anIndex, aValue in self:iterator() do
427                 if aType == "string" then
428                         aValue = aValue:invoke( aMethod )
429                 elseif aType == "function" then
430                         aValue = aMethod( aValue )
431                 end
432                
433                 aList:add( aValue )
434         end
435
436         return aList
437 end
438
439 function self:distinct( aMethod )
440         local aType = type( aMethod )
441         local aMap = require( "LUMap" ):new()
442        
443         for anIndex, aValue in self:iterator() do
444                 local aLocalValue = aValue
445                
446                 if aType == "string" then
447                         aLocalValue = aLocalValue:invoke( aMethod )
448                 elseif aType == "function" then
449                         aLocalValue = aMethod( aLocalValue )
450                 end
451
452                 aMap:put( aLocalValue, aValue )
453         end
454        
455         return aMap:values()
456 end
457
458 function self:between( aMin, aMax, aMethod )
459         local aType = type( aMethod )
460         local aList = self:new()
461
462         for anIndex, aValue in self:iterator() do
463                 local aLocalValue = aValue
464
465                 if aType == "string" then
466                         aLocalValue = aLocalValue:invoke( aMethod )
467                 elseif aType == "function" then
468                         aLocalValue = aMethod( aLocalValue )
469                 end
470                
471                 if aLocalValue ~= nil and aLocalValue >= aMin and aLocalValue <= aMax then
472                         aList:add( aValue )
473                 end
474         end
475
476         return aList
477 end
478
479 function self:exists( aMethod )
480         local aType = type( aMethod )
481
482         for anIndex, aValue in self:iterator() do
483                 if aType == "string" then
484                         aValue = aValue:invoke( aMethod )
485                 elseif aType == "function" then
486                         aValue = aMethod( aValue )
487                 end
488
489                 if aValue ~= nil and aValue ~= false then
490                         return true
491                 end
492         end
493        
494         return false
495 end
496
497 function self:group( aMethod )
498         local aType = type( aMethod )
499         local aMap = require( "LUMap" ):new()
500
501         for anIndex, aValue in self:iterator() do               
502                 local aLocalValue = aValue
503                
504                 if aType == "string" then
505                         aLocalValue = aLocalValue:invoke( aMethod )
506                 elseif aType == "function" then
507                         aLocalValue = aMethod( aLocalValue )
508                 end
509                
510                 if aLocalValue ~= nil then
511                         local aList = aMap:get( aLocalValue )
512                        
513                         if aList == nil then
514                                 aList = self:new()
515                                 aMap:put( aLocalValue, aList )       
516                         end
517                        
518                         aList:add( aValue )
519                 end
520         end
521        
522         return aMap
523 end
524
525 function self:having( aMethod )
526         local aType = type( aMethod )
527         local aList = self:new()
528        
529         for anIndex, aValue in self:iterator() do
530                 local aLocalValue = aValue
531
532                 if aType == "string" then
533                         aLocalValue = aLocalValue:invoke( aMethod )
534                 elseif aType == "function" then
535                         aLocalValue = aMethod( aLocalValue )
536                 end
537
538                 if aLocalValue ~= nil and aLocalValue ~= false then
539                         aList:add( aValue )
540                 end
541         end
542        
543         return aList
544 end
545
546 function self:order( aMethod )
547         local aType = type( aMethod )
548         local aList = self:copy()
549         local aComparator = nil
550        
551         if aType == "string" then
552                 aComparator = function( anObject, anotherObject )
553                         return anObject:invoke( aMethod ) < anotherObject:invoke( aMethod )
554                 end
555         elseif aType == "function" then
556                 aComparator = function( anObject, anotherObject )
557                         return aMethod( anObject ) < aMethod( anotherObject )
558                 end
559         end
560
561         table.sort( aList:content(), aComparator )
562        
563         return aList
564 end
565
566 function self:union( someValues, aMethod )
567         local aList = self:copy()
568        
569         aList:addAll( someValues )
570        
571         return aList:distinct( aMethod )
572 end
573
574 function self:unionAll( someValues )
575         local aList = self:copy()
576
577         aList:addAll( someValues )
578
579         return aList
580 end
581
582 function self:intersect( someValues, aMethod )
583         local aType = type( aMethod )
584         local aList = self:new()
585         local aMap = someValues:group( aMethod )
586
587         for anIndex, aValue in self:iterator() do
588                 local aLocalValue = aValue
589
590                 if aType == "string" then
591                         aLocalValue = aLocalValue:invoke( aMethod )
592                 elseif aType == "function" then
593                         aLocalValue = aMethod( aLocalValue )
594                 end
595
596                 if aMap:hasKey( aLocalValue ) == true then
597                         aList:add( aValue )
598                 end         
599         end
600        
601         return aList
602 end
603
604 function self:minus( someValues, aMethod )
605         local aType = type( aMethod )
606         local aList = self:new()
607         local aMap = someValues:group( aMethod )
608
609         for anIndex, aValue in self:iterator() do
610                 local aLocalValue = aValue
611
612                 if aType == "string" then
613                         aLocalValue = aLocalValue:invoke( aMethod )
614                 elseif aType == "function" then
615                         aLocalValue = aMethod( aLocalValue )
616                 end
617
618                 if aMap:hasKey( aLocalValue ) == false then
619                         aList:add( aValue )
620                 end         
621         end
622        
623         return aList
624 end
625
626
627 return self
Note: See TracBrowser for help on using the browser.