Modify

Opened 10 years ago

Closed 10 years ago

Last modified 10 years ago

#2563 closed Feature Request (Rejected)

UDF - _ArrayUnique - proposal

Reported by: mlipok Owned by:
Milestone: Component: Standard UDFs
Version: Severity: None
Keywords: Cc:

Description

now is:

Func _ArrayUnique(Const ByRef $aArray, $iColumn = Default, $iBase = Default, $iCase = Default, $iFlags = Default)
	If $iColumn = Default Then $iColumn = 1
	If $iBase = Default Then $iBase = 0
	If $iCase = Default Then $iCase = 0
	If $iFlags = Default Then $iFlags = 1
	; Start bounds checking
	If UBound($aArray) = 0 Then Return SetError(1, 0, 0) ; Check if array is empty, or not an array
	; $iBase can only be 0 or 1, if anything else, return with an error
	If $iBase < 0 Or $iBase > 1 Or (Not IsInt($iBase)) Then Return SetError(2, 0, 0)
	If $iCase < 0 Or $iCase > 1 Or (Not IsInt($iCase)) Then Return SetError(2, 0, 0)
	If $iFlags < 0 Or $iFlags > 1 Or (Not IsInt($iFlags)) Then Return SetError(4, 0, 0)
	Local $iDims = UBound($aArray, 0), $iNumColumns = UBound($aArray, 2)
	If $iDims > 2 Then Return SetError(3, 0, 0)
	; checks the given dimension is valid
	If ($iColumn < 1) Or ($iNumColumns = 0 And ($iColumn - 1 > $iNumColumns)) Or ($iNumColumns > 0 And ($iColumn > $iNumColumns)) Then Return SetError(3, 0, 0)
	; make $iColumn an array index, note this is ignored for 1D arrays
	$iColumn -= 1
	; create dictionary
	Local $oD = ObjCreate("Scripting.Dictionary")
	; compare mode for strings
	; 0 = binary, which is case sensitive
	; 1 = text, which is case insensitive
	; this expression forces either 1 or 0
	$oD.CompareMode = Number(Not $iCase)
	Local $vElem
	; walk the input array
	For $i = $iBase To UBound($aArray) - 1
		If $iDims = 1 Then
			; 1D array
			$vElem = $aArray[$i]
		Else
			; 2D array
			$vElem = $aArray[$i][$iColumn]
		EndIf
		; add key to dictionary
		; NOTE: accessing the value (.Item property) of a key that doesn't exist creates the key :)
		; keys are guaranteed to be unique
		$oD.Item($vElem)
	Next
	;
	; return the array of unique keys
	If BitAND($iFlags, 1) = 1 Then
		Local $aTemp = $oD.Keys()
		_ArrayInsert($aTemp, 0, $oD.Count)
		Return $aTemp
	Else
		Return $oD.Keys()
	EndIf
EndFunc   ;==>_ArrayUnique

proposal:

