Jump to content

_ArrayUniqueR


czardas
 Share

Recommended Posts

This seems currently a hot topic. Anyway I have been thinking of trying out this idea for a while. This is in experimental stage and uses an old idea of declaring uniquely named variables followed by testing for their existence. I always thought it was an interesting approach. You can set the second parameter to case sensitive. R stands for Row. It returns either a one dimensional array or a two dimensional array of unique rows. Three datatypes are ignored: namely nested arrays, objects and DLLstructs. If someone knows a way to test these items quickly for uniqueness, or represent the data as binary then please enlighten me. Rows containing these items are simply just left in place. I'm not sure how efficient or what limitations this code has. Testing begins in the morning - oh, it already is morning. Meanwhile you might like to have a play with it.

; #FUNCTION# ===================================================================================================================
; Name...........: _ArrayUniqueR
; Description ...: Removes duplicate items (or rows) from a one (or two) dimensional array.
; Syntax.........: _ArrayUniqueR($aArray [, $bCaseSense = False])
; Parameters.....; $aArray - The array containing duplicate data to be removed.
; Return values .: Success - Returns the unique array [ByRef].
;                  $bCaseSense - [Optional] Set to true for case sensitive matches. Default value = False
;                  Failure sets @error as follows
;                  |@error = 1 1st parameter is not an array.
;                  |@error = 2 1st parameter has too many dimensions.
; Author ........: czardas
; Comments ......; Works with strings, numbers, binary, boolean values, pointers, functions and keywords (Null and Default).
;                  Integers of the same magnitude are treated as duplicates regardless of data type.
;                  All elements within a row must be duplicated (in the same positions) before removal takes place.
;                  This function does not remove rows containing objects, DLLStructs or other arrays.
;                  If you modify _ArrayUniqueR, avoid using variable names that could collide with internally generated names.
; ==============================================================================================================================

Func _ArrayUniqueR(ByRef $aArray, $bCaseSense = Default)
    If Not IsArray($aArray) Then Return SetError(1) ; not an array

    Local $iDim = UBound($aArray, 0)
    If $iDim > 2 Then Return SetError(2) ; wrong number of dimensions

    Local $iRows = UBound($aArray)
    If $iRows < 2 Then Return ; array is already unique

    If $bCaseSense = Default Then $bCaseSense = False

    Local $iCols = ($iDim = 1) ? 1 : UBound($aArray, 2), _
    $iItems = 0, $aFunction[1] = [0], $vElement, $iInt, $sName

    ; for each item, or row, generate unique names using word characters only
    For $i = 0 To $iRows -1
        $sName = '' ; clear previous name
        For $j = 0 To $iCols -1
            $vElement = ($iDim = 1) ? $aArray[$i] : $aArray[$i][$j] ; get the data contained in each element

            ; use non-hexadecimal characters (other than x) as datatype identifiers and convert the data to hexadecimal where necessary
            Switch VarGetType($vElement)
                Case 'String'
                    If Not $bCaseSense Then $vElement = StringUpper($vElement) ; generates a case insensitive name segment
                    $sName &= 's' & StringToBinary($vElement, 4) ; UTF8 [$SB_UTF8]
                Case 'Int32', 'Int64' ; use decimal without conversion
                    ; the minus sign of a negative integer is replaced with 'g': to distinguish it from the positive value
                    $sName &= ($vElement < 0) ? 'g' & StringTrimLeft($vElement, 1) : 'i' & $vElement
                Case 'Double' ; may be an integer
                    $iInt = Int($vElement)
                    $sName &= ($vElement = $iInt) ? (($iInt < 0) ? 'g' & StringTrimLeft($iInt, 1) : 'i' & $iInt) : 'h' & Hex($vElement)
                Case 'Bool' ; True or False
                    $sName &= ($vElement = True) ? 't' : 'v'
                Case 'Binary'
                    $sName &= 'y' & $vElement
                Case 'Ptr'
                    $sName &= 'p' & $vElement
                Case 'Keyword' ; Default or Null (other variable declarations are illegal)
                    $sName &= ($vElement = Default) ? 'w' : 'n'
                Case 'Function', 'UserFunction' ; string conversion will fail
                    For $k = 1 To $aFunction[0] ; unique functions are stored in a separate array
                        If $vElement = $aFunction[$k] Then ; this function has been encountered previously
                            $sName &= 'u' & $k
                            ContinueLoop 2
                        EndIf
                    Next
                    $aFunction[0] += 1
                    If $aFunction[0] > UBound($aFunction) -1 Then ReDim $aFunction[$aFunction[0] + 10]
                    $aFunction[$aFunction[0]] = $vElement
                    $sName &= 'u' & $aFunction[0]
                Case Else ; Array, Object or DLLStruct
                    $sName = False ; set to ignore
                    ExitLoop
            EndSwitch
        Next

        If $sName Then
            If IsDeclared($sName) = -1 Then ContinueLoop ; [$DECLARED_LOCAL] this row has been seen previously
            Assign($sName, "", 1) ; [else] declare each unique row as a new variable name
        EndIf

        ; overwrite the next row (assumes that the first duplicate will be found quite quickly)
        Switch $iDim
            Case 1
                $aArray[$iItems] = $aArray[$i]
            Case 2
                For $j = 0 To $iCols -1
                    $aArray[$iItems][$j] = $aArray[$i][$j]
                Next
        EndSwitch

        $iItems += 1
    Next

    Switch $iDim
        Case 1
            ReDim $aArray[$iItems]
        Case 2
            ReDim $aArray[$iItems][$iCols]
    EndSwitch
