Sign in to follow this  
Followers 0
funkey

String Expression To Integer Array

24 posts in this topic

Hello,
I just needed a small helping function to convert strings like "1, 2, 12-15" to an array [1, 2, 12, 13, 14, 15].
Improvements in speed or functionality are welcome.
 

 
#include <Array.au3>
Opt("MustDeclareVars", 1)
Global $sExp1 = "13 - 17, 20, 40, 50 - 60"
Global $aTest1 = _ExpressionToIntArray($sExp1)
_ArrayDisplay($aTest1, $sExp1)

Global $sExp2 = "3000-3500, 1-20"
Global $aTest2 = _ExpressionToIntArray($sExp2)
_ArrayDisplay($aTest2, $sExp2)

Func _ExpressionToIntArray($sExpression)
 ;funkey 2014.01.16
 ;check for 'comma'
 Local $aExpression1 = StringSplit($sExpression, ",", 2)
 Local $aExpression2[UBound($aExpression1)]
 ;check for 'from x to y'
 Local $iElements = 0, $aTemp, $aTemp2
 For $i = 0 To UBound($aExpression1) - 1
  $aTemp = StringSplit($aExpression1[$i], "-", 2)
  If UBound($aTemp) = 2 Then
   If $aTemp[1] - $aTemp[0] < 0 Then Return SetError(1, 0, 0)
   Dim $aTemp2[$aTemp[1] - $aTemp[0] + 1]
   For $j = 0 To UBound($aTemp2) - 1
    $aTemp2[$j] = $aTemp[0] + $j
   Next
   $iElements += UBound($aTemp2)
   $aExpression2[$i] = $aTemp2
  Else
   $iElements += 1
   $aExpression2[$i] = $aTemp
  EndIf
 Next
 ;create full array with all elements
 Local $aExpression3[$iElements]
 Local $k = 0
 For $i = 0 To UBound($aExpression1) - 1
  For $j = 0 To UBound($aExpression2[$i]) - 1
   $aTemp = $aExpression2[$i]
   $aExpression3[$k] = Int($aTemp[$j])
   $k += 1
  Next
 Next
 Return $aExpression3
EndFunc   ;==>_ExpressionToIntArray
 

Greetings from Austria

Programming today is a race between software engineers striving to
build bigger and better idiot-proof programs, and the Universe
trying to produce bigger and better idiots.
So far, the Universe is winning.

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

Hi funkey

I botched this and it seems a little faster

also it automatically arrange numbers in "range" mask so that if you pass 100 - 10 it will be automatically considered as 10 - 100

(I inverted 17 with 13 in first call for example)

or maybe it would be better generate the list in reverse order? 100 99 98 ......

or maybe you prefer to return an error in that case ?

anyway here it is

#include <Array.au3>
Opt("MustDeclareVars", 1)
Global $sExp1 = "17 - 13, 20, 40, 50 - 60"
Local $time = TimerInit()
Global $aTest1 = _ExpressionToIntArray($sExp1)
ConsoleWrite(TimerDiff($time) & @CRLF)
_ArrayDisplay($aTest1, $sExp1)

Global $sExp2 = "3000-3500, 1-20"
$time = TimerInit()
Global $aTest2 = _ExpressionToIntArray($sExp2)
ConsoleWrite(TimerDiff($time) & @CRLF)
_ArrayDisplay($aTest2, $sExp2)