Func _ArrayUnique(Const ByRef $aArray, $iColumn = Default, $iBase = Default, $iCase = Default, $iFlags = Default)
	If $iColumn = Default Then $iColumn = 1
	If $iBase = Default Then $iBase = 0
	If $iCase = Default Then $iCase = 0
	If $iFlags = Default Then $iFlags = 1
	; Start bounds checking
	Local $iUBound = UBound($aArray)
	If $iUBound = 0 Then Return SetError(1, 0, 0) ; Check if array is empty, or not an array
	; $iBase can only be 0 or 1, if anything else, return with an error
	If $iBase < 0 Or $iBase > 1 Or (Not IsInt($iBase)) Then Return SetError(2, 0, 0)
	If $iCase < 0 Or $iCase > 1 Or (Not IsInt($iCase)) Then Return SetError(2, 0, 0)
	If $iFlags < 0 Or $iFlags > 1 Or (Not IsInt($iFlags)) Then Return SetError(4, 0, 0)
	Local $iDims = UBound($aArray, 0), $iNumColumns = UBound($aArray, 2)
	If $iDims > 2 Then Return SetError(3, 0, 0)
	; checks the given dimension is valid
	If ($iColumn < 1) Or ($iNumColumns = 0 And ($iColumn - 1 > $iNumColumns)) Or ($iNumColumns > 0 And ($iColumn > $iNumColumns)) Then Return SetError(3, 0, 0)
	; make $iColumn an array index, note this is ignored for 1D arrays
	$iColumn -= 1
	; create dictionary
	Local $oD = ObjCreate("Scripting.Dictionary")
	; compare mode for strings
	; 0 = binary, which is case sensitive
	; 1 = text, which is case insensitive
	; this expression forces either 1 or 0
	$oD.CompareMode = Number(Not $iCase)
	Local $vElem
	; walk the input array
	$iUBound -=1
	For $i = $iBase To $iUBound
		If $iDims = 1 Then
			; 1D array
			$vElem = $aArray[$i]
		Else
			; 2D array
			$vElem = $aArray[$i][$iColumn]
		EndIf
		; add key to dictionary
		; NOTE: accessing the value (.Item property) of a key that doesn't exist creates the key :)
		; keys are guaranteed to be unique
		$oD.Item($vElem)
	Next
	;
	; return the array of unique keys
	If BitAND($iFlags, 1) = 1 Then
		Local $aTemp = $oD.Keys()
		_ArrayInsert($aTemp, 0, $oD.Count)
		Return $aTemp
	Else
		Return $oD.Keys()
	EndIf
EndFunc   ;==>_ArrayUnique

changed:

Local $iUBound = UBound($aArray)
If $iUBound = 0 Then Return SetError(1, 0, 0) ; Check if array is empty, or not an array

and

$iUBound -=1
For $i = $iBase To $iUBound

because: faster

Attachments (0)

Change History (10)

comment:1 Changed 10 years ago by mlipok

sorry for short topic name
please change to
UDF - _ArrayUnique - proposal

comment:2 Changed 10 years ago by TicketCleanup

  • Version Other deleted

Automatic ticket cleanup.

comment:3 Changed 10 years ago by AdmiralAlkex

  • Summary changed from UDF - to UDF - _ArrayUnique - proposal

comment:4 Changed 10 years ago by BrewManNH

How much faster is it doing it this way?

comment:5 Changed 10 years ago by mlipok

RESULTS AutoIt 3.3.8.1:

>"C:\Program Files (x86)\AutoIt3\SciTE\AutoIt3Wrapper\AutoIt3Wrapper.exe" /run /prod /ErrorStdOut /in "L:\TOOLs\Macro\       TEST\TEST ArrayUnique.au3" /UserParams    
+>14:57:33 Starting AutoIt3Wrapper v.2.1.3.2 SciTE v.3.3.7.0 ;  Keyboard:00000415  OS:WIN_7/Service Pack 1  CPU:X64 OS:X64    Environment(Language:0415  Keyboard:00000415  OS:WIN_7/Service Pack 1  CPU:X64 OS:X64)
>Running AU3Check (3.3.9.5)  from:C:\Program Files (x86)\AutoIt3
+>14:57:33 AU3Check ended.rc:0
>Running:(3.3.8.1):C:\Program Files (x86)\AutoIt3\autoit3.exe "L:\TOOLs\Macro\       TEST\TEST ArrayUnique.au3"    
--> Press Ctrl+Alt+F5 to Restart or Ctrl+Break to Stop
TEST OLD: 61.8902842999841
TEST NEW: 7.55799276374465
+>14:57:34 AutoIt3.exe ended.rc:0
>Exit code: 0    Time: 0.974

RESULTS AutoIt 3.3.9.25:

>"C:\Program Files (x86)\AutoIt3\SciTE\AutoIt3Wrapper\AutoIt3Wrapper.exe" /run /beta /ErrorStdOut /in "L:\TOOLs\Macro\       TEST\TEST ArrayUnique.au3" /UserParams    
+>14:58:19 Starting AutoIt3Wrapper v.2.1.3.2 SciTE v.3.3.7.0 ;  Keyboard:00000415  OS:WIN_7/Service Pack 1  CPU:X64 OS:X64    Environment(Language:0415  Keyboard:00000415  OS:WIN_7/Service Pack 1  CPU:X64 OS:X64)
>Running AU3Check (3.3.9.25)  from:C:\Program Files (x86)\AutoIt3\Beta
+>14:58:19 AU3Check ended.rc:0
>Running:(3.3.9.25):C:\Program Files (x86)\AutoIt3\Beta\autoit3.exe "L:\TOOLs\Macro\       TEST\TEST ArrayUnique.au3"    
--> Press Ctrl+Alt+F5 to Restart or Ctrl+Break to Stop
TEST OLD: 9.17964658320134
TEST NEW: 3.99375813207523
+>14:58:19 AutoIt3.exe ended.rc:0
>Exit code: 0    Time: 0.750

TEST SCRIPT:

#include <array.au3>
Local $aTest[100][100]
For $iRows = 0 To 99
	For $iCols = 0 To 99
		$aTest[$iRows][$iCols] = '$iRows = ' & $iRows & '  $iCols = ' & $iCols
	Next
Next
Local $start = TimerInit()
_ArrayUnique($aTest)

ConsoleWrite('TEST OLD: ' & TimerDiff($start) & @CRLF)

Local $start = TimerInit()
_ArrayUnique_NEW($aTest)

ConsoleWrite('TEST NEW: ' & TimerDiff($start) & @CRLF)



Func _ArrayUnique_NEW(Const ByRef $aArray, $iColumn = Default, $iBase = Default, $iCase = Default, $iFlags = Default)
	If $iColumn = Default Then $iColumn = 1
	If $iBase = Default Then $iBase = 0
	If $iCase = Default Then $iCase = 0
	If $iFlags = Default Then $iFlags = 1
	; Start bounds checking
	Local $iUBound = UBound($aArray)
	If $iUBound = 0 Then Return SetError(1, 0, 0) ; Check if array is empty, or not an array
	; $iBase can only be 0 or 1, if anything else, return with an error
	If $iBase < 0 Or $iBase > 1 Or (Not IsInt($iBase)) Then Return SetError(2, 0, 0)
	If $iCase < 0 Or $iCase > 1 Or (Not IsInt($iCase)) Then Return SetError(2, 0, 0)
	If $iFlags < 0 Or $iFlags > 1 Or (Not IsInt($iFlags)) Then Return SetError(4, 0, 0)
	Local $iDims = UBound($aArray, 0), $iNumColumns = UBound($aArray, 2)
	If $iDims > 2 Then Return SetError(3, 0, 0)
	; checks the given dimension is valid
	If ($iColumn < 1) Or ($iNumColumns = 0 And ($iColumn - 1 > $iNumColumns)) Or ($iNumColumns > 0 And ($iColumn > $iNumColumns)) Then Return SetError(3, 0, 0)
	; make $iColumn an array index, note this is ignored for 1D arrays
	$iColumn -= 1
	; create dictionary
	Local $oD = ObjCreate("Scripting.Dictionary")
	; compare mode for strings
	; 0 = binary, which is case sensitive
	; 1 = text, which is case insensitive
	; this expression forces either 1 or 0
	$oD.CompareMode = Number(Not $iCase)
	Local $vElem
	; walk the input array
	$iUBound -=1
	For $i = $iBase To $iUBound
		If $iDims = 1 Then
			; 1D array
			$vElem = $aArray[$i]
		Else
			; 2D array
			$vElem = $aArray[$i][$iColumn]
		EndIf
		; add key to dictionary
		; NOTE: accessing the value (.Item property) of a key that doesn't exist creates the key :)
		; keys are guaranteed to be unique
		$oD.Item($vElem)
	Next
	;
	; return the array of unique keys
	If BitAND($iFlags, 1) = 1 Then
		Local $aTemp = $oD.Keys()
		_ArrayInsert($aTemp, 0, $oD.Count)
		Return $aTemp
	Else
		Return $oD.Keys()
	EndIf
