Jump to content
Pilot123

Group array indexes, with the same values

Recommended Posts

Pilot123

Hi,

 

In this 1-dimensional array, I have two 'groups' of array indexes, with the value "red" in it.

#include <Array.au3>

Local $aArray[10] = [1, 2, "red", "red", "red", 6, 7, "red", "red", 10] ; Create a 1-dimensional array that contains duplicate values.
_ArrayDisplay($aArray, "$aArray Initial") ; Display the current array.

Group 1

Index2= red
Index3= red
Index4= red

 

Group 2

Index7= red
Index8= red

 

Could someone please help me with a script that will test the array for a group/groups with the specific value "red", and then display a message box for example:

Group 1 starts with index2, and ends with index4

Group 2 starts with index7, and ends with index8

Thanks,

Edited by Pilot123

Share this post


Link to post
Share on other sites
Luigi

Maybe this?

#include <Array.au3>

Local $aArray[10] = [1, 2, "red", "red", "red", 6, 7, "red", "red", 10]
;~                  [0, 1, 2      3,     4,     5, 6, 7,     8,     9 ]
;~ Local $aArray[1] = [1]

Local $oGroup = group($aArray)

ConsoleWrite("number of groups[ " & $oGroup.Count & " ]" & @LF)
For $each In $oGroup
    ConsoleWrite("$each[ " & $each & " ] ids[ " & $oGroup.Item($each) & " ]" & @LF)
Next


Func group($aInput)
    Local $oRet = ObjCreate("Scripting.Dictionary")
    Local $iStart
    Local $aRet[1]

    If Not IsArray($aInput) Or UBound($aInput, 1) <= 1 Then Return $oRet

    For $ii = 1 To UBound($aInput, 1) - 1
        If $aInput[$ii] = $aInput[$ii - 1] Then
            If $iStart Then
                _ArrayAdd($aRet, $ii)
            Else
                $iStart = $ii - 1
                _ArrayAdd($aRet, $ii - 1)
                _ArrayAdd($aRet, $ii)
            EndIf
        Else
            If $iStart Then
                $oRet.Add($oRet.Count + 1, _ArrayToString($aRet, "|", 1))
                Local $aRet[1]
                $iStart = 0
            EndIf
        EndIf
    Next
    Return $oRet
EndFunc   ;==>group

 


Visit my repository

Share this post


Link to post
Share on other sites
mikell

Without using cpu consuming _Array* funcs  :)

#include <Array.au3>  ; for display only  

Local $aArray[14] = ["red", "red", 1, 2, "red", "red", "red", 6, 7, "red", "red", 10, "red", "red"]
_ArrayDisplay($aArray, "$aArray Initial") ; Display the current array.

$announce = _group($aArray, "red")
$nb = StringReplace($announce, @crlf, @crlf)
Msgbox(0, @extended & "  groups", $announce)

Func _group($array, $txt)
    Local $a, $u = UBound($array), $res[$u][2], $n, $k = 0, $announce = ""
    For $i = 0 to $u-1
       If $array[$i] = $txt and $n = 0 Then 
           $n = 1
           $res[$k][0] = $i
       EndIf
       If ($array[$i] <> $txt or  $i = $u-1) and $n = 1 Then 
           $res[$k][1] = ($array[$i] = $txt and $i = $u-1) ? $i : $i-1
           $n = 0
           $k += 1
       EndIf
    Next
    ; Redim $res[$k][2]  ; can be useful if you plan to return the array
   ; _ArrayDisplay($res)
    For $i = 0 to $k-1
         $announce &= "Group" & $i+1 & " starts with " & $res[$i][0] & ", and ends with " & $res[$i][1] & @crlf
    Next
    Return $announce
EndFunc

 

Edited by mikell

Share this post


Link to post
Share on other sites
Pilot123

Thank you very much mikell, this is exactly what I am looking for. :)

I will study your code and implement it in my project code.

Share this post


Link to post
Share on other sites
Chimp

Without using cpu consuming _Array* funcs  :)

:blink:

try with this array

Local $aArray[14] = [0, 0, 1, 2, 0, 0, 0, 6, 7, 0, 0, 10, 0, 0]

 

p.s.

it fails also with @Luigi function


small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Share this post


Link to post
Share on other sites
Luigi

Hum... @Chimp is a bugfinder! Greate! ^^

Try this:

#include <Array.au3>

;~ Local $aArray[10] = [1, 2, "red", "red", "red", 6, 7, "red", "red", 10]
Local $aArray[14] = [0, 0, 1, 2, 0, 0, 0, 6, 7, 0, 0, 9, 0, 0]
;~                  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13]
;~                  [0, 1, 2      3,     4,     5, 6, 7,     8,     9 ]
;~ Local $aArray[1] = [1]