Func _ExpressionToIntArray($sExpression)
    Local $iElements = 0, $0 = 0, $1 = 1
    Local $aExpression1 = StringSplit($sExpression, ",", 2)
    Local $iElements = 0, $aTemp[UBound($aExpression1)][2], $aTemp1

    For $x = 0 To UBound($aExpression1) - 1
        If Not StringInStr($aExpression1[$x], "-") Then $aExpression1[$x] &= "-" & $aExpression1[$x]
        $aTemp1 = StringSplit($aExpression1[$x], "-", 2)
        $0 = 1 * (Int($aTemp1[0]) > Int($aTemp1[1]))
        $1 = 1 - $0
        $aTemp[$x][0] = Int($aTemp1[$0])
        $aTemp[$x][1] = Int($aTemp1[$1])
        $iElements += $aTemp1[$1] - $aTemp1[$0] + 1
    Next
    Local $aExpression3[$iElements], $k = 0
    For $x = 0 To UBound($aExpression1) - 1
        For $y = $aTemp[$x][0] To $aTemp[$x][1]
            ; $aExpression3[$k] = Int($y)
            $aExpression3[$k] = $y
            $k += 1
        Next
    Next
    Return $aExpression3
EndFunc   ;==>_ExpressionToIntArray

bye

Edited by PincoPanco

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

#3 ·  Posted (edited)

I cut out a lot of time by using a regexp:

#include <Array.au3>

$i1 = TimerInit()
Global $sExp1 = "13 - 17, 20, 40, 50 - 60"
Global $aTest1 = _ExpressionToIntArray_new($sExp1)
;~ _ArrayDisplay($aTest1, $sExp1)

Global $sExp2 = "3000-350000, 1-20"
Global $aTest2 = _ExpressionToIntArray_new($sExp2)
;~ _ArrayDisplay($aTest2, $sExp2)
ConsoleWrite(TimerDiff($i1) & @CRLF)

$i2 = TimerInit()
Global $sExp1 = "13 - 17, 20, 40, 50 - 60"
Global $aTest1 = _ExpressionToIntArray($sExp1)
;~ _ArrayDisplay($aTest1, $sExp1)

Global $sExp2 = "3000-350000, 1-20"
Global $aTest2 = _ExpressionToIntArray($sExp2)
;~ _ArrayDisplay($aTest2, $sExp2)
ConsoleWrite(TimerDiff($i2) & @CRLF)


Func _ExpressionToIntArray($sExpression)
 ;funkey 2014.01.16
 ;check for 'comma'
 Local $aExpression1 = StringSplit($sExpression, ",", 2)
 Local $aExpression2[UBound($aExpression1)]
 ;check for 'from x to y'
 Local $iElements = 0, $aTemp, $aTemp2
 For $i = 0 To UBound($aExpression1) - 1
  $aTemp = StringSplit($aExpression1[$i], "-", 2)
  If UBound($aTemp) = 2 Then
   If $aTemp[1] - $aTemp[0] < 0 Then Return SetError(1, 0, 0)
   Dim $aTemp2[$aTemp[1] - $aTemp[0] + 1]
   For $j = 0 To UBound($aTemp2) - 1
    $aTemp2[$j] = $aTemp[0] + $j
   Next
   $iElements += UBound($aTemp2)
   $aExpression2[$i] = $aTemp2
  Else
   $iElements += 1
   $aExpression2[$i] = $aTemp
  EndIf
 Next
 ;create full array with all elements
 Local $aExpression3[$iElements]
 Local $k = 0
 For $i = 0 To UBound($aExpression1) - 1
  For $j = 0 To UBound($aExpression2[$i]) - 1
   $aTemp = $aExpression2[$i]
   $aExpression3[$k] = Int($aTemp[$j])
   $k += 1
  Next
 Next
 Return $aExpression3
EndFunc   ;==>_ExpressionToIntArray


Func _ExpressionToIntArray_new($sExpression)
 ;funkey 2014.01.16
 ;check for 'comma'
    $aSplit = StringRegExp($sExpression,"([^,\s]+)\s*\-\s*([^,\s]+)|([^,\s]+)",4)
    $iUbound = 0
    For $i = 0 To UBound($aSplit)-1
        ; Get ubound of array to create
        If UBound($aSplit[$i])=3 Then
            $aTemp = $aSplit[$i]
            $iUbound+=Abs($aTemp[2]-$aTemp[1])+1
        Else
            $iUbound+=1
        EndIf
    Next
    Local $aReturn[$iUbound]
    $iSub = 0
    For $i = 0 To UBound($aSplit)-1
        ; Get ubound of array to create
        $aTemp = $aSplit[$i]
        If UBound($aTemp)=3 Then
            For $j = $aTemp[1] To $aTemp[2]
                $aReturn[$iSub]=$j
                $iSub+=1
            Next
        Else
            $aReturn[$iSub]=$aTemp[3]
            $iSub+=1
        EndIf
    Next
    Return $aReturn
