Jump to content
Sign in to follow this  
supersonic

Remove empty rows in 2D array...

Recommended Posts

supersonic

Hi!

Is there already a build-in function to remove empty rows in a 2D array?

_ArrayUnique wouldn't help, because it's only for 1D arrays.

Sure, i could write an UDF like this:

#include <Array.au3>

Dim $aTmp[5][5] = [[0, 1], [2, 3], ["", ""], ["", ""], [4, 5]]

_ArrayDisplay($aTmp, "$aTmp_BEFORE")
_Array2DDeleteEmptyRows($aTmp)
_ArrayDisplay($aTmp, "$aTmp_AFTER")

Func _Array2DDeleteEmptyRows(ByRef $iArray)
    Local $vEmpty = True
    For $i = UBound($iArray, 2) - 1 To 0 Step -1
        For $j = 0 To UBound($iArray, 1) - 1 Step 1
            If StringCompare($iArray[$i][$j], "", 0) <> 0 Then
                $vEmpty = False
            EndIf
        Next
        If $vEmpty = True Then _ArrayDelete($iArray, $i)
        $vEmpty = True
    Next
EndFunc

But when targeting rather huge arrays (> 1000) this UDF will be slow.

Any ideas?

Greets,

-supersonic.

Edited by supersonic

Share this post


Link to post
Share on other sites
supersonic

I updated the script; maybe someone could produce fast code?

Hi!

Is there already a build-in function to remove empty rows in a 2D array?

_ArrayUnique wouldn't help, because it's only for 1D arrays.

Sure, i could write an UDF like this:

#include <Array.au3>

Dim $aTmp[5][5] = [[0, 1], [2, 3], ["", ""], ["", ""], [4, 5]]

_ArrayDisplay($aTmp, "$aTmp_BEFORE")
_Array2DDeleteEmptyRows($aTmp)
_ArrayDisplay($aTmp, "$aTmp_AFTER")

Func _Array2DDeleteEmptyRows(ByRef $iArray)
    Local $vEmpty = True
    For $i = UBound($iArray, 2) - 1 To 0 Step -1
        For $j = 0 To UBound($iArray, 1) - 1 Step 1
            If StringCompare($iArray[$i][$j], "", 0) <> 0 Then
                $vEmpty = False
            EndIf
        Next
        If $vEmpty = True Then _ArrayDelete($iArray, $i)
        $vEmpty = True
    Next
EndFunc

But when targeting rather huge arrays (> 1000) this UDF will be slow.

Any ideas?

Greets,

-supersonic.

Share this post


Link to post
Share on other sites
weaponx

Why are using StringCompare instead of just checking if the element equals "" ?

Share this post


Link to post
Share on other sites
supersonic

Why are using StringCompare instead of just checking if the element equals "" ?

You're right, I've changed the UDF:

Func _Array2DDeleteEmptyRows(ByRef $iArray)
    Local $vEmpty = True
    If IsArray($iArray) = 1 And UBound($iArray, 2) - 1 > -1 Then
        For $i = UBound($iArray, 2) - 1 To 0 Step -1
            For $j = 0 To UBound($iArray, 1) - 1 Step 1
                If $iArray[$i][$j] <> "" Then $vEmpty = False
            Next
            If $vEmpty = True Then _ArrayDelete($iArray, $i)
            $vEmpty = True
        Next
    Else
        SetError(1, 0, 0)
    EndIf
EndFunc

But it's still slow...

Share this post


Link to post
Share on other sites
weaponx

Try this. Yours took 486 ms and I got it down to 30 ms.

#include <Array.au3>

Dim $aTmp[1000][2]

;Fill array with junk
For $X = 0 to 999
    
    ;Insert empty element every 10 nodes, fill the rest with junk
    If Mod($X,10) = 0 Then
        $aTmp[$X][0] = ""
        $aTmp[$X][1] = ""
    Else
        $aTmp[$X][0] = "ebgrhrrtbnwrbnwfg"
        $aTmp[$X][1] = "bwefwergethcbrtgh"
    EndIf
Next

_ArrayDisplay($aTmp, "$aTmp_BEFORE")
$T1 = TimerInit()
;_Array2DDeleteEmptyRows($aTmp)
$aTmp = _DeleteEmptyRows($aTmp)
ConsoleWrite("T1: " & TimerDiff($T1) & @CRLF)
_ArrayDisplay($aTmp, "$aTmp_AFTER")

;486 ms
Func _Array2DDeleteEmptyRows(ByRef $iArray)
    Local $vEmpty = True
    For $i = UBound($iArray) - 1 To 0 Step -1
        For $j = 0 To UBound($iArray, 2)-1 Step 1
            If $iArray[$i][$j] <> "" Then
                $vEmpty = False
            EndIf
        Next
        If $vEmpty = True Then _ArrayDelete($iArray, $i)
        $vEmpty = True
    Next
EndFunc