EndFunc ;==> _ArrayUniqueR

Example:

#include <Array.au3>

Local $aOddMix[14] = [Null, Null, True, True, False, False, Default, Default, Binary('0x0001'), Binary('0x0001'), Ptr(0x0001), Ptr(0x0001), MsgBox, MsgBox]
_ArrayDisplay($aOddMix)
$aUnique = _ArrayUniqueR($aOddMix)
_ArrayDisplay($aOddMix)

 

Edited by czardas
Link to comment
Share on other sites

I don't see any logical reason to be using Assign and IsDeclared in that code.

UDF List:

 
_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_ArrayFilter/_ArrayReduce_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 22/04/2018

Link to comment
Share on other sites

I first thought this might compare objects, it does not, but it rules it out and shines a bit of light on my dopeyness a bit:)

$obj1 = ObjCreate("scripting.dictionary")
If Not IsObj($obj1) Then Exit MsgBox(0,0,1)

$obj2 = ObjCreate("scripting.dictionary")
If Not IsObj($obj2) Then Exit MsgBox(0,0,2)
$obj2.Add("Key1", "Value1")

If $obj1 = $obj2 Then MsgBox(0,0,3)

MsgBox(0,0, _ObjCompare($obj1, $obj2))

Func _ObjCompare(ByRef $o1, ByRef $o2)
    if ObjName($o1, 1) <> ObjName($o2, 1) Then Return False
    if ObjName($o1, 2) <> ObjName($o2, 2) Then Return False
    if ObjName($o1, 3) <> ObjName($o2, 3) Then Return False
    if ObjName($o1, 4) <> ObjName($o2, 4) Then Return False
    if ObjName($o1, 5) <> ObjName($o2, 5) Then Return False
    if ObjName($o1, 6) <> ObjName($o2, 6) Then Return False
    if ObjName($o1, 7) <> ObjName($o2, 7) Then Return False
    Return True
EndFunc

 

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

How would you do it?

Why do you need it when the variable already exists?

UDF List:

 
_AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_ArrayFilter/_ArrayReduce_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_DotNet_Load()/_DotNet_Unload()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrFileReadLastChars()GeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()Int_Parse() & Int_TryParse()IsISBN()LockFile()Mapping CtrlIDsOOP in AutoItParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandProtect GlobalsQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...