EndFunc   ;==>_ExpressionToIntArray

output:

1091.86530293837
3221.73827203298

added no error handling, so keep that in mind.

Edited by jdelaney

IEbyXPATH-Grab IE DOM objects by XPATH IEscriptRecord-Makings of an IE script recorder ExcelFromXML-Create Excel docs without excel installed GetAllWindowControls-Output all control data on a given window.

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

the speed improvement is not due to the use of regexp, but on the different management of the arrays

my version do not use regexp, but takes the same time as that of jdelaney

Edited by PincoPanco

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

I wrote this last april, but it was only on the MVP forum.

Not sure how much use it will be to you, it's a little different in purpose and doesn't just work with integers.

#include <Array.au3>
#include <Constants.au3>

Local $aMyRange = _Range_Parse("1-3;5;5-10;9;2-4")

; Result should be: 1-4;5-10
MsgBox($MB_OK, "Normalised Range:", _Range_ToString($aMyRange))

Func _Range_Parse($sRange)
    Local $aRegions = StringSplit($sRange, ";"), $aBounds
    Local $aRet[$aRegions[0] + 1][2]
    $aRet[0][0] = $aRegions[0]

    For $i = 1 To $aRegions[0]

        If StringIsFloat($aRegions[$i]) Or StringIsInt($aRegions[$i]) Then ; Just a single number
            $aRet[$i][0] = Number($aRegions[$i])
            $aRet[$i][1] = Number($aRegions[$i])
        Else
            $aBounds = StringRegExp($aRegions[$i], "(-?\d+(?:\.\d+)?)\-(-?\d+(?:\.\d+)?)", 3)

            If UBound($aBounds) <> 2 Then Return SetError(1, 0, 0)

            $aRet[$i][0] = Number($aBounds[0])
            $aRet[$i][1] = Number($aBounds[1])
        EndIf
    Next

    _Range_Normalize($aRet)

    Return $aRet
EndFunc   ;==>_Range_Parse

Func _Range_Normalize(ByRef $aRange)
    If Not IsArray($aRange) Then Return SetError(1, 0, 0)

    ; Sort the array by region lower bounds
    _ArraySort($aRange, 0, 1)

    ; Check for overlapping regions

    Local $i = 0
    While $i < $aRange[0][0] - 1
        $i += 1

        If $aRange[$i][1] >= $aRange[$i + 1][0] Then ; Regions Overlap
            If $aRange[$i + 1][1] > $aRange[$i][1] Then $aRange[$i][1] = $aRange[$i + 1][1]
            _ArrayDelete($aRange, $i + 1)
            $aRange[0][0] -= 1
            $i -= 1
        EndIf
    WEnd

    Return 1
EndFunc   ;==>_Range_Normalize

; Could be replaced by a binary search, but $aRange is expected to contain very few (<10) elements
Func _Range_Contains(ByRef Const $aRange, $nNeedle)
    If Not IsArray($aRange) Then Return SetError(1, 0, 0)

    For $i = 1 To $aRange[0][0]
        If $nNeedle < $aRange[$i][0] Then Return False
        If $nNeedle <= $aRange[$i][1] Then Return True
    Next

    Return False
EndFunc   ;==>_Range_Contains

Func _Range_ToString(ByRef Const $aRange)
    If Not IsArray($aRange) Then Return SetError(1, 0, 0)

    Local $sRet = ""

    For $i = 1 To $aRange[0][0]
        If $aRange[$i][0] = $aRange[$i][1] Then
            $sRet &= $aRange[$i][0] & ";"
        Else
            $sRet &= $aRange[$i][0] & "-" & $aRange[$i][1] & ";"
        EndIf
    Next

    $sRet = StringTrimRight($sRet, 1)

    Return $sRet
