Sign in to follow this  
Followers 0
furuseth

_arraysort and eliminate duplicates

14 posts in this topic

Hallo, i want to make a GUI with an inputfield to fill with different values separated by komma. I found this CodeSnippet here in this forum but it doesn't work and it does not eliminate the duplicates:

Local $arString=StringSplit("19,28,3,17,7",",")
;sorting the array
For $i=2 To UBound($arString)-1
    If Int($arString[$i-1])>Int($arString[$i]) Then _ArraySwap($arString[$i-1],$arString[$i])
Next

Thx furuseth

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

Use _ArrayUnique to get an array that only contains unique elements. I'm not sure but I think the array has to be sorted in advance.

As an example:

#include <array.au3>
Global $aSource[7] = [1,2,4,3,5,3,6]
Global $aTarget[7]

_ArraySort($aSource)
$aTarget = _ArrayUnique($aSource)
_ArrayDisplay($aTarget)
Edited by water

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

furuseth,

Welcome to the AutoIt forum. :graduated:

One way to do it:

#include <GUIConstantsEx.au3>
#include <Array.au3>

$hGUI = GUICreate("Test", 500, 500)

$hInput = GUICtrlCreateInput("19,28,3,17,7", 10, 10, 400, 20)

$hButton = GUICtrlCreateButton("Go!", 10, 50, 80, 30)

GUISetState()

While 1

    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit
        Case $hButton
            _Sort_Input()
    EndSwitch

WEnd

Func _Sort_Input()

    ; Read the input
    $sInputString = GUICtrlRead($hInput)
    ; Convert to an array and get unique elements
    $aOutArray = _ArrayUnique(StringSplit($sInputString, ","), 1, 1)
    ; Convert to numbers
    For $i = 1 To $aOutArray[0]
        $aOutArray[$i] = Number($aOutArray[$i])
    Next
    ; Sort the elements
     _ArraySort($aOutArray, 0, 1)
    ; Display array
    _ArrayDisplay($aOutArray)

EndFunc

Note that you have to convert the elements to numbers before sorting - otherwise you get an ASCII not a numeric sort. :D