Updated: 22/04/2018

Link to comment
Share on other sites

This part

If $sName Then
    If IsDeclared($sName) = -1 Then ContinueLoop
    Assign($sName, "", 1)
EndIf

If $sName was not declared, would the script not fail with "Variable not declared" error?

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

This part

If $sName Then
    If IsDeclared($sName) = -1 Then ContinueLoop
    Assign($sName, "", 1)
EndIf

If $sName was not declared, would the script not fail with "Variable not declared" error?

JohnOne, he's not checking if the $sName is or isn't declared. He's checking if the value held by $sName is declared.

♡♡♡

.

eMyvnE

Link to comment
Share on other sites

The first version of this function completely failed the integer test I ran just now. I was too tired yesterday to try everything. The Hex conversion method I was using was completely doomed to failure. The following remark (about Hex) in the help file still has me slightly baffled.

Omitting the second parameter (length) or using the keyword Default results in automatic sizing of the output; 8-characters display for numbers that can fit in that and 16-characters display for others.

Even if it worked the way I had expected, there was still a serious flaw in my logic. Two's compliment (and the potential collision range 0x80000000 - 0xFFFFFFFF) which prevents stripping out leading zeros, makes comparison of the Hex unsuitable. So how to solve this problem using minimal processing? Update in the first post - now works with all types of integers.

Testing Integers

#include <Array.au3>

Local $aIntegers[2000000]
For $i = 0 To    1999999
    $aIntegers[$i] = Number(Random(-500, 500, 1), Random(1, 3, 1)) ; Int32, Int64 or Double [randomly cast]
Next

Local $iTimer = TimerInit()
_ArrayUniqueR($aIntegers) ; returns (~) 1001 unique integers in approx 22 seconds using 2GB RAM
ConsoleWrite(TimerDiff($iTimer) & @LF)
_ArrayDisplay($aIntegers)

As many as 3003 integers (of different types) may be generated (and duplicated) in the code above, but only 1001 unique values can ever appear in the returned array - because approximately 67% of the two million randomly produced numbers differ only in type, and not in magnitude. I'll document this function after I've had more time to play around with it.

Edited by czardas
Link to comment
Share on other sites

I have added documentation and commented the code. I also removed some superfluous syntax which was only there in case it might be needed. Hopefully it will be easier to understand now. The method used is slower than the current version of _ArrayUnique() but it is still much faster than earlier versions of _ArrayUnique(). It also behaves differently in several ways.

There are limitations to using this approach. I estimate that it can handle about 30 MB of unique string data. This estimate may vary on more powerful computers - yet to be tested. With a mixture of data types, the true size may also vary significantly. The more duplicates there are in an array, the more elements it can handle. In plain English - this function is good for hundreds of thousands of rows of medium length, a couple of million numbers, or a couple of hundred unique short stories. It's perfect for something like small business accounts stored in tables - if you don't mind waiting a few seconds.

Some of the (collision avoidance) solutions presented in the code could potentially be used in other situations. Careful consideration is needed for all possible collision scenarios when implementing methods of this nature - perhaps interesting from a design perspective.

Edited by czardas
Link to comment
Share on other sites

  • 2 months later...

The above was intended to test a concept and act as an intermediary online backup: posted here in case it might also be of interest to you. Until I created _PreDim() - formerly called _ArrayForceDim() and found here: https://www.autoitscript.com/forum/topic/179817-_arrayforcedim/ - I was not sure how to proceed further. It seems I've been having a bit of naming crisis recently: describing what some functions do is tricky enough without having to do it in one or two camel-cased words. :think:

This new function, '_UniqueRegion', works for arrays of up to five dimensions; and uniqueness can be applied to any available dimension. The notes in the code offer further explanation. Additional functions are planned which employ the same kind of logic. Some multi-loop processes may be exported to helper functions: to be shared with other functions yet to be written.