EndFunc   ;==>_Range_ToString

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

Time taken = 352.03113227595 milliseconds which can be compared to the values in jdelaney's post #3.

#include <Array.au3>

Local $i1 = TimerInit()

Global $sExp1 = "13 - 17, 20, 40, 50 - 60"
Global $aTest1 = StringSplit(Execute('"' & StringRegExpReplace(StringStripWS($sExp1, 8), "(\d+)-(\d+)", '" & _Expand(\1, \2) & "') & '"'), ",", 2)

Global $sExp2 = "3000-350000, 1-20"
Global $aTest2 = StringSplit(Execute('"' & StringRegExpReplace(StringStripWS($sExp2, 8), "(\d+)-(\d+)", '" & _Expand(\1, \2) & "') & '"'), ",", 2)

ConsoleWrite(TimerDiff($i1) & @CRLF)
_ArrayDisplay($aTest1, $sExp1)
_ArrayDisplay($aTest2, $sExp2)


Func _Expand($a, $b)
    Local $sRet
    For $i = $a To $b
        $sRet &= $i & ","
    Next
    Return StringTrimRight($sRet, 1)
EndFunc   ;==>_Expand

;Time taken = 352.03113227595 milliseconds
Edited by Malkey

Share this post


Link to post
Share on other sites

Thanks for your feedback and improvements. I'll have a look at it. I think reversing the array could be a nice feature and sometimes useful.

Mat: Why isn't the result of your example "1-10" ?


Programming today is a race between software engineers striving to
build bigger and better idiot-proof programs, and the Universe
trying to produce bigger and better idiots.
So far, the Universe is winning.

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

Func _ExpressionToIntArray2($sExpression)
    Local $aArray = StringRegExp($sExpression, '[^\d]*(?|(\d+)[\-\h]+(\d+)|(\d+))', 4)
    $sExpression = ""
    For $i = 0 To UBound($aArray) - 1
        If UBound($aArray[$i]) > 2 Then
            For $y = ($aArray[$i])[1] To ($aArray[$i])[2]
                $sExpression &= $y & ","
            Next
        Else
            $sExpression &= ($aArray[$i])[1] & ","
        EndIf
    Next
    Return StringSplit(StringTrimRight($sExpression, 1), ",", 3)
    ;$aArray = StringSplit(StringTrimRight($sExpression, 1), ",", 3)
    ;For $i = 0 To UBound($aArray) - 1
    ;    $aArray[$i] = Int($aArray[$i])
    ;Next
    ;Return $aArray
EndFunc   ;==>_ExpressionToIntArray


;; Recommended way, More faster & $aArray[$n] = INT (is number, is not strings)
Func _ExpressionToIntArray3($sExpression)
    Local $iaArray, $aExpression = StringRegExp($sExpression, '[^\d]*(?|(\d+)\h*\-[\-\h]*(\d+)|(\d+))', 4)
    If @Error Then Return SetError(1, 0, 0)
    For $i = 0 To UBound($aExpression) - 1
        $iaArray += 1
        If UBound($aExpression[$i]) > 2 Then $iaArray += ($aExpression[$i])[2] - ($aExpression[$i])[1]
    Next
    Local $aArray[$iaArray]
    $iaArray = 0
    For $i = 0 To UBound($aExpression) - 1
        If UBound($aExpression[$i]) > 2 Then
            For $y = ($aExpression[$i])[1] To ($aExpression[$i])[2]
                $aArray[$iaArray] = $y
                $iaArray += 1
            Next
        Else
            $aArray[$iaArray] = Int(($aExpression[$i])[1])
            $iaArray += 1
        EndIf
    Next
    Return $aArray
EndFunc   ;==>_ExpressionToIntArray

Ciao.

Edited by DXRW4E

apps-odrive.pngdrive_app_badge.png box-logo.png new_logo.png MEGA_Logo.png