;30 ms
Func _DeleteEmptyRows($aArray)
    Local $Rows = Ubound($aArray,1)
    Local $Cols = Ubound($aArray,2)
    Local $aTemp[$Rows][$Cols]
    Local $not_empty
    Local $Count = 0
    
    ;Loop through rows
    For $Y = 0 to $Rows - 1
        $not_empty = 0
        
        ;Loop through columns
        For $X = 0 to $Cols - 1
            
            ;Copy all columns to temp array even if they are all empty
            $aTemp[$Count][$X] = $aArray[$Y][$X]
            
            ;If even one column contains data, make sure it doesn't get deleted
            If $aArray[$Y][$X] <> "" Then $not_empty = BitOr($not_empty, 1)
        Next
        
        ;If the row has any data, increment, else keep overwriting last row until it contains something
        If $not_empty Then $Count += 1
    Next

    Redim $aTemp[$Count][$Cols]
    Return $aTemp
EndFunc
Edited by weaponx
  • Like 1

Share this post


Link to post
Share on other sites
supersonic

Thanks for your help! :)

Maybe UBound() in the second For-loop slows down my UDF.

Try this. Yours took 486 ms and I got it down to 30 ms.

#include <Array.au3>

Dim $aTmp[1000][2]

;Fill array with junk
For $X = 0 to 999
    
    ;Insert empty element every 10 nodes, fill the rest with junk
    If Mod($X,10) = 0 Then
        $aTmp[$X][0] = ""
        $aTmp[$X][1] = ""
    Else
        $aTmp[$X][0] = "ebgrhrrtbnwrbnwfg"
        $aTmp[$X][1] = "bwefwergethcbrtgh"
    EndIf
Next

_ArrayDisplay($aTmp, "$aTmp_BEFORE")
$T1 = TimerInit()
;_Array2DDeleteEmptyRows($aTmp)
$aTmp = _DeleteEmptyRows($aTmp)
ConsoleWrite("T1: " & TimerDiff($T1) & @CRLF)
_ArrayDisplay($aTmp, "$aTmp_AFTER")

;486 ms
Func _Array2DDeleteEmptyRows(ByRef $iArray)
    Local $vEmpty = True
    For $i = UBound($iArray) - 1 To 0 Step -1
        For $j = 0 To UBound($iArray, 2)-1 Step 1
            If $iArray[$i][$j] <> "" Then
                $vEmpty = False
            EndIf
        Next
        If $vEmpty = True Then _ArrayDelete($iArray, $i)
        $vEmpty = True
    Next
EndFunc

;30 ms
Func _DeleteEmptyRows($aArray)
    Local $Rows = Ubound($aArray,1)
    Local $Cols = Ubound($aArray,2)
    Local $aTemp[$Rows][$Cols]
    Local $not_empty
    Local $Count = 0
    
    ;Loop through rows
    For $Y = 0 to $Rows - 1
        $not_empty = 0
        
        ;Loop through columns
        For $X = 0 to $Cols - 1
            
            ;Copy all columns to temp array even if they are all empty
            $aTemp[$Count][$X] = $aArray[$Y][$X]
            
            ;If even one column contains data, make sure it doesn't get deleted
            If $aArray[$Y][$X] <> "" Then $not_empty = BitOr($not_empty, 1)
        Next
        
        ;If the row has any data, increment, else keep overwriting last row until it contains something
        If $not_empty Then $Count += 1
    Next

    Redim $aTemp[$Count][$Cols]
    Return $aTemp
EndFunc

Share this post


Link to post
Share on other sites
weaponx

Thanks for your help! :)

Maybe UBound() in the second For-loop slows down my UDF.

No. _ArrayDelete is the slow part.

  • Like 1

Share this post


Link to post
Share on other sites
wolf9228

Hi!

Is there already a build-in function to remove empty rows in a 2D array?

_ArrayUnique wouldn't help, because it's only for 1D arrays.

Sure, i could write an UDF like this:

#include <Array.au3>

Dim $aTmp[5][5] = [[0, 1], [2, 3], ["", ""], ["", ""], [4, 5]]

_ArrayDisplay($aTmp, "$aTmp_BEFORE")
_Array2DDeleteEmptyRows($aTmp)
_ArrayDisplay($aTmp, "$aTmp_AFTER")

Func _Array2DDeleteEmptyRows(ByRef $iArray)
    Local $vEmpty = True
    For $i = UBound($iArray, 2) - 1 To 0 Step -1
        For $j = 0 To UBound($iArray, 1) - 1 Step 1
            If StringCompare($iArray[$i][$j], "", 0) <> 0 Then
                $vEmpty = False
            EndIf
        Next
        If $vEmpty = True Then _ArrayDelete($iArray, $i)
        $vEmpty = True
    Next
EndFunc

Sorry there is an error in this code, I reform :)

This code right

#include <Array.au3>

Dim $aTmp[5][5] = [[0, 1], [2, 3], ["", ""], ["", ""], [4, 5]]