Optimization can wait. You may want to consider where latency occurs in this script and see if you spot the same thing that I did (look inside the loops). The difference may not be so great, although I think the unoptimized script is possibly a little easier to understand, so I am posting it as it is for now.

; #FUNCTION# ===================================================================================================================
; Name...........: _UniqueRegion
; Description ...: Removes duplicate items from an array (or duplicate regions from a multidimensional array).
; Syntax.........: _UniqueRegion($aArray [, $bCaseSense = False [, $iDimension = 1]])
; Parameters.....; $aArray - The array containing duplicate regions to be removed.
;                  $bCaseSense - [Optional] Set to true for case sensitive matches. Default value = False
;                  $iDimension - [Optional] Integer value - the dimension to which uniqueness applies. Default = 1st dimension
; Return values .: Success - Returns the unique array [ByRef].
;                  Failure sets @error as follows:
;                  |@error = 1 The 1st parameter is not an array.
;                  |@error = 2 The 1st parameter has too many dimensions.
;                  |@error = 3 The 3rd parameter is not a valid dimension.
; Author ........: czardas
; Comments ......; This function is limited to (medium sized) arrays of up to five dimensions.
;                  Unique array regions are defined as follows:
;                  1. unique items within a one dimensional array
;                  2. unique item collections within a two dimensional array (rows or columns)
;                  3. unique tables within a three dimensional array
;                  4. unique cuboidal areas within a four dimension array
;                  5. unique four dimensional cuboids within a five dimensional array
;                  All elements within a region must be duplicated (in juxtaposed positions) before removal takes place.
;                  Works with strings, numbers, binary, boolean values, pointers, functions and keywords (Null and Default).
;                  Integers of the same magnitude are treated as duplicates regardless of data type.
;                  This function does not remove regions containing objects, DLLStructs or other arrays.
;                  If you modify _UniqueRegion, avoid using variable names that could collide with internally generated names.
; Example .......; _UniqueRegion($aArray, Default, 2) ; [$iDimension = 2] deletes duplicate COLUMNS from a 2D array [concept ^^]
; ==============================================================================================================================