EndFunc   ;==>_ArrayUnique

comment:6 Changed 10 years ago by BrewManNH

Two things.
First thing, your 2 tests aren't equal because the second time you run it the array has already run through the _ArrayUnique function and isn't the same the second test.
Second thing, reverse the two tests and the results will be reversed with the new function being just as slow as the old function, slow being a relative term here.

#include <array.au3>
Local $aTest[100][100], $aTest1
For $iRows = 0 To 99
	For $iCols = 0 To 99
		$aTest[$iRows][$iCols] = '$iRows = ' & $iRows & '  $iCols = ' & $iCols
	Next
Next
$aTest1 = $aTest ; make 2 identical arrays

Local $start = TimerInit()
_ArrayUnique_NEW($aTest1) ; swap the new one to run first

ConsoleWrite('TEST NEW: ' & TimerDiff($start) & @CRLF)
Local $start = TimerInit()
_ArrayUnique($aTest) ; and original to run second

ConsoleWrite('TEST OLD: ' & TimerDiff($start) & @CRLF)



Func _ArrayUnique_NEW(Const ByRef $aArray, $iColumn = Default, $iBase = Default, $iCase = Default, $iFlags = Default)
	If $iColumn = Default Then $iColumn = 1
	If $iBase = Default Then $iBase = 0
	If $iCase = Default Then $iCase = 0
	If $iFlags = Default Then $iFlags = 1
	; Start bounds checking
	Local $iUBound = UBound($aArray)
	If $iUBound = 0 Then Return SetError(1, 0, 0) ; Check if array is empty, or not an array
	; $iBase can only be 0 or 1, if anything else, return with an error
	If $iBase < 0 Or $iBase > 1 Or (Not IsInt($iBase)) Then Return SetError(2, 0, 0)
	If $iCase < 0 Or $iCase > 1 Or (Not IsInt($iCase)) Then Return SetError(2, 0, 0)
	If $iFlags < 0 Or $iFlags > 1 Or (Not IsInt($iFlags)) Then Return SetError(4, 0, 0)
	Local $iDims = UBound($aArray, 0), $iNumColumns = UBound($aArray, 2)
	If $iDims > 2 Then Return SetError(3, 0, 0)
	; checks the given dimension is valid
	If ($iColumn < 1) Or ($iNumColumns = 0 And ($iColumn - 1 > $iNumColumns)) Or ($iNumColumns > 0 And ($iColumn > $iNumColumns)) Then Return SetError(3, 0, 0)
	; make $iColumn an array index, note this is ignored for 1D arrays
	$iColumn -= 1
	; create dictionary
	Local $oD = ObjCreate("Scripting.Dictionary")
	; compare mode for strings
	; 0 = binary, which is case sensitive
	; 1 = text, which is case insensitive
	; this expression forces either 1 or 0
	$oD.CompareMode = Number(Not $iCase)
	Local $vElem
	; walk the input array
	$iUBound -=1
	For $i = $iBase To $iUBound
		If $iDims = 1 Then
			; 1D array
			$vElem = $aArray[$i]
		Else
			; 2D array
			$vElem = $aArray[$i][$iColumn]
		EndIf
		; add key to dictionary
		; NOTE: accessing the value (.Item property) of a key that doesn't exist creates the key :)
		; keys are guaranteed to be unique
		$oD.Item($vElem)
	Next
	;
	; return the array of unique keys
	If BitAND($iFlags, 1) = 1 Then
		Local $aTemp = $oD.Keys()
		_ArrayInsert($aTemp, 0, $oD.Count)
		Return $aTemp
	Else
		Return $oD.Keys()
	EndIf