Local $oGroup = group($aArray)

ConsoleWrite("number of groups[ " & $oGroup.Count & " ]" & @LF)
For $each In $oGroup
    ConsoleWrite("$each[ " & $each & " ] ids[ " & $oGroup.Item($each) & " ]" & @LF)
Next


Func group($aInput)
    Local $oRet = ObjCreate("Scripting.Dictionary")
    Local $iStart
    Local $aRet[1]

    If Not IsArray($aInput) Or UBound($aInput, 1) <= 1 Then Return $oRet

    For $ii = 1 To UBound($aInput, 1) - 1
        If ($aInput[$ii]) = ($aInput[$ii - 1]) Then
            If $iStart Then
                _ArrayAdd($aRet, $ii)
            Else
                $iStart = $ii - 1
                _ArrayAdd($aRet, $ii - 1)
                _ArrayAdd($aRet, $ii)
            EndIf
        ElseIf UBound($aRet, 1) > 1 Then
            $oRet.Add($oRet.Count + 1, _ArrayToString($aRet, "|", 1))
            Local $aRet[1]
            $iStart = 0
        EndIf
    Next
    If $aInput[UBound($aInput, 1) - 1] = $aInput[UBound($aInput, 1) - 2] Then $oRet.Add($oRet.Count + 1, _ArrayToString($aRet, "|", 1))
    Return $oRet
EndFunc   ;==>group

 

Edited by Luigi

Visit my repository

Share this post


Link to post
Share on other sites
mikell

Chimp, what doesn't work for you ?
I tried with your array and got no error and correct results

The only thing I just see now is that a confusion may occur between a string and the number 0

Share this post


Link to post
Share on other sites
Pilot123

Hi again,

One last question. How can I adapt @mikell example code so that each index number of the $aArray that is written to the message box , is also matched with a second  $bArray index,and those values passed to variables, so that I can use them in my other code. Let's say the possible groups in my $aArray will not exceed 4.

My second additional array I want to add:

Local $bArray[14] = ["Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November"]

This is the output of the message box from @mikell's code:

Group1 starts with 0, and ends with 1  
Group2 starts with 4, and ends with 6
Group3 starts with 9, and ends with 10
Group4 starts with 12, and ends with 13

So, the value 0 in the message box above , equals the stored location with the index0, of $aArray

and, the value 1 equals the stored location with the index1, of $aArray

and, the value 4 equals the stored location with the index4, of $aArray, etc..

 

What I need is that when, the value 0 (representing index0) is written to the message box, it also goes to the second array $bArray and goes to its index0, and reads the element stored in that location and writes it to the variable $a = "Alpha", for example.

The variables will then look like this:

$a = "Alpha"
$b = "Bravo"
$c = "Echo"
$d = "Golf"
$e = "Juliet"
$f = "Kilo"
$g = "Mike"
$h = "November

 

If a group only consists of one element, for example; 

Group3 starts with 9, and ends with 9

Then the variables for group 3 will be:

$e = "Juliet"
$f = "Juliet"

If would appreciate if someone can help me with this please

Share this post


Link to post
Share on other sites
mikell

For this you have to return the array from the func and then apply the result to $bArray (which is not a nice name in terms of hungarian notation, should be i.e. $aArray1 and $aArray2)

#include <Array.au3> 

Local $aArray1[14] = ["red", "red", 1, 2, "red", "red", "red", 6, 7, "red", 9, 10, "red", "red"]
Local $aArray2[14] = ["Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November"]
;_ArrayDisplay($aArray, "$aArray Initial")

$aGr = _group($aArray1, "red")
 _ArrayDisplay($aGr)

Local $aNew[UBound($aGr)*2]
For $i = 0 to UBound($aGr)*2-2 step 2
   $aNew[$i] = $aArray2[$aGr[$i/2][0] ]
   $aNew[$i+1] = $aArray2[$aGr[$i/2][1] ]
Next
 _ArrayDisplay($aNew)

Func _group($array, $txt)
    Local $u = UBound($array), $aRes[$u][2], $n, $k 
    For $i = 0 to $u-1
       If $array[$i] = $txt and $n = 0 Then 
           $n = 1
           $aRes[$k][0] = $i
       EndIf
       If ($array[$i] <> $txt or  $i = $u-1) and $n = 1 Then 
           $aRes[$k][1] = ($array[$i] = $txt and $i = $u-1) ? $i : $i-1
           $n = 0
           $k += 1
       EndIf
    Next
    Redim $aRes[$k][2]  
    Return $aRes 
EndFunc

Then you can use the elements of the new array as variables (or assign them to variables)

Edit
The added code part could easily be included in the func using the 2nd array as a parameter

Edited by mikell

Share this post


Link to post
Share on other sites
Pilot123