Func _UniqueRegion(ByRef $aArray, $bCaseSense = Default, $iDimension = 1)
    If Not IsArray($aArray) Then Return SetError(1) ; not an array

    Local $iDim = UBound($aArray, 0) ; get the number of dimensions
    If $iDim > 5 Then Return SetError(2) ; wrong number of dimensions

    $iDimension = ($iDimension = Default) ? 1 : Int($iDimension)
    If $iDimension < 1 Or $iDimension > $iDim Then Return SetError(3) ; [or override ambiguous input with $iDimension = 1]

    Local $aBound[6]
    For $i = 1 To $iDim
        $aBound[$i] = UBound($aArray, $i) ; get the size of each dimension
    Next
    If $aBound[$iDimension] < 2 Then Return ; the array is already unique

    For $i = $iDim + 1 To 5
        $aBound[$i] = 1 ; set the upper bounds of dimensions to be added
    Next
    _Predim($aArray, 5) ; adding more dimensions introduces some latency - too bad because this reduces complexity!

    Local $sExpression = '$aArray' ; to access elements at their original indeces
    For $i = 1 To 5
        If $i <> $iDimension Then
            $sExpression &= '[$' & $i & ']' ; default expression = '$aArray[$iNext][$2][$3][$4][$5]'
        Else
            $sExpression &= '[$iNext]'
        EndIf
    Next

    $aBound[0] = $aBound[$iDimension] ; the first loop will run to the bounds of the specified dimension
    $aBound[$iDimension] = 1 ; whichever loop this relates to must only run once on each encounter

    Local $iItems = 0, $aFunction[1] = [0], $vElement, $iInt, $sName
    If $bCaseSense = Default Then $bCaseSense = False

    For $iNext = 0 To $aBound[0] -1
        $sName = '' ; clear previous name

        For $5 = 0 To $aBound[5] -1
            For $4 = 0 To $aBound[4] -1
                For $3 = 0 To $aBound[3] -1
                    For $2 = 0 To $aBound[2] -1
                        For $1 = 0 To $aBound[1] -1
                            $vElement = Execute($sExpression) ; get the data contained in each element

                            ; use non-hexadecimal characters (other than x) as datatype identifiers and convert the data to hexadecimal where necessary
                            Switch VarGetType($vElement)
                                Case 'String'
                                    If Not $bCaseSense Then $vElement = StringUpper($vElement) ; generates a case insensitive name segment
                                    $sName &= 's' & StringToBinary($vElement, 4) ; UTF8 [$SB_UTF8]
                                Case 'Int32', 'Int64' ; use decimal without conversion
                                    ; the minus sign of a negative integer is replaced with 'g': to distinguish it from the positive value
                                    $sName &= ($vElement < 0) ? 'g' & StringTrimLeft($vElement, 1) : 'i' & $vElement
                                Case 'Double' ; may be an integer
                                    $iInt = Int($vElement)
                                    $sName &= ($vElement = $iInt) ? (($iInt < 0) ? 'g' & StringTrimLeft($iInt, 1) : 'i' & $iInt) : 'h' & Hex($vElement)
                                Case 'Bool' ; True or False
                                    $sName &= ($vElement = True) ? 't' : 'v'
                                Case 'Binary'
                                    $sName &= 'y' & $vElement
                                Case 'Ptr'
                                    $sName &= 'p' & $vElement
                                Case 'Keyword' ; Default or Null (other variable declarations are illegal)
                                    $sName &= ($vElement = Default) ? 'w' : 'n'
                                Case 'Function', 'UserFunction' ; string conversion will fail
                                    For $k = 1 To $aFunction[0] ; unique functions are stored in a separate array
                                        If $vElement = $aFunction[$k] Then ; this function has been encountered previously
                                            $sName &= 'u' & $k
                                            ContinueLoop 2
                                        EndIf
                                    Next
                                    $aFunction[0] += 1
                                    If $aFunction[0] > UBound($aFunction) -1 Then ReDim $aFunction[$aFunction[0] + 10]
                                    $aFunction[$aFunction[0]] = $vElement
                                    $sName &= 'u' & $aFunction[0]
                                Case Else ; Array, Object or DLLStruct
                                    $sName = False ; set to ignore
                                    ExitLoop 5
                            EndSwitch
                        Next
                    Next
                Next
            Next
        Next

        If $sName Then
            If IsDeclared($sName) = -1 Then ContinueLoop ; [$DECLARED_LOCAL] this region has been seen previously
            Assign($sName, "", 1) ; [else] declare each unique region as a new variable name
        EndIf

        ; overwrite the next region (assumes that the first duplicate region will be found quite quickly)
        For $5 = 0 To $aBound[5] -1
            For $4 = 0 To $aBound[4] -1
                For $3 = 0 To $aBound[3] -1
                    For $2 = 0 To $aBound[2] -1
                        For $1 = 0 To $aBound[1] -1
                            Switch $iDimension
                                Case 1
                                    $aArray[$iItems][$2][$3][$4][$5] = $aArray[$iNext][$2][$3][$4][$5]
                                Case 2
                                    $aArray[$1][$iItems][$3][$4][$5] = $aArray[$1][$iNext][$3][$4][$5]
                                Case 3
                                    $aArray[$1][$2][$iItems][$4][$5] = $aArray[$1][$2][$iNext][$4][$5]
                                Case 4
                                    $aArray[$1][$2][$3][$iItems][$5] = $aArray[$1][$2][$3][$iNext][$5]
                                Case 5
                                    $aArray[$1][$2][$3][$4][$iItems] = $aArray[$1][$2][$3][$4][$iNext]
                            EndSwitch
                        Next
                    Next
                Next
            Next
        Next

        $iItems += 1
    Next

    _Predim($aArray, $iDim) ; remove the extra dimensions
    $aBound[$iDimension] = $iItems ; reset the bounds

    Switch $iDim ; remove the remaining duplicate array regions
        Case 1
            ReDim $aArray [$aBound[1]]
        Case 2
            ReDim $aArray [$aBound[1]] [$aBound[2]]
        Case 3
            ReDim $aArray [$aBound[1]] [$aBound[2]] [$aBound[3]]
        Case 4
            ReDim $aArray [$aBound[1]] [$aBound[2]] [$aBound[3]] [$aBound[4]]
        Case 5
            ReDim $aArray [$aBound[1]] [$aBound[2]] [$aBound[3]] [$aBound[4]] [$aBound[5]]
    EndSwitch