Share this post


Link to post
Share on other sites

#include <Array.au3>

$intarray = _ExpressionToIntArray("13 - 17, 20, 40, 50 - 60")

_ArrayDisplay($intarray)

Func _ExpressionToIntArray($sExpression)
    Local $aExpression = StringSplit($sExpression, ",", 2)

    For $i = 0 To UBound($aExpression) -1
        $aExpression[$i] = Int(Execute($aExpression[$i]))
    Next

    Return $aExpression
EndFunc

:unsure:


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

Monkey's are, like, natures humans.

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

 

Time taken = 352.03113227595 milliseconds which can be compared to the values in jdelaney's post #3.

#include <Array.au3>

Local $i1 = TimerInit()

Global $sExp1 = "13 - 17, 20, 40, 50 - 60"
Global $aTest1 = StringSplit(Execute('"' & StringRegExpReplace(StringStripWS($sExp1, 8), "(\d+)-(\d+)", '" & _Expand(\1, \2) & "') & '"'), ",", 2)

Global $sExp2 = "3000-350000, 1-20"
Global $aTest2 = StringSplit(Execute('"' & StringRegExpReplace(StringStripWS($sExp2, 8), "(\d+)-(\d+)", '" & _Expand(\1, \2) & "') & '"'), ",", 2)

ConsoleWrite(TimerDiff($i1) & @CRLF)
_ArrayDisplay($aTest1, $sExp1)
_ArrayDisplay($aTest2, $sExp2)


Func _Expand($a, $b)
    Local $sRet
    For $i = $a To $b
        $sRet &= $i & ","
    Next
    Return StringTrimRight($sRet, 1)
EndFunc   ;==>_Expand

;Time taken = 352.03113227595 milliseconds