Thanks you very much Mikell, again looks like exactly what I need.:)

As a newbie, I still need a lot of practice!!

I'll study your example code again.

Thanks,

Share this post


Link to post
Share on other sites
Chimp

@mikell
your function is nearly ok, but if you have an input array like that one I've posted above, your function will consider any presence of the number 0 like if it is the searched word, even if it's not. maybe if you change the comparison statement
If $array[$i] = $txt and $n = 0 Then
by using a double == instead of just one =, then the problem do not arise.
If $array[$i] == $txt and $n = 0 Then

@Luigi, Your function finds if there are 2 or more consecutive elements with the same value in the array. Any word is ok, not just the wanted word ("red" in this case).
One more bug is that if the wanted word appears one or more times in the array but none of them is alongside another, the function simply do not consider that word even if should be considered instead.

Edited by Chimp

small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Share this post


Link to post
Share on other sites
mikell

Chimp,
You're right, forcing string comparison is the solution but I'll use String() rather than ==  (because == forces case sensitivity)

Share this post


Link to post
Share on other sites
Chimp

Hi again,

One last question. How can I adapt @mikell example code so that each index number of the $aArray that is written to the message box , is also matched with a second  $bArray index,and those values passed to variables, so that I can use them in my other code. Let's say the possible groups in my $aArray will not exceed 4.

My second additional array I want to add:

Local $bArray[14] = ["Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November"]

This is the output of the message box from @mikell's code:

Group1 starts with 0, and ends with 1  
Group2 starts with 4, and ends with 6
Group3 starts with 9, and ends with 10
Group4 starts with 12, and ends with 13

So, the value 0 in the message box above , equals the stored location with the index0, of $aArray

and, the value 1 equals the stored location with the index1, of $aArray

and, the value 4 equals the stored location with the index4, of $aArray, etc..

 

What I need is that when, the value 0 (representing index0) is written to the message box, it also goes to the second array $bArray and goes to its index0, and reads the element stored in that location and writes it to the variable $a = "Alpha", for example.

The variables will then look like this:

$a = "Alpha"
$b = "Bravo"
$c = "Echo"
$d = "Golf"
$e = "Juliet"
$f = "Kilo"
$g = "Mike"
$h = "November

 

If a group only consists of one element, for example; 

Group3 starts with 9, and ends with 9

Then the variables for group 3 will be:

$e = "Juliet"
$f = "Juliet"

If would appreciate if someone can help me with this please

Hi Pilot123

Not sure what's your exact goal and if you already got your wanted answer, anyway I think that create a new variable for each value of starting and ending index of the found groups, is not the right way, and is not even a need.
indeed, since you already have the $bArray with all needed values, you can simply read directly those values using the indexes from array of found groups.
Of course the nr. of elements in the first array $aArray must be less or equal  to the nr of elements of the second array $bArray otherwise you can get the "subscript dimension range exceeded" error

maybe an example is better than many words (expecially from a not native English speakers like me)
hope it can help you

here one further way to go:

#include <Array.au3>
; Input arrays
Local $aArray[14] = ["red", "red", 1, 2, "red", "red", "red", 6, 7, "red", "red", 10, "red", "red"]
; Array with lookup values
Local $bArray[14] = ["Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November"]

Local $aResult = _FindGroups($aArray, "red", 0)
If Not @error Then
    _ArrayDisplay($aResult, @extended & " groups found")
Else
    MsgBox(0, "Error", "no groups found")
EndIf

; now use indexes found in the $aResult array as indexes to point to values in your second array $bArray
; (we just use indexes from the first array to get the wanted data from the second array)
Local $aMatches[UBound($aResult)][4] ; create another array with same number of rows as $aResult to hold first array and corresponding matches from the second array
if IsArray($aResult) Then
    for $i = 0 to UBound($aResult) - 1
        $aMatches[$i][0] = $aResult[$i][0] ; clone data from $aResult
        $aMatches[$i][1] = $aResult[$i][1]
        $aMatches[$i][2] = $bArray[$aResult[$i][0]] ; Get values from $bArray according to indexes from the $aResult array
        $aMatches[$i][3] = $bArray[$aResult[$i][1]]
    Next
    _ArrayDisplay($aMatches, "Correspondences with second array")
EndIf

; --------------------------------------------------------------------------
; General purpose function to find how a value is groupped within an 1D array
; $aArray   : the input array
; $sTarget  : the value to search string or value (i.e. 0 is <> from "0")
; $iCase    : Case sensitive search 0=no 1=yes
;
; returns   : on success:
;             a 2D array with as many rows as many groups are found
;             [n][0] and [n][1] index of first [0] and last [1] position of the n group
;             @extended contains the number of rows (groups) found
;             on error:
;             returns -1 and set @error to 1
; -------------------------------------------------------------------------