EndFunc ;==> _UniqueRegion


; #FUNCTION# ===================================================================================================================
; Name...........: _Predim
; Description ...: Changes the size of an array by adding, or removing, dimensions.
; Syntax.........: _Predim($aArray, $iDimensions [, $iPush = False])
; Parameters.....; $aArray - The original array.
;                  $iDimensions - The number of dimensions in the returned array.
;                  $bPush - [Optional] If set to True, new dimensions are created on, or removed from, the left [see comments].
; Return values .: Returns the modified array ByRef.
;                  Failure sets @error as follows:
;                  |@error = 1 The first parameter is not an array.
;                  |@error = 2 The requested array has more than 7 dimensions.
;                  |@error = 3 The original array has more than 7 dimensions.
; Author.........: czardas
; Comments ......; This function works for up to 7 dimensions.
;                  By default, new dimensions are added to the right in a standard sequence: $aArray[7][6] ==> $aArray[7][6][1]
;                  Dimensions are removed in reverse sequence: $aArray[7][6] ==> $aArray[7]
;                  When the $bPush parameter is set to True, the original array will be pushed to higher dimensions:
;                  $aArray[7][6] ==> $aArray[1][7][6], or the process reversed: $aArray[7][6] ==> $aArray[6]
; ==============================================================================================================================