EndFunc   ;==>_ArrayUnique

Try this one and see what I'm talking about, reverse the 2 tests back and forth and you'll see that they run equally as fast.

comment:7 Changed 10 years ago by mlipok

I see what you mean
but see even my modified script:

#include <array.au3>
Local $aTest[100][100], $aTest1
For $iRows = 0 To 99
	For $iCols = 0 To 99
		$aTest[$iRows][$iCols] = '$iRows = ' & $iRows & '  $iCols = ' & $iCols
	Next
Next
Local $aTest_OLD = $aTest ; make 2 identical arrays
Local $aTest_NEW = $aTest ; make 3 identical arrays

_ArrayUnique($aTest) ; Initial test


Local $start = TimerInit()
_ArrayUnique($aTest_OLD)

ConsoleWrite('TEST OLD: ' & TimerDiff($start) & @CRLF)


Local $start = TimerInit()
_ArrayUnique_NEW($aTest_NEW) ; swap the new one to run first

ConsoleWrite('TEST NEW: ' & TimerDiff($start) & @CRLF)


Func _ArrayUnique_NEW(Const ByRef $aArray, $iColumn = Default, $iBase = Default, $iCase = Default, $iFlags = Default)
	If $iColumn = Default Then $iColumn = 1
	If $iBase = Default Then $iBase = 0
	If $iCase = Default Then $iCase = 0
	If $iFlags = Default Then $iFlags = 1
	; Start bounds checking
	Local $iUBound = UBound($aArray)
	If $iUBound = 0 Then Return SetError(1, 0, 0) ; Check if array is empty, or not an array
	; $iBase can only be 0 or 1, if anything else, return with an error
	If $iBase < 0 Or $iBase > 1 Or (Not IsInt($iBase)) Then Return SetError(2, 0, 0)
	If $iCase < 0 Or $iCase > 1 Or (Not IsInt($iCase)) Then Return SetError(2, 0, 0)
	If $iFlags < 0 Or $iFlags > 1 Or (Not IsInt($iFlags)) Then Return SetError(4, 0, 0)
	Local $iDims = UBound($aArray, 0), $iNumColumns = UBound($aArray, 2)
	If $iDims > 2 Then Return SetError(3, 0, 0)
	; checks the given dimension is valid
	If ($iColumn < 1) Or ($iNumColumns = 0 And ($iColumn - 1 > $iNumColumns)) Or ($iNumColumns > 0 And ($iColumn > $iNumColumns)) Then Return SetError(3, 0, 0)
	; make $iColumn an array index, note this is ignored for 1D arrays
	$iColumn -= 1
	; create dictionary
	Local $oD = ObjCreate("Scripting.Dictionary")
	; compare mode for strings
	; 0 = binary, which is case sensitive
	; 1 = text, which is case insensitive
	; this expression forces either 1 or 0
	$oD.CompareMode = Number(Not $iCase)
	Local $vElem
	; walk the input array
	$iUBound -=1
	For $i = $iBase To $iUBound
		If $iDims = 1 Then
			; 1D array
			$vElem = $aArray[$i]
		Else
			; 2D array
			$vElem = $aArray[$i][$iColumn]
		EndIf
		; add key to dictionary
		; NOTE: accessing the value (.Item property) of a key that doesn't exist creates the key :)
		; keys are guaranteed to be unique
		$oD.Item($vElem)
	Next
	;
	; return the array of unique keys
	If BitAND($iFlags, 1) = 1 Then
		Local $aTemp = $oD.Keys()
		_ArrayInsert($aTemp, 0, $oD.Count)
		Return $aTemp
	Else
		Return $oD.Keys()
	EndIf
EndFunc   ;==>_ArrayUnique