Func _FindGroups(ByRef $aArray, $sTarget, $iCase = 0, $iCompare = 2)
    Local $Targets = _ArrayFindAll($aArray, $sTarget, 0, 0, $iCase, $iCompare)
    If IsArray($Targets) Then
        _ArraySort($Targets, 1)
        Local $i = UBound($Targets)
        Local $aStack[$i + 1][2] ; make room for all possible indexes (only some of them will be usually used)
        $i -= 1 ; start parsing from last element back to first
        _Push($aStack, $Targets[$i])
        While $i
            If $Targets[$i] = $Targets[$i - 1] - 1 Then
                ; do nothin and continue parsing
            Else
                _Push($aStack, $Targets[$i])
                _Push($aStack, $Targets[$i - 1])
            EndIf
            $i -= 1
        WEnd
        _Push($aStack, $Targets[$i])
        Return SetError(0, $aStack[0][0], _ArrayExtract($aStack, 1, $aStack[0][0]))
    EndIf
    Return SetError(1, 0, -1)
EndFunc   ;==>_FindGroups

Func _Push(ByRef $aStack, $Value) ; 2D stack for internal use
    $aStack[0][1] = Not $aStack[0][1]
    $aStack[0][0] += $aStack[0][1]
    $aStack[$aStack[0][0]][Not $aStack[0][1]] = $Value
EndFunc   ;==>_Push
Edited by Chimp

small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Share this post


Link to post
Share on other sites
Pilot123
#include <Array.au3>
; Input arrays
Local $aArray[14] = ["red", "red", 1, 2, "red", "red", "red", 6, 7, "red", "red", 10, "red", "red"]
; Array with lookup values
Local $bArray[14] = ["Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November"]

Local $aResult = _FindGroups($aArray, "red", 0)
If Not @error Then
    _ArrayDisplay($aResult, @extended & " groups found")
Else
    MsgBox(0, "Error", "no groups found")
EndIf

; now use indexes found in the $aResult array as indexes to point to values in your second array $bArray
; (we just use indexes from the first array to get the wanted data from the second array)
Local $aMatches[UBound($aResult)][4] ; create another array with same number of rows as $aResult to hold first array and corresponding matches from the second array
if IsArray($aResult) Then
    for $i = 0 to UBound($aResult) - 1
        $aMatches[$i][0] = $aResult[$i][0] ; clone data from $aResult
        $aMatches[$i][1] = $aResult[$i][1]
        $aMatches[$i][2] = $bArray[$aResult[$i][0]] ; Get values from $bArray according to indexes from the $aResult array
        $aMatches[$i][3] = $bArray[$aResult[$i][1]]
    Next
    _ArrayDisplay($aMatches, "Correspondences with second array")
EndIf

; --------------------------------------------------------------------------
; General purpose function to find how a value is groupped within an 1D array
; $aArray   : the input array
; $sTarget  : the value to search string or value (i.e. 0 is <> from "0")
; $iCase    : Case sensitive search 0=no 1=yes
;
; returns   : on success:
;             a 2D array with as many rows as many groups are found
;             [n][0] and [n][1] index of first [0] and last [1] position of the n group
;             @extended contains the number of rows (groups) found
;             on error:
;             returns -1 and set @error to 1
; -------------------------------------------------------------------------

Func _FindGroups(ByRef $aArray, $sTarget, $iCase = 0, $iCompare = 2)
    Local $Targets = _ArrayFindAll($aArray, $sTarget, 0, 0, $iCase, $iCompare)
    If IsArray($Targets) Then
        _ArraySort($Targets, 1)
        Local $i = UBound($Targets)
        Local $aStack[$i + 1][2] ; make room for all possible indexes (only some of them will be usually used)
        $i -= 1 ; start parsing from last element back to first
        _Push($aStack, $Targets[$i])
        While $i
            If $Targets[$i] = $Targets[$i - 1] - 1 Then
                ; do nothin and continue parsing
            Else
                _Push($aStack, $Targets[$i])
                _Push($aStack, $Targets[$i - 1])
            EndIf
            $i -= 1
        WEnd
        _Push($aStack, $Targets[$i])
        Return SetError(0, $aStack[0][0], _ArrayExtract($aStack, 1, $aStack[0][0]))
    EndIf
    Return SetError(1, 0, -1)
EndFunc   ;==>_FindGroups

Func _Push(ByRef $aStack, $Value) ; 2D stack for internal use
    $aStack[0][1] = Not $aStack[0][1]
    $aStack[0][0] += $aStack[0][1]
    $aStack[$aStack[0][0]][Not $aStack[0][1]] = $Value
EndFunc   ;==>_Push

Hi Chimp,

Thanks.

I included your code in my project. Working great so far.

Edited by Pilot123

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

×