_ArrayDisplay($aTmp, "$aTmp_BEFORE")
$iArrayOut = _Array2DDeleteEmptyRows($aTmp)
_ArrayDisplay($iArrayOut, "$aTmp_AFTER")



Func _Array2DDeleteEmptyRows(ByRef $iArray)
    Local $vEmpty = False
    $cols = UBound($iArray, 2)
    Local $iArrayOut[1][$cols] , $A = 0
    For $i = 0 To (UBound($iArray, 1) - 1) Step 1
    if $vEmpty = True Then $A += 1
    $vEmpty = False
    For $j = 0 To (UBound($iArray, 2) - 1) Step 1
    If StringCompare($iArray[$i][$j], "", 0) <> 0 Then
    ReDim $iArrayOut[$A + 1][$cols]
    $iArrayOut[$A][$j] = $iArray[$i][$j]
    $vEmpty = True
    EndIf
    Next
    Next
    Return $iArrayOut
EndFunc
Edited by wolf9228

صرح السماء كان هنا

 

Share this post


Link to post
Share on other sites
weaponx

#include <Array.au3>

Dim $aTmp[5][5] = [[0, 1], [2, 3], ["", ""], ["", ""], [4, 5]]

_ArrayDisplay($aTmp, "$aTmp_BEFORE")
_Array2DDeleteEmptyRows($aTmp)
_ArrayDisplay($aTmp, "$aTmp_AFTER")
MsgBox(0,"",$aTmp[0][0])


Func _Array2DDeleteEmptyRows(ByRef $iArray)
    Local $vEmpty = False
    $cols = UBound($iArray, 2)
    Local $iArrayOut[1][$cols] , $A = 0 
    For $i = 0 To (UBound($iArray, 1) - 1) Step 1
    if $vEmpty = True Then $A += 1
    $vEmpty = False
    For $j = 0 To (UBound($iArray, 2) - 1) Step 1
    If StringCompare($iArray[$i][$j], "", 0) <> 0 Then 
    ReDim $iArrayOut[$A + 1][$cols]
    $iArrayOut[$A][$j] = $iArray[$i][$j]
    $vEmpty = True
    EndIf
    Next
    Next
    $iArray = $iArrayOut
EndFunc
That took longer than the one the OP posted originally! 1740 ms
  • Like 1

Share this post


Link to post
Share on other sites
AnonymousX
On 7/1/2009 at 10:35 AM, weaponx said:

Try this. Yours took 486 ms and I got it down to 30 ms.

 

#include <Array.au3>

Dim $aTmp[1000][2]

;Fill array with junk
For $X = 0 to 999
    
    ;Insert empty element every 10 nodes, fill the rest with junk
    If Mod($X,10) = 0 Then
        $aTmp[$X][0] = ""
        $aTmp[$X][1] = ""
    Else
        $aTmp[$X][0] = "ebgrhrrtbnwrbnwfg"
        $aTmp[$X][1] = "bwefwergethcbrtgh"
    EndIf
Next

_ArrayDisplay($aTmp, "$aTmp_BEFORE")
$T1 = TimerInit()
;_Array2DDeleteEmptyRows($aTmp)
$aTmp = _DeleteEmptyRows($aTmp)
ConsoleWrite("T1: " & TimerDiff($T1) & @CRLF)
_ArrayDisplay($aTmp, "$aTmp_AFTER")

;486 ms
Func _Array2DDeleteEmptyRows(ByRef $iArray)
    Local $vEmpty = True
    For $i = UBound($iArray) - 1 To 0 Step -1
        For $j = 0 To UBound($iArray, 2)-1 Step 1
            If $iArray[$i][$j] <> "" Then
                $vEmpty = False
            EndIf
        Next
        If $vEmpty = True Then _ArrayDelete($iArray, $i)
        $vEmpty = True
    Next
EndFunc

;30 ms
Func _DeleteEmptyRows($aArray)
    Local $Rows = Ubound($aArray,1)
    Local $Cols = Ubound($aArray,2)
    Local $aTemp[$Rows][$Cols]
    Local $not_empty
    Local $Count = 0
    
    ;Loop through rows
    For $Y = 0 to $Rows - 1
        $not_empty = 0
        
        ;Loop through columns
        For $X = 0 to $Cols - 1
            
            ;Copy all columns to temp array even if they are all empty
            $aTemp[$Count][$X] = $aArray[$Y][$X]
            
            ;If even one column contains data, make sure it doesn't get deleted
            If $aArray[$Y][$X] <> "" Then $not_empty = BitOr($not_empty, 1)
        Next
        
        ;If the row has any data, increment, else keep overwriting last row until it contains something
        If $not_empty Then $Count += 1
    Next

    Redim $aTemp[$Count][$Cols]
    Return $aTemp
EndFunc

Thanks weaponx,

Still helping folks 8 years after posted! 

  • Like 1

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  

×