_ArraySort sortiert "komisch"

    • Offizieller Beitrag

    Ich habe ein Problem mit _ArraySort. Und zwar mit einem 2D-Array. Ich möchte nach dem 2. Element sortieren lassen. Nun kann es aber sein, dass es mehrere gleiche Einträge gibt, die sich erst im dritten Element unterscheiden.
    Dann passiert es, dass die Sortierung mal so und mal so ausfällt, aber nicht so wie ich das haben will.
    Hier mal ein Beispiel:

    [autoit]


    #include <Array.au3>
    Dim $array[3][3] = [['abc', 'a11', '555'],['bcd', 'a11', '444'],['cde', 'a11', '666']]
    _ArraySort($array,0,0,0,1)
    _ArrayDisplay($array)
    _ArraySort($array,0,0,0,1)
    _ArrayDisplay($array)
    _ArraySort($array,0,0,0,1)
    _ArrayDisplay($array)
    _ArraySort($array,0,0,0,1)
    _ArrayDisplay($array)

    [/autoit]


    Ganz oben sollte die "444" stehen, dann die "555" und dann die "666".
    Gibt's eine Möglichkeit das hinzubekommen oder hat jemand eine Sortierfunktion, die das beherrscht?

  • Hi Oscar,

    hilft Dir vielleicht die Func von BugFix?
    http://www.autoit.de/index.php?page=Thread&postid=16574#post16574

    MfG Schnuffel

    "Sarkasmus ist die niedrigste Form des Witzes, aber die höchste Form der Intelligenz."
    Val McDermid

    ein paar Infos ...

    Wer mehr als "nur" Hilfe benötigt, kann sich gern im Forum "Programmieranfragen" an uns wenden. Wir helfen in allen Fällen, die die Forenregeln zulassen.

    Für schnelle Hilfe benötigen wir ein ! lauffähiges ! Script, dass wir als Demonstration des Problems testen können. Wer von uns erwartet ein Teilscript erstmal lauffähig zu bekommen, der hat
    1. keine wirkliche Not
    2. keinen Respekt vor Menschen die ihm in ihrer Freizeit Ihre Hilfe anbieten
    3. oder ist einfach nur faul und meint wir coden das für ihn

    In solchen Fällen erlaube ich mir, die Anfrage einfach zu ignorieren. ;)

  • Hallo Oscar,

    _ArraySort sortiert nur innerhalb des angebenen SubIndexes. Du könntest dies mit folgendem Workaround umgehen:

    Spoiler anzeigen
    [autoit]

    #include <array.au3>
    Dim $array[3][3] = [['abc', 'a11', '555'],['bcd', 'a11', '444'],['cde', 'a11', '666']]
    Dim $aNew[3][3]
    for $i = 0 to UBound($array,1) -1
    $aNew[$i][0] = $array[$i][1] & "|" & $array[$i][2] & "|" & $array[$i][0]
    ConsoleWrite($array[$i][1] & "|" & $array[$i][2] & "|" & $array[$i][0] & @CRLF)
    Next
    _ArraySort($aNew,0,0,0,0)
    _ArrayDisplay($aNew)
    for $i = 0 to 2
    $aSplit = StringSplit($aNew[$i][0],"|")
    $aNew[$i][0] = $aSplit[3]
    $aNew[$i][1] = $aSplit[1]
    $aNew[$i][2] = $aSplit[2]
    Next
    _ArrayDisplay($aNew)

    [/autoit]

    geht sehr warscheinlich bei großen Datenmengen auf Kosten der Performance,

    mfg (Auto)Bert

    • Offizieller Beitrag

    Schnuffel: die Funktion von BugFix wirft mehrere Fehlermeldungen aus:

    Code
    _ArraySort() called with wrong number of args.


    Wurde die Parameteranzahl mal gekürzt? Ich erinnere mich nicht mehr. Habe diese Funktion zu wenig genutzt. :S

    autoBert: Na, dann muss ich mir wohl eine eigene Sortierfunktion schreiben. Mal sehen, was sich da machen läßt...

  • Hallo @kleiner27,

    Ich habe ein Problem mit _ArraySort. Und zwar mit einem 2D-Array. Ich möchte nach dem 2. Element sortieren lassen. Nun kann es aber sein, dass es mehrere gleiche Einträge gibt, die sich erst im dritten Element unterscheiden.

    autoBert
    das geht auch mit

    [autoit]

    _ArraySort($array, 0, 0, 0, 2)

    [/autoit]

    Aber trotzdem wird 'abc' nicht richtig Sotiert!

    Das Array soll laut Vorgabe von Oscar zuerst nach der 2. Spalte und falls diese gleich danach nach der 3. Spalte sortiert werden. Dies geht mit dem normalen _ArraySort ja nicht (sonst hätte Oscar ja keinen Thread eröffnen müssen).
    Bei den Testdaten kann abc nicht an 1. Stelle stehen.
    Der Workaround die einzelnen Zeilen zusammenzuSTRINGen (in der Reihenfolge in der sortiert werden soll), danach zu sortieren um dann wieder jede Zeile zu splitten und in die entsprechende Spalte einzufügen funktioniert (sortiert sogar falls die 3. Spalte auch gleich danach noch nach der 1. Spalte).

    Oscar ich habe es jetzt einmal mit 9999 Zufallseinträgen probiert, die Zeit ist meines Erachtens nach akzeptabel. Hier das Testskript:

    Spoiler anzeigen
    [autoit]

    #include <array.au3>
    Dim $array[9999][3] ; = [['abc', 'a11', '555'],['bcd', 'a11', '444'],['cde', 'a11', '666']]
    Dim $aNew[9999][3]
    for $i = 0 to 9998
    $array[$i][0]=chr(Random(65,90,1)) & chr(Random(65,90,1)) & chr(Random(65,90,1))
    $array[$i][1]=chr(Random(65,90,1)) & Random(0,99,1)
    $array[$i][2]= Random(0,999,1)
    Next
    ;_ArrayDisplay($array)

    [/autoit] [autoit][/autoit] [autoit]

    for $i = 0 to UBound($array,1) -1
    $aNew[$i][0] = $array[$i][1] & "|" & $array[$i][2] & "|" & $array[$i][0]
    ;ConsoleWrite($array[$i][1] & "|" & $array[$i][2] & "|" & $array[$i][0] & @CRLF)
    Next
    _ArraySort($aNew,0,0,0,0)
    ;_ArrayDisplay($aNew)
    for $i = 0 to UBound($array,1) -1
    $aSplit = StringSplit($aNew[$i][0],"|")
    $aNew[$i][0] = $aSplit[3]
    $aNew[$i][1] = $aSplit[1]
    $aNew[$i][2] = $aSplit[2]
    Next
    _ArrayDisplay($aNew)

    [/autoit]

    mfg (Auto)Bert

  • Und so?

    [autoit]


    #include <Array.au3>
    Dim $array[3][3] = [['abc', 'a11', '555'],['bcd', 'a11', '444'],['cde', 'a11', '666']]
    _ArrayDisplay($array)
    _ArraySort($array,1,0,0,2)
    _ArraySort($array,0,0,0,1)
    _ArrayDisplay($array)

    [/autoit]

    Gruß,
    UEZ

    Edit: Führt auch nicht zum Ziel :thumbdown:

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

    2 Mal editiert, zuletzt von UEZ (24. Februar 2010 um 23:26)

  • Hi Leute!

    Habe ein Func geschrieben nenn sie _ArraySort2D()

    Soweit ohne Bug´s!

    [autoit]

    #include <Array.au3>

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Dim $Split, $Speicher, $TE
    Dim $Array[4][4] = [['abc', 'a11', '555', 'b'],['bcd', 'a11', '444', 'c'],['cde', 'a11', '666', 'd'],['fde', 'a11', '777', 'h']]

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    $TE = _ArraySort2D($Array)
    _ArrayDisplay($TE)

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func _ArraySort2D($Array)
    For $i = 0 To UBound($Array, 2) - 1
    $Speicher = ''
    For $e = 0 To UBound($Array) - 1
    $Speicher &= $Array[$e][$i] & '|'
    Next
    $Split = StringSplit($Speicher, '|')
    _ArraySort($Array, 0, 0, 0, 2)
    For $a = 1 To UBound($Split) - 2
    $Array[$a - 1][$i] = $Split[$a]
    Next
    Next
    Return $Array
    EndFunc ;==>_ArraySort2D

    [/autoit]


    Edit: doch ein Bug gefunden :(


    Jetzt aber ! ;)

    [autoit]

    #include <Array.au3>

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Dim $Split, $Speicher, $TE
    Dim $Array[4][4] = [['abc', 'a11', '555', 'b'],['xcd', 'a11', '444', 'c'],['cde', 'a11', '666', 'd'],['fde', 'a11', '777', 'h']]

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    $TE = _ArraySort2D($Array)
    _ArrayDisplay($TE)

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func _ArraySort2D($Array)
    For $i = 0 To UBound($Array, 2) - 1
    $Speicher = ''
    For $e = 0 To UBound($Array) - 1
    $Speicher &= $Array[$e][$i] & '|'
    Next
    $Split = StringSplit($Speicher, '|')
    _ArrayDelete($Split, 0)
    _ArrayDelete($Split, UBound($Split))
    _ArraySort($Split, 0, 0, 0, 2)
    For $a = 0 To UBound($Split) - 1
    $Array[$a][$i] = $Split[$a]
    Next
    Next
    Return $Array
    EndFunc ;==>_ArraySort2D

    [/autoit]

    LG Kleiner

    Einmal editiert, zuletzt von kleiner27 (24. Februar 2010 um 23:24)

  • Hier mein zweiter Versuch:

    [autoit]


    #include <array.au3>
    $max = 9999
    Dim $array[$max][3]
    for $i = 0 to $max - 1
    $array[$i][0]= Chr(Random(65,90,1)) & Chr(Random(65,90,1)) & Chr(Random(65,90,1))
    $array[$i][1]= Chr(Random(65,90,1)) & Random(0,99,1)
    $array[$i][2]= Random(0,999,1)
    Next

    [/autoit][autoit][/autoit][autoit]

    ;~ Dim $array[4][3] = [['AAB', 'S60', '127'], _
    ;~ ['AAA', 'Z27', '256'], _
    ;~ ['AAC', 'A11', '550'], _
    ;~ ['AAG', 'Z47', '406']]

    [/autoit][autoit][/autoit][autoit]

    $1ste_spalte = 1
    $2te_spalte = 2
    $j = 0
    $k = 1

    [/autoit][autoit][/autoit][autoit]

    $bench_start = TimerInit()

    [/autoit][autoit][/autoit][autoit]

    _ArraySort($array, 0, 0, 0, $1ste_spalte)

    [/autoit][autoit][/autoit][autoit]

    While $k < UBound($array)
    If $array[$j][$1ste_spalte] <> $array[$k][$1ste_spalte] Then
    If $k - $j > 1 Then
    _ArraySort($array, 0, $j, $k - 1, $2te_spalte)
    $j = $k
    Else
    $j = $k
    EndIf
    EndIf
    $k += 1
    WEnd

    [/autoit][autoit][/autoit][autoit]

    If $k - $j > 1 Then _ArraySort($array, 0, $j, $k, $2te_spalte)

    [/autoit][autoit][/autoit][autoit]

    $bench_end = Round(TimerDiff($bench_start), 2)
    ConsoleWrite("Laufzeit: " & $bench_end & " ms!" & @CRLF)

    [/autoit][autoit][/autoit][autoit]

    _ArrayDisplay($array)

    [/autoit]

    Meine Laufzeit: 2596.75 ms!

    Ich hoffe, dass der Code fehlerfrei ist :S
    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

    4 Mal editiert, zuletzt von UEZ (25. Februar 2010 um 01:29)

  • Hallo UEZ,

    ich habe einmal deine und meine Lösung gegenübergestellt: deine Laufzeit 7384 ms, meine 5741 ms. Eine anschliessende Überprüfung auf Übereinstimmung erbrachte nur Unterschiede in Spalte 1 von 3, was ja laut Vorgabe von Oscar zulässig ist.
    Hier das Skript dazu:

    Spoiler anzeigen
    [autoit]

    #include <array.au3>
    Dim $array[9999][3] ; = [['abc', 'a11', '555'],['bcd', 'a11', '444'],['cde', 'a11', '666']]
    Dim $aNew[9999][3]
    For $i = 0 To 9998
    $array[$i][0] = Chr(Random(65, 90, 1)) & Chr(Random(65, 90, 1)) & Chr(Random(65, 90, 1))
    $array[$i][1] = Chr(Random(65, 90, 1)) & Random(0, 99, 1)
    $array[$i][2] = Random(100, 999, 1)
    Next
    $bench_start = TimerInit()
    For $i = 0 To UBound($array, 1) - 1
    $aNew[$i][0] = $array[$i][1] & "|" & $array[$i][2] & "|" & $array[$i][0]
    ;ConsoleWrite($array[$i][1] & "|" & $array[$i][2] & "|" & $array[$i][0] & @CRLF)
    Next
    _ArraySort($aNew, 0, 0, 0, 0)
    For $i = 0 To UBound($array, 1) - 1
    $aSplit = StringSplit($aNew[$i][0], "|")
    $aNew[$i][0] = $aSplit[3]
    $aNew[$i][1] = $aSplit[1]
    $aNew[$i][2] = $aSplit[2]
    Next
    $bench_end = Round(TimerDiff($bench_start), 2)
    ConsoleWrite("Laufzeit: " & $bench_end & " ms!" & @CRLF)
    ;_ArrayDisplay($aNew)

    [/autoit] [autoit][/autoit] [autoit]

    $1ste_spalte = 1
    $2te_spalte = 2
    $j = 0
    $k = 1

    [/autoit] [autoit][/autoit] [autoit]

    $bench_start = TimerInit()
    _ArraySort($array, 0, 0, 0, $1ste_spalte)

    [/autoit] [autoit][/autoit] [autoit]

    While $k < UBound($array)
    If $array[$j][$1ste_spalte] <> $array[$k][$1ste_spalte] Then
    If $k - $j > 1 Then
    _ArraySort($array, 0, $j, $k - 1, $2te_spalte)
    $j = $k
    Else
    $j = $k
    EndIf
    EndIf
    $k += 1
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    If $k - $j > 1 Then _ArraySort($array, 0, $j, $k, $2te_spalte)

    [/autoit] [autoit][/autoit] [autoit]

    $bench_end = Round(TimerDiff($bench_start), 2)
    ConsoleWrite("Laufzeit: " & $bench_end & " ms!" & @CRLF & @crlf)
    ;_ArrayDisplay($array)

    [/autoit] [autoit][/autoit] [autoit]

    ConsoleWrite("Jetzt wird noch auf Übereinstimmung geprüft!" & @CRLF)

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To UBound($array, 1) - 1
    For $j = 0 To 2 ;alle 3 Spalten vergleichen
    If $aNew[$i][$j] <> $array[$i][$j] Then
    ConsoleWrite("Unterschied in ARRAYS Zeile: " & $i & " Spalte: " & $j & @CRLF)
    if $j <> 0 then ;Spalte 0 darf sich unterscheiden
    ConsoleWrite("Eine der beiden Sortiermethoden ist fehlerhaft" & @CRLF)
    Exit
    EndIf
    EndIf
    Next
    Next

    [/autoit]


    @kleiner27, deine Routine ist fehlerhaft (Unterschied in Spalte 2, schon in der 1. Zeile)

    mfg (Auto)Bert

    • Offizieller Beitrag

    Mehrstufig sortieren kannst du über einen Umweg mit SQL: https://autoit.de/index.php?page…46336#post46336

    Diese Funktion führt zum Erfolg! :thumbup:
    Darf ich die in einem Projekt (will noch nichts verraten, aber wenn's fertig ist, werde ich es hier veröffentlichen) benutzen?

    Danke auch an alle anderen für die zahlreichen Lösungsansätze, aber BugFixs Funktion lässt sich gut in das Projekt integrieren. :thumbup:

  • Ich hatte eine Idee und die habe ich versucht zu implementieren 8)

    Nur was mir bei dir aufgefallen ist, dass deine Sortierung nicht ganz stimmt:

    autoit.de/wcf/attachment/7897/

    In Zeile 2 ist die 85 nach 590 und 710! Anscheinend wird "nur" nach der ersten Ziffer links sortiert ;)

    Gruß,
    UEZ

  • Hallo UEZ,

    da ich die Standardsortierung mit _Arraysort verwende ist dies das leidige Problem, dass Zahlen wie Strings einsortiert werden. Ich habe auf Grund deines Postes in Zeile 7 meines Testskriptes jetzt Zufallszahlen von 1 - 999. Dadurch fällt der Unterschied unserer beiden Sortierroutinen auf. Deine behandelt Zahlen als solche und ist daher als besser einzustufen. Ich könnte jetzt zwar noch meine Routine umbauen indem ich die Zahlen rechtsbündig formatiere (von links mit entsprechenden Leerstellen auffülle) aber da sich Oscar für die SQL-Version entschieden hat, dürfte das unnötig sein.

    mfg (Auto)Bert

    • Offizieller Beitrag

    Ich habe mich noch an einer Bubble-Sort-Funktion versucht. Die Lösung ist ja eigentlich sehr simpel, aber so habe ich mir das vorgestellt.
    Vielleicht nicht die schnellste Lösung, aber für diese Aufgabe reicht's.

    Spoiler anzeigen
    [autoit]


    #include <Array.au3>
    Dim $array[3][3] = [['abc', 'a11', '555'],['bcd', 'a11', '444'],['cde', 'a11', '666']]
    _Array2DSort($array)
    _ArrayDisplay($array)

    [/autoit] [autoit][/autoit] [autoit]

    Func _Array2DSort(ByRef $aSortArray)
    For $i = 0 To UBound($aSortArray) - 2
    For $j = $i + 1 To UBound($aSortArray) - 1
    If $aSortArray[$i][1] > $aSortArray[$j][1] Then _Array2DSwap($aSortArray, $i, $j)
    If $aSortArray[$i][1] = $aSortArray[$j][1] Then
    If $aSortArray[$i][2] > $aSortArray[$j][2] Then _Array2DSwap($aSortArray, $i, $j)
    EndIf
    Next
    Next
    EndFunc ;==>_Array2DSort

    [/autoit] [autoit][/autoit] [autoit]

    Func _Array2DSwap(ByRef $aSwap, $a, $b)
    Local $sTmp
    For $i = 0 To UBound($aSwap, 2) - 1
    $sTmp = $aSwap[$a][$i]
    $aSwap[$a][$i] = $aSwap[$b][$i]
    $aSwap[$b][$i] = $sTmp
    Next
    EndFunc ;==>_Array2DSwap

    [/autoit]


    Warum bin ich da nicht schon gestern drauf gekommen... :huh:

  • Nun ja für eine handvoll ist die Laufzeit -> O(n^2) ja noch akzeptable! Aber versuche es doch mal mit den 9999 Daten :D

    Intern wird ja Quicksort benutzt, das im schlechtesten Fall auch quadratische Laufzeit hat, wenn man nicht irgendwelche Tricks benutzt!

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • joa das is ne variante, wenn ich alles richtig gemacht hab isses n QuickSort.

    der Fall das zwei Werte gleich sind wird nicht berücksichtigt, aber das braucht man ja auch nicht.
    man muss doch nur tauschen wenn der nächste größer ist ansonsten bleiben die beiden ja eh nebeneinander ;)