Your script takes longer than mine on my computer ;). (it's an old one)....yours took mine 1331.39271715158

You would have to run ALL options relative to your computer.

Still a cool technique though

Edit: 1 run is hardly a good sample, I'd collect the times for 20 runs to have a good one

Edited by jdelaney

IEbyXPATH-Grab IE DOM objects by XPATH IEscriptRecord-Makings of an IE script recorder ExcelFromXML-Create Excel docs without excel installed GetAllWindowControls-Output all control data on a given window.

Share this post


Link to post
Share on other sites

Thanks for your feedback and improvements. I'll have a look at it. I think reversing the array could be a nice feature and sometimes useful.

Mat: Why isn't the result of your example "1-10" ?

 

Because it was written to work with numbers other than integers. 4.5 is in between 4 and 5 so you can't combine those regions.

As I said, it was written with rather a different purpose in mind I suspect.

Share this post


Link to post
Share on other sites

#13 ·  Posted (edited)

#include <Array.au3>

$timer = TimerInit()
$intarray = _ExpressionToIntArray("13 - 17, 20, 40, 50 - 60")
ConsoleWrite(TimerDiff($timer) & @LF)
_ArrayDisplay($intarray)

Func _ExpressionToIntArray($sExpression)
    $sExpression = StringStripWS($sExpression, 8)
    Local $aExpression = StringSplit($sExpression, ",", 2)
    Local $sArray = ""
    For $i = 0 To UBound($aExpression) - 1
        If StringInStr($aExpression[$i], "-") Then
            $sArray &= _ParseElement($aExpression[$i])
        Else
            $sArray &= $aExpression[$i] & "|"
        EndIf

    Next
    $sArray = StringTrimRight($sArray, 1)
    Return StringSplit($sArray, "|", 2)
EndFunc   ;==>_ExpressionToIntArray

Func _ParseElement($val)
    Local $stmp = ""
    Local $atmp = StringSplit($val, "-", 2)
    For $i = Int($atmp[0]) To Int($atmp[1])
        $stmp &= $i & "|"
    Next
    Return $stmp
EndFunc   ;==>_ParseElement

Edited by JohnOne

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

Monkey's are, like, natures humans.

Share this post


Link to post
Share on other sites

#15 ·  Posted (edited)

#include <Array.au3>

GLobal COnst $string = "13 - 17, 20, 40, 50 - 60"

Global COnst $int_array = StringRegExp($string, "\d+", 3)

_ArrayDisplay($int_array)

That won't return 14 through 16, or 51 through 59 in the array.

Edited by jdelaney

IEbyXPATH-Grab IE DOM objects by XPATH IEscriptRecord-Makings of an IE script recorder ExcelFromXML-Create Excel docs without excel installed GetAllWindowControls-Output all control data on a given window.

Share this post


Link to post
Share on other sites

was curious to make some statistics

used this call to test time spent by various functions:

$timer = TimerInit()
$intarray = _ExpressionToIntArray("13 - 17, 20, 40, 50 - 60, 3000-3500, 1-2000")
ConsoleWrite(TimerDiff($timer) & @LF)

results:

function   run 1      run 2      run 3      run 4      run 5      run 6     average
-------------------------------------------------------------------------------------
funkey     43.539891, 38.180830, 40.049224, 44.582481, 37.371509, 56.409403 43.355556
-------------------------------------------------------------------------------------
PincoPanco 16.368002, 13.228496, 13.647544, 13.994236, 15.770998, 11.503137 14.085402
-------------------------------------------------------------------------------------
jdelaney   16.732852, 13.349741, 11.991188, 17.148268, 14.528103, 13.387735 14.522981
-------------------------------------------------------------------------------------
Malkey     18.182757, 17.414224, 16.700446, 15.997843, 18.562415, 17.123684 17.330228
-------------------------------------------------------------------------------------
DXRW4E
function2  16.287265, 19.737983, 18.706008, 17.454732, 15.404751, 16.382529 17.328878
function3  13.876065, 16.734529, 13.158096, 11.551188, 18.626948, 16.785652 15.122080
-------------------------------------------------------------------------------------
JohnOne    25.481450, 23.892422, 18.949056, 17.244091, 17.856180, 20.220447 20.607274

(results truncated to 6 decimals to fit in table)


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

I can shave a bit off the time of mine.

#include <Array.au3>

$timer = TimerInit()
$intarray = _ExpressionToIntArray("13 - 17, 20, 40, 50 - 60, 3000-3500, 1-2000")
ConsoleWrite(TimerDiff($timer) & @LF)

Func _ExpressionToIntArray($sExpression)
    $sExpression = StringStripWS($sExpression, 8)
    Local $aExpression = StringSplit($sExpression, ",", 2)
    Local $sArray = ""
    For $i = 0 To UBound($aExpression) - 1
        If StringInStr($aExpression[$i], "-") Then
            _ParseElement($sArray, $aExpression[$i])
        Else
            $sArray &= $aExpression[$i] & "|"
        EndIf

    Next
    $sArray = StringTrimRight($sArray, 1)
    Return StringSplit($sArray, "|", 2)
EndFunc   ;==>_ExpressionToIntArray

Func _ParseElement(ByRef $stmp, $val)
    Local $atmp = StringSplit($val, "-", 2)
    For $i = Int($atmp[0]) To Int($atmp[1])
        $stmp &= $i & "|"
    Next
    Return $stmp
EndFunc   ;==>_ParseElement

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

Monkey's are, like, natures humans.

Share this post


Link to post
Share on other sites

 

I can shave a bit off the time of mine.

 

JohnOne

before shave    25.481450, 23.892422, 18.949056, 17.244091, 17.856180, 20.220447 20.607274

after shave     29.538949, 17.359468, 19.136789, 22.047774, 18.002008, 19.673170 20.959693


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

I think a lot more needs to be added to the inital string, to get execution time-lengths up to seconds.

Millisecs are fleeting :)


IEbyXPATH-Grab IE DOM objects by XPATH IEscriptRecord-Makings of an IE script recorder ExcelFromXML-Create Excel docs without excel installed GetAllWindowControls-Output all control data on a given window.

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