Func _Predim(ByRef $aArray, $iDimensions, $bPush = False)
    If Not IsArray($aArray) Then Return SetError(1)

    $iDimensions = Int($iDimensions)
    If $iDimensions < 1 Or $iDimensions > 7 Then Return SetError(2)

    Local $iPreDims = UBound($aArray, 0) ; current number of dimensions
    If $iPreDims = $iDimensions Then Return ; no change
    If $iPreDims > 7 Then Return SetError(3) ; too many dimensions

    ; get the size of each original dimension
    Local $a[$iPreDims +1]
    For $i = 1 To $iPreDims
        $a[$i] = UBound($aArray, $i)
    Next

    Local $sElement = '[$1][$2][$3][$4][$5][$6][$7]' ; array syntax used to access the original array elements

    If $bPush Then ; prefix dimensions, or delete from the left
        Local $iOffset = Abs($iDimensions - $iPreDims)
        If $iPreDims > $iDimensions Then
            For $i = 1 To $iDimensions ; shift elements to lower indices
                $a[$i] = $a[$i + $iOffset]
            Next
            $sElement = '$aArray' & StringLeft('[0][0][0][0][0][0]', $iOffset * 3) & StringLeft($sElement, $iDimensions * 4)

        Else
            ReDim $a [$iDimensions +1] ; make space for more dimensions
            For $i = $iDimensions To $iOffset +1 Step -1 ; shift elements to higher indices
                $a[$i] = $a[$i - $iOffset]
            Next
            For $i = 1 To $iOffset ; assign the size of each additional dimension [1][1][1]... etc...
                $a[$i] = 1
            Next

            $sElement = '$aArray' & StringMid($sElement, 1 + $iOffset * 4, $iPredims * 4)
        EndIf

    Else ; Default behaviour = append dimensions, or delete from the right
        ReDim $a [$iDimensions +1] ; modify the number of dimensions

        For $i = $iPredims + 1 To $iDimensions ; assign the size of each new dimension ...[1][1][1] etc...
            $a[$i] = 1
        Next
        $sElement = '$aArray' & StringLeft($sElement, $iPredims * 4)

        Local $2 = 0, $3 = 0, $4 = 0, $5 = 0, $6 = 0, $7 = 0 ; required to reference new higher dimensions
    EndIf

    ; add or remove dimensions
    Switch $iDimensions
        Case 1
            Local $aNewArray[$a[1]]
            For $1 = 0 To $a[1] -1
                $aNewArray[$1] = Execute($sElement)
            Next

        Case 2
            Local $aNewArray[$a[1]][$a[2]]
            For $1 = 0 To $a[1] -1
                For $2 = 0 To $a[2] -1
                    $aNewArray[$1][$2] = Execute($sElement)
                Next
            Next

        Case 3
            Local $aNewArray[$a[1]][$a[2]][$a[3]]
            For $1 = 0 To $a[1] -1
                For $2 = 0 To $a[2] -1
                    For $3 = 0 To $a[3] -1
                        $aNewArray[$1][$2][$3] = Execute($sElement)
                    Next
                Next
            Next

        Case 4
            Local $aNewArray[$a[1]][$a[2]][$a[3]][$a[4]]
            For $1 = 0 To $a[1] -1
                For $2 = 0 To $a[2] -1
                    For $3 = 0 To $a[3] -1
                        For $4 = 0 To $a[4] -1
                            $aNewArray[$1][$2][$3][$4] = Execute($sElement)
                        Next
                    Next
                Next
            Next

        Case 5
            Local $aNewArray[$a[1]][$a[2]][$a[3]][$a[4]][$a[5]]
            For $1 = 0 To $a[1] -1
                For $2 = 0 To $a[2] -1
                    For $3 = 0 To $a[3] -1
                        For $4 = 0 To $a[4] -1
                            For $5 = 0 To $a[5] -1
                                $aNewArray[$1][$2][$3][$4][$5] = Execute($sElement)
                            Next
                        Next
                    Next
                Next
            Next

        Case 6
            Local $aNewArray[$a[1]][$a[2]][$a[3]][$a[4]][$a[5]][$a[6]]
            For $1 = 0 To $a[1] -1
                For $2 = 0 To $a[2] -1
                    For $3 = 0 To $a[3] -1
                        For $4 = 0 To $a[4] -1
                            For $5 = 0 To $a[5] -1
                                For $6 = 0 To $a[6] -1
                                    $aNewArray[$1][$2][$3][$4][$5][$6] = Execute($sElement)
                                Next
                            Next
                        Next
                    Next
                Next
            Next

        Case 7
            Local $aNewArray[$a[1]][$a[2]][$a[3]][$a[4]][$a[5]][$a[6]][$a[7]]
            For $1 = 0 To $a[1] -1
                For $2 = 0 To $a[2] -1
                    For $3 = 0 To $a[3] -1
                        For $4 = 0 To $a[4] -1
                            For $5 = 0 To $a[5] -1
                                For $6 = 0 To $a[6] -1
                                    For $7 = 0 To $a[7] -1
                                        $aNewArray[$1][$2][$3][$4][$5][$6][$7] = Execute($sElement)
                                    Next
                                Next
                            Next
                        Next
                    Next
                Next
            Next
    EndSwitch

    $aArray = $aNewArray
EndFunc ;==> _Predim

So far only one example (deleting duplicate columns from a table). Note the difference when you omit the 3rd parameter.

#include <Array.au3> ; For _ArrayDisplay()
Local $aTest = [[1,2,2,1],[1,2,2,1]]
_ArrayDisplay($aTest)

_UniqueRegion($aTest, Default, 2) ; remove duplicate regions encountered within the second dimension
_ArrayDisplay($aTest)

 

Edited by czardas
Link to comment
Share on other sites

  • 8 months later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...