RESULTS AutoIt 3.3.8.1:

>"C:\Program Files (x86)\AutoIt3\SciTE\AutoIt3Wrapper\AutoIt3Wrapper.exe" /run /prod /ErrorStdOut /in "L:\TOOLs\Macro\       TEST\TEST ArrayUnique2.au3" /UserParams    
+>18:41:28 Starting AutoIt3Wrapper v.2.1.3.2 SciTE v.3.3.7.0 ;  Keyboard:00000415  OS:WIN_7/Service Pack 1  CPU:X64 OS:X64    Environment(Language:0415  Keyboard:00000415  OS:WIN_7/Service Pack 1  CPU:X64 OS:X64)
>Running AU3Check (3.3.9.5)  from:C:\Program Files (x86)\AutoIt3
+>18:41:28 AU3Check ended.rc:0
>Running:(3.3.8.1):C:\Program Files (x86)\AutoIt3\autoit3.exe "L:\TOOLs\Macro\       TEST\TEST ArrayUnique2.au3"    
--> Press Ctrl+Alt+F5 to Restart or Ctrl+Break to Stop
TEST OLD: 62.1766151060786
TEST NEW: 7.80358215946651
+>18:41:28 AutoIt3.exe ended.rc:0
>Exit code: 0    Time: 1.169

RESULTS AutoIt 3.3.9.25:

>"C:\Program Files (x86)\AutoIt3\SciTE\AutoIt3Wrapper\AutoIt3Wrapper.exe" /run /beta /ErrorStdOut /in "L:\TOOLs\Macro\       TEST\TEST ArrayUnique2.au3" /UserParams    
+>18:41:58 Starting AutoIt3Wrapper v.2.1.3.2 SciTE v.3.3.7.0 ;  Keyboard:00000415  OS:WIN_7/Service Pack 1  CPU:X64 OS:X64    Environment(Language:0415  Keyboard:00000415  OS:WIN_7/Service Pack 1  CPU:X64 OS:X64)
>Running AU3Check (3.3.9.25)  from:C:\Program Files (x86)\AutoIt3\Beta
+>18:41:58 AU3Check ended.rc:0
>Running:(3.3.9.25):C:\Program Files (x86)\AutoIt3\Beta\autoit3.exe "L:\TOOLs\Macro\       TEST\TEST ArrayUnique2.au3"    
--> Press Ctrl+Alt+F5 to Restart or Ctrl+Break to Stop
TEST OLD: 3.76685532009558
TEST NEW: 3.74821554549098
+>18:41:58 AutoIt3.exe ended.rc:0
>Exit code: 0    Time: 0.962

and focus on:

_ArrayUnique($aTest) ; Initial test

now your statement:
"
Second thing, reverse the two tests and the results will be reversed with the new function being just as slow as the old function, slow being a relative term here.
"

is not applicable for this new script.

QUESTION: why initial _ArrayUnique is slower than each subsequent call.

comment:8 Changed 10 years ago by BrewManNH

You'll find that when doing speed comparisons on functions, you always have to try them in a different order each time or the results will make you see something that isn't real. This is an example of that, your 2 tests are identically written, yet the first one to run will always take a little bit longer than the second run of it.

Also, a difference of .02 ms is statistically insignificant because of the way AutoIt works.

Also, the comparison to 3.3.8.1 is invalid as that uses a very slow script that was replaced within the last few beta releases.

comment:9 Changed 10 years ago by Jos

  • Resolution set to Rejected
  • Status changed from new to closed

comment:10 Changed 10 years ago by mlipok

Thank you very much for your lecture.
For the future, I will try to first carry out appropriate tests.

Guidelines for posting comments:

  • You cannot re-open a ticket but you may still leave a comment if you have additional information to add.
  • In-depth discussions should take place on the forum.

For more information see the full version of the ticket guidelines here.

Add Comment

Modify Ticket

Action
as closed The ticket will remain with no owner.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.