Please ask if you have any questions. :(

M23


Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind._______My UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Share this post


Link to post
Share on other sites

Wow, thank you. They are working like a charme. The one of Melba23was exactly the snippet I need. One more question, how to get a sorted output string? For Example if the input was 19,28,3,17,7 the result should be 1 variable containing the sorted numbers: 3,7,17,19,28

Sorry for my stupid questions, I'm really newbie.

Thx furuseth

@Melba23 I'm honored; read so many of your threads in this forum.

Share this post


Link to post
Share on other sites

#5 ·  Posted (edited)

One more question, how to get a sorted output string?

$sString = _ArrayToString($aOutArray, ",", 1) Edited by water

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2017-04-18 - Version 1.4.8.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX (NEW 2017-02-27 - Version 1.3.1.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2015-04-01 - Version 0.4.0.0) - Download - General Help & Support - Example Scripts
Excel - Example Scripts - Wiki
Word - Wiki
PowerPoint (2015-06-06 - Version 0.0.5.0) - Download - General Help & Support

Tutorials:
ADO - Wiki

 

Share this post


Link to post
Share on other sites

furuseth,

Change the end of the function to read: :graduated:

; Sort the elements
_ArraySort($aOutArray, 0, 1)
; Return it to string format
$sOutputString = _ArrayToString($aOutArray, ",", 1)
; Display it
MsgBox(0, "Result", $sOutputString)

M23


Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind._______My UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

It seems to be that _ArrayUnique() has a bug and it is very slow:

#include <Array.au3>
;~ Dim $aNames[10][2] = [["Anton", ""], ["Berta", 15]]
Dim $aNames[10] = ["Antonia", "Anton", "Cäsar", "Dora", "Emil", "Friedrich", "Gustav", "Heinrich", "Ida", "Julius"]

Dim $aUnique[10000]
For $I = 0 To Ubound($aUnique) - 1
    $r = Random(0, 9, 1)
    $aUnique[$I] = $aNames[$r]
Next

;Standard _ArrayUnique()
$ts = TimerInit()
$test = _ArrayUnique($aUnique)
$te = TimerDiff($ts)
ConsoleWrite(Round($te, 2) & " ms." & @CRLF)
_ArrayDisplay($test)

$ts = TimerInit()
$test = ArrayUnique($aUnique)
$te = TimerDiff($ts)
ConsoleWrite(Round($te, 2) & " ms." & @CRLF)
_ArrayDisplay($test)


Dim $aNames[10][2] = [["Antonia", ""], ["Anton", ""], ["Cäsar", 300], ["Dora", 24], ["Emil", 33], ["Friedrich", 57], ["Gustav", 53], ["Heinrich", 34], ["Ida", 13], ["Julius", 77]]
Dim $aUnique[100000][2]
For $I = 0 To Ubound($aUnique) - 1
    $r = Random(0, 9, 1)
    $aUnique[$I][0] = $aNames[$r][0]
    $aUnique[$I][1] = $aNames[$r][1]
Next

$ts = TimerInit()
$test = ArrayUnique($aUnique)
$te = TimerDiff($ts)
ConsoleWrite(Round($te, 2) & " ms." & @CRLF)
_ArrayDisplay($test)


Exit



; #FUNCTION# ============================================================================
; Name.............:    ArrayUnique
; Description ...:  Returns the Unique Elements of a 1-dimensional or 2-dimensional array.
; Syntax...........:    _ArrayUnique($aArray[, $iBase = 0, oBase = 0])
; Parameters ...:   $aArray - The Array to use
;                           $iBase  - [optional] Is the input Array 0-base or 1-base index.  0-base by default
;                           $oBase  - [optional] Is the output Array 0-base or 1-base index.  0-base by default
; Return values:    Success - Returns a 1-dimensional or 2-dimensional array containing only the unique elements
;                           Failure - Returns 0 and Sets @Error:
;                           0 - No error.
;                           1 - Returns 0 if parameter is not an array.
;                           2 - Array has more than 2 dimensions
;                           3 - Array is already unique
;                           4 - when source array is selected as one base but UBound(array) - 1 <> array[0] / array[0][0]
;                           5 - Scripting.Dictionary cannot be created for 1D array unique code
; Author .........:     UEZ 2010 for 2D-array, Yashied for 1D-array (modified by UEZ)
; Version ........:     0.96 Build 2010-11-20 Beta
; =======================================================================================
Func ArrayUnique($aArray, $iBase = 0, $oBase = 0)
    If Not IsArray($aArray) Then Return SetError(1, 0, 0) ;not an array
    If UBound($aArray, 0) > 2 Then Return SetError(2, 0, 0) ;array is greater than a 2D array
    If UBound($aArray) = $iBase + 1 Then Return SetError(3, 0, $aArray) ;array is already unique because of only 1 element
    Local $dim = UBound($aArray, 2), $i
    If $dim Then ;2D array
        If $iBase And UBound($aArray) - 1 <> $aArray[0][0] Then Return SetError(4, 0, 0)
        Local $oD = ObjCreate('Scripting.Dictionary')
        If @error Then Return SetError(5, 0, 0)
        Local $i, $j, $k = $oBase, $l, $s, $aTmp, $flag, $sSep = Chr(01)
        Local $aUnique[UBound($aArray)][$dim]
        If Not $oBase Then $flag = 2
        For $i =  $iBase To UBound($aArray) - 1
            For $j = 0 To $dim - 1
                $s &= $aArray[$i][$j] & $sSep
            Next
            If Not $oD.Exists($s) And StringLen($s) > 3 Then
                $oD.Add($s, $i)
                $aTmp = StringSplit(StringTrimRight($s, 1), $sSep, 2)
                For $l = 0 To $dim - 1
                    $aUnique[$k][$l] = $aTmp[$l]
                Next
                $k += 1
            EndIf
            $s = ""
        Next
        $oD.RemoveAll
        $oD = ""
        If $k > 0 Then
            If $oBase Then $aUnique[0][0] = $k - 1
            ReDim $aUnique[$k][$dim]
        Else
            ReDim $aUnique[1][$dim]
        EndIf
    Else ;1D array
        If $iBase And UBound($aArray) - 1 <> $aArray[0] Then Return SetError(4, 0, 0)
        Local $sData = '', $sSep = ChrW(160), $flag
        For $i = $iBase To UBound($aArray) - 1
            If Not IsDeclared($aArray[$i] & '$') Then
                Assign($aArray[$i] & '$', 0, 1)
                $sData &= $aArray[$i] & $sSep
            EndIf
        Next
        If Not $oBase Then $flag = 2
        Local $aUnique = StringSplit(StringTrimRight($sData, 1), $sSep, $flag)
    EndIf
    Return SetError(0, 0, $aUnique)EndFunc   ;==>ArrayUnique

It might be that my version has a bug, too! :graduated:

Br,

UEZ

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

This might be a way:

#include <Array.au3>

Dim $aNumbers[10] = [19, 22, 3, 9, 27, 22, 19, 99, 88, 3]
$aUnique = ArrayUnique($aNumbers)
_ArrayDisplay($aUnique)
_ArraySort($aUnique) ;standard sort not natural! Look here for natural order sorting: http://www.autoitscript.com/forum/topic/83626-natural-order-string-comparison/page__p__598311
_ArrayDisplay($aUnique)

Exit



; #FUNCTION# ============================================================================
; Name.............:    ArrayUnique
; Description ...:  Returns the Unique Elements of a 1-dimensional or 2-dimensional array.
; Syntax...........:    _ArrayUnique($aArray[, $iBase = 0, oBase = 0])
; Parameters ...:   $aArray - The Array to use
;                           $iBase  - [optional] Is the input Array 0-base or 1-base index.  0-base by default
;                           $oBase  - [optional] Is the output Array 0-base or 1-base index.  0-base by default
; Return values:    Success - Returns a 1-dimensional or 2-dimensional array containing only the unique elements
;                           Failure - Returns 0 and Sets @Error:
;                           0 - No error.
;                           1 - Returns 0 if parameter is not an array.
;                           2 - Array has more than 2 dimensions
;                           3 - Array is already unique
;                           4 - when source array is selected as one base but UBound(array) - 1 <> array[0] / array[0][0]
;                           5 - Scripting.Dictionary cannot be created for 1D array unique code
; Author .........:     UEZ 2010 for 2D-array, Yashied for 1D-array (modified by UEZ)
; Version ........:     0.96 Build 2010-11-20 Beta
; =======================================================================================
Func ArrayUnique($aArray, $iBase = 0, $oBase = 0)
    If Not IsArray($aArray) Then Return SetError(1, 0, 0) ;not an array
    If UBound($aArray, 0) > 2 Then Return SetError(2, 0, 0) ;array is greater than a 2D array
    If UBound($aArray) = $iBase + 1 Then Return SetError(3, 0, $aArray) ;array is already unique because of only 1 element
    Local $dim = UBound($aArray, 2), $i
    If $dim Then ;2D array
        If $iBase And UBound($aArray) - 1 <> $aArray[0][0] Then Return SetError(4, 0, 0)
        Local $oD = ObjCreate('Scripting.Dictionary')
        If @error Then Return SetError(5, 0, 0)
        Local $i, $j, $k = $oBase, $l, $s, $aTmp, $flag, $sSep = Chr(01)
        Local $aUnique[UBound($aArray)][$dim]
        If Not $oBase Then $flag = 2
        For $i =  $iBase To UBound($aArray) - 1
            For $j = 0 To $dim - 1
                $s &= $aArray[$i][$j] & $sSep
            Next
            If Not $oD.Exists($s) And StringLen($s) > 3 Then
                $oD.Add($s, $i)
                $aTmp = StringSplit(StringTrimRight($s, 1), $sSep, 2)
                For $l = 0 To $dim - 1
                    $aUnique[$k][$l] = $aTmp[$l]
                Next
                $k += 1
            EndIf
            $s = ""
        Next
        $oD.RemoveAll
        $oD = ""
        If $k > 0 Then
            If $oBase Then $aUnique[0][0] = $k - 1
            ReDim $aUnique[$k][$dim]
        Else
            ReDim $aUnique[1][$dim]
        EndIf
    Else ;1D array
        If $iBase And UBound($aArray) - 1 <> $aArray[0] Then Return SetError(4, 0, 0)
        Local $sData = '', $sSep = ChrW(160), $flag
        For $i = $iBase To UBound($aArray) - 1
            If Not IsDeclared($aArray[$i] & '$') Then
                Assign($aArray[$i] & '$', 0, 1)
                $sData &= $aArray[$i] & $sSep
            EndIf
        Next
        If Not $oBase Then $flag = 2
        Local $aUnique = StringSplit(StringTrimRight($sData, 1), $sSep, $flag)
    EndIf
    Return SetError(0, 0, $aUnique)EndFunc   ;==>ArrayUnique

Br,

UEZ

PS: where is the post between post#7 and post#8?

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites

[it might be that my version has a bug, too! :whistle:

Br,

UEZ

A very minor contribution on your faster version...

Shouldn't:

If UBound($aArray) + $iBase = $iBase + 1 Then Return SetError(3, 0, $aArray) ;array is already unique

be:

If UBound($aArray) = $iBase + 1 Then Return SetError(3, 0, $aArray) ;array only has one element

Share this post


Link to post
Share on other sites

@Spiff59: indeed, it was a logical error. Posted Image

I corrected it!

I'm happy that somebody tested it!

Thank you very much Spiff59!

Br,

UEZ


Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

I'm happy that somebody tested it!

Br,

UEZ

You're welcome.

Just an idea, but considering that the Scripting.Dictionary is so blazing fast, couldn't it be implemented for your 2D routine as well? Like:

Func ArrayUnique2($aArray, $iBase = 0, $oBase = 0, $iCase = 0)
    If Not IsArray($aArray) Then Return SetError(1, 0, 0) ;not an array
    If UBound($aArray, 0) > 2 Then Return SetError(2, 0, 0) ;array is greater than a 2D array
    Local $oD = ObjCreate('Scripting.Dictionary')
    If @error Then Return SetError(4, 0, 0)
    Local $dim2 = UBound($aArray, 2), $i
    If $dim2 Then ;2D array
        Local $dim1 = UBound($aArray, 1), $aUnique[$dim1][$dim2], $j
        For $i = 0 to $dim1 - 1
            If Not $oD.Exists($aArray[$i][0]) Then
                $oD.Add($aArray[$i][0], 0)
                $aUnique[$j][0] = $aArray[$i][0]
                $aUnique[$j][1] = $aArray[$i][1]
                $j += 1
            EndIf
        Next
        ReDim $aUnique[$j][2]
    Else ;1D array
        For $i In $aArray
            If Not $oD.Exists($i) Then $oD.Add($i, 0)
        Next
        Local $aUnique = $oD.Keys()
    EndIf
    $oD.RemoveAll
    $oD = ""
    Return SetError(0, 0, $aUnique)
EndFunc

(I didn't add any of the handling for $IBase and $OBase, that were already missing from the 1D section).

Edit: Pardon me, OP, for potential thread-jacking :graduated:

Edited by Spiff59

Share this post


Link to post
Share on other sites

#12 ·  Posted (edited)

You're welcome.

Just an idea, but considering that the Scripting.Dictionary is so blazing fast, couldn't it be implemented for your 2D routine as well? Like:

Func ArrayUnique2($aArray, $iBase = 0, $oBase = 0, $iCase = 0)
    If Not IsArray($aArray) Then Return SetError(1, 0, 0) ;not an array
    If UBound($aArray, 0) > 2 Then Return SetError(2, 0, 0) ;array is greater than a 2D array
    Local $oD = ObjCreate('Scripting.Dictionary')
    If @error Then Return SetError(4, 0, 0)
    Local $dim2 = UBound($aArray, 2), $i
    If $dim2 Then ;2D array
        Local $dim1 = UBound($aArray, 1), $aUnique[$dim1][$dim2], $j
        For $i = 0 to $dim1 - 1
            If Not $oD.Exists($aArray[$i][0]) Then
                $oD.Add($aArray[$i][0], 0)
                $aUnique[$j][0] = $aArray[$i][0]
                $aUnique[$j][1] = $aArray[$i][1]
                $j += 1
            EndIf
        Next
        ReDim $aUnique[$j][2]
    Else ;1D array
        For $i In $aArray
            If Not $oD.Exists($i) Then $oD.Add($i, 0)
        Next
        Local $aUnique = $oD.Keys()
    EndIf
    $oD.RemoveAll
    $oD = ""
    Return SetError(0, 0, $aUnique)
EndFunc

(I didn't add any of the handling for $IBase and $OBase, that were already missing from the 1D section).

Edit: Pardon me, OP, for potential thread-jacking :graduated:

Well, I updated my version with some more checks but after having it bug free I wanted to implement the 2D part also with Scripting.Dictionary because I started first with 2D arrays and found the Scripting.Dictionary method afterwards which is very fast. I mixed up both versions as a start but you did already my next step! Posted Image

I will do some more checks with your version and then let's see... Posted Image

Thanks again Spiff59!

Br,

UEZ

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites

#13 ·  Posted (edited)

@UEZ

You do not need to use COM, simply Assign()...

#Include <Array.au3>

Global $aUnique

Dim $aNames[10] = ['Antonia', 'Anton', 'Casar', 'Dora', 'Emil', 'Friedrich', 'Gustav', 'Heinrich', 'Ida', 'Julius']

Dim $aArray[10000]
For $i = 0 To UBound($aArray) - 1
    $aArray[$i] = $aNames[Random(0, 9, 1)]
Next

_ArrayUniqueFast($aArray, $aUnique)
_ArrayDisplay($aUnique)

Func _ArrayUniqueFast(Const ByRef $aArray, ByRef $aUnique)

    Local $sData = '', $sSep = ChrW(160)

    For $i = 0 To UBound($aArray) - 1
        If Not IsDeclared($aArray[$i] & '$') Then
            Assign($aArray[$i] & '$', 0, 1)
            $sData &= $aArray[$i] & $sSep
        EndIf
    Next
    $aUnique = StringSplit(StringTrimRight($sData, 1), $sSep)
EndFunc   ;==>_ArrayUniqueFast
Edited by Yashied

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

Great idea Yashied! Posted Image

I thought that COM is the fastest for 1D array but you have beaten it to the factor ~3!

Well done! Posted Image

I updated my codes above.

Br,

UEZ

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites

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
Sign in to follow this  
